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_utils.c

1050 lines
29 KiB
C
Raw Normal View History

2007-10-03 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2007 <20>
// qcc_utils.c - qcc common tools
//=======================================================================
2008-06-04 22:00:00 +02:00
#include "vprogs.h"
2008-08-15 22:00:00 +02:00
#include "const.h"
2007-10-03 22:00:00 +02:00
2007-10-04 22:00:00 +02:00
void Hash_InitTable(hashtable_t *table, int numbucks)
2007-10-03 22:00:00 +02:00
{
table->numbuckets = numbucks;
2007-10-04 22:00:00 +02:00
table->bucket = (bucket_t **)Qalloc(BytesForBuckets(numbucks));
2007-10-03 22:00:00 +02:00
}
2007-10-08 22:00:00 +02:00
uint Hash_Key(char *name, int modulus)
2007-10-03 22:00:00 +02:00
{
2007-10-08 22:00:00 +02:00
//FIXME: optimize.
uint key;
2007-10-03 22:00:00 +02:00
for (key = 0; *name; name++)
key += ((key<<3) + (key>>28) + *name);
2007-10-08 22:00:00 +02:00
return (key%modulus);
2007-10-03 22:00:00 +02:00
}
void *Hash_Get(hashtable_t *table, char *name)
{
2007-10-08 22:00:00 +02:00
uint bucknum = Hash_Key(name, table->numbuckets);
2008-06-04 22:00:00 +02:00
bucket_t *buck;
2007-10-03 22:00:00 +02:00
buck = table->bucket[bucknum];
while(buck)
{
if (!STRCMP(name, buck->keystring))
return buck->data;
buck = buck->next;
}
return NULL;
}
void *Hash_GetKey(hashtable_t *table, int key)
{
2007-10-08 22:00:00 +02:00
uint bucknum = key%table->numbuckets;
2008-06-04 22:00:00 +02:00
bucket_t *buck;
2007-10-03 22:00:00 +02:00
buck = table->bucket[bucknum];
while(buck)
{
if ((int)buck->keystring == key)
return buck->data;
buck = buck->next;
}
return NULL;
}
void *Hash_GetNext(hashtable_t *table, char *name, void *old)
{
2007-10-08 22:00:00 +02:00
uint bucknum = Hash_Key(name, table->numbuckets);
2008-06-04 22:00:00 +02:00
bucket_t *buck;
2007-10-03 22:00:00 +02:00
buck = table->bucket[bucknum];
while(buck)
{
if (!STRCMP(name, buck->keystring))
{
if (buck->data == old)//found the old one
break;
}
buck = buck->next;
}
if (!buck) return NULL;
buck = buck->next;//don't return old
while(buck)
{
if (!STRCMP(name, buck->keystring))
return buck->data;
buck = buck->next;
}
return NULL;
}
void *Hash_Add(hashtable_t *table, char *name, void *data, bucket_t *buck)
{
2007-10-08 22:00:00 +02:00
uint bucknum = Hash_Key(name, table->numbuckets);
2007-10-03 22:00:00 +02:00
buck->data = data;
buck->keystring = name;
buck->next = table->bucket[bucknum];
table->bucket[bucknum] = buck;
return buck;
}
void *Hash_AddKey(hashtable_t *table, int key, void *data, bucket_t *buck)
{
2007-10-08 22:00:00 +02:00
uint bucknum = key%table->numbuckets;
2007-10-03 22:00:00 +02:00
buck->data = data;
buck->keystring = (char*)key;
buck->next = table->bucket[bucknum];
table->bucket[bucknum] = buck;
return buck;
}
void Hash_Remove(hashtable_t *table, char *name)
{
2007-10-08 22:00:00 +02:00
uint bucknum = Hash_Key(name, table->numbuckets);
2008-06-04 22:00:00 +02:00
bucket_t *buck;
2007-10-03 22:00:00 +02:00
buck = table->bucket[bucknum];
if (!STRCMP(name, buck->keystring))
{
table->bucket[bucknum] = buck->next;
return;
}
while(buck->next)
{
if (!STRCMP(name, buck->next->keystring))
{
buck->next = buck->next->next;
return;
}
buck = buck->next;
}
}
void Hash_RemoveData(hashtable_t *table, char *name, void *data)
{
2007-10-08 22:00:00 +02:00
uint bucknum = Hash_Key(name, table->numbuckets);
2008-06-04 22:00:00 +02:00
bucket_t *buck;
2007-10-03 22:00:00 +02:00
buck = table->bucket[bucknum];
if (buck->data == data)
{
if (!STRCMP(name, buck->keystring))
{
table->bucket[bucknum] = buck->next;
return;
}
}
while(buck->next)
{
if (buck->next->data == data)
{
if (!STRCMP(name, buck->next->keystring))
{
buck->next = buck->next->next;
return;
}
}
buck = buck->next;
}
}
void Hash_RemoveKey(hashtable_t *table, int key)
{
2007-10-08 22:00:00 +02:00
uint bucknum = key%table->numbuckets;
2008-06-04 22:00:00 +02:00
bucket_t *buck;
2007-10-03 22:00:00 +02:00
buck = table->bucket[bucknum];
if ((int)buck->keystring == key)
{
table->bucket[bucknum] = buck->next;
return;
}
while(buck->next)
{
if ((int)buck->next->keystring == key)
{
buck->next = buck->next->next;
return;
}
buck = buck->next;
}
}
/*
================
PR_decode
================
*/
2007-10-08 22:00:00 +02:00
int PR_decode(int complen, int len, int method, char *src, char **dst)
2007-10-03 22:00:00 +02:00
{
int i;
2007-10-08 22:00:00 +02:00
char *buffer = *dst;
switch(method)
2007-10-03 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
case CMP_NONE:
2007-10-03 22:00:00 +02:00
if (complen != len)
{
2007-10-08 22:00:00 +02:00
MsgDev(D_WARN, "PR_decode: complen[%d] != len[%d]\n", complen, len);
return false;
}
Mem_Copy(buffer, src, len);
break;
2008-07-24 22:00:00 +02:00
case CMP_LZSS:
2007-10-08 22:00:00 +02:00
if (complen != len)
{
MsgDev(D_WARN, "PR_decode: complen[%d] != len[%d]\n", complen, len);
2007-10-03 22:00:00 +02:00
return false;
}
2007-10-08 22:00:00 +02:00
for (i = 0; i < len; i++) buffer[i] = src[i] ^ 0xA5;
break;
2008-07-24 22:00:00 +02:00
case CMP_ZLIB:
2007-11-11 22:00:00 +01:00
VFS_Unpack( src, complen, dst, len );
2007-10-08 22:00:00 +02:00
break;
default:
MsgDev(D_WARN, "PR_decode: invalid method\n");
2007-10-03 22:00:00 +02:00
return false;
2007-10-08 22:00:00 +02:00
}
2007-10-03 22:00:00 +02:00
return true;
}
2007-10-08 22:00:00 +02:00
/*
================
PR_encode
================
*/
2007-11-20 22:00:00 +01:00
int PR_encode(int len, int method, char *src, file_t *h)
2007-10-08 22:00:00 +02:00
{
2007-11-20 22:00:00 +01:00
int i, pos;
vfile_t *vf;
2007-10-08 22:00:00 +02:00
switch(method)
{
2008-07-24 22:00:00 +02:00
case CMP_NONE:
2007-11-20 22:00:00 +01:00
FS_Write(h, src, len);
2007-10-08 22:00:00 +02:00
return len;
2008-07-24 22:00:00 +02:00
case CMP_LZSS:
2007-10-08 22:00:00 +02:00
for (i = 0; i < len; i++) src[i] = src[i] ^ 0xA5;
2007-11-20 22:00:00 +01:00
FS_Write(h, src, len);
2007-10-08 22:00:00 +02:00
return len;
2008-07-24 22:00:00 +02:00
case CMP_ZLIB:
2007-11-20 22:00:00 +01:00
vf = VFS_Open( h, "wz" );
pos = FS_Tell(h); // member ofs
VFS_Write( vf, src, len );
VFS_Close( vf );
return FS_Tell(h) - pos;
2007-10-08 22:00:00 +02:00
default:
MsgDev(D_WARN, "PR_encode: invalid method\n");
return false;
}
}
2007-10-03 22:00:00 +02:00
// CopyString returns an offset from the string heap
int PR_CopyString (char *str, bool noduplicate )
{
int old;
char *s;
if (noduplicate)
{
if (!str || !*str) return 0;
for (s = strings; s < strings + strofs; s++)
2008-06-04 22:00:00 +02:00
if (!com.strcmp(s, str)) return s - strings;
2007-10-03 22:00:00 +02:00
}
old = strofs;
2008-06-04 22:00:00 +02:00
com.strcpy (strings + strofs, str);
strofs += com.strlen(str)+1;
2007-10-03 22:00:00 +02:00
return old;
}
/*
================
PR_WriteSourceFiles
include sources into progs.dat for fake decompile
if it needs
================
*/
2008-08-13 22:00:00 +02:00
bool PR_WriteSourceFiles( file_t *h, dprograms_t *progs, bool sourceaswell )
2007-10-03 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
dsource_t *idf;
2007-10-03 22:00:00 +02:00
cachedsourcefile_t *f;
int num = 0;
int ofs;
for (f = sourcefile, num = 0; f; f = f->next)
{
if (f->type == FT_CODE && !sourceaswell)
continue;
num++;
}
2008-07-24 22:00:00 +02:00
if( !num )
{
progs->ofssources = progs->numsources = 0;
return false;
}
2008-07-23 22:00:00 +02:00
idf = Qalloc(sizeof(dsource_t) * num);
2008-07-18 22:00:00 +02:00
for( f = sourcefile, num = 0; f; f = f->next )
2007-10-03 22:00:00 +02:00
{
2008-07-18 22:00:00 +02:00
if( f->type == FT_CODE && !sourceaswell )
2007-10-03 22:00:00 +02:00
continue;
2008-07-24 22:00:00 +02:00
com.strncpy( idf[num].name, f->filename, 64 );
2007-10-03 22:00:00 +02:00
idf[num].size = f->size;
2008-07-24 22:00:00 +02:00
idf[num].compression = CMP_ZLIB;
idf[num].filepos = FS_Tell( h );
idf[num].disksize = PR_encode(f->size, idf[num].compression, f->file, h );
2007-10-03 22:00:00 +02:00
num++;
}
2008-07-24 22:00:00 +02:00
ofs = FS_Tell( h );
2008-07-23 22:00:00 +02:00
FS_Write( h, idf, sizeof(dsource_t) * num );
2007-10-03 22:00:00 +02:00
sourcefile = NULL;
2008-07-24 22:00:00 +02:00
// update header
progs->ofssources = ofs;
progs->numsources = num;
return true;
2007-10-03 22:00:00 +02:00
}
2007-11-20 22:00:00 +01:00
int PR_WriteBodylessFuncs (file_t *handle)
2007-10-03 22:00:00 +02:00
{
def_t *d;
int ret = 0;
for (d = pr.def_head.next; d; d = d->next)
{
// function parms are ok
if (d->type->type == ev_function && !d->scope)
{
if (d->initialized != 1 && d->references > 0)
{
2008-06-04 22:00:00 +02:00
FS_Write(handle, d->name, com.strlen(d->name)+1);
2007-10-03 22:00:00 +02:00
ret++;
}
}
}
return ret;
}
/*
====================
AddBuffer
copy string into buffer and process crc
====================
*/
#define NEED_CRC 1 // calcaulate crc
#define NEED_CPY 2 // copy string
#define NEED_ALL NEED_CRC|NEED_CPY
#define ADD1(p) AddBuffer(p, &crc, file, NEED_ALL)
#define ADD2(p) AddBuffer(p, &crc, file, NEED_CPY)
#define ADD3(p) AddBuffer(p, &crc, file, NEED_CRC)
_inline void AddBuffer(char *p, word *crc, char *file, byte flags )
{
char *s;
2008-06-04 22:00:00 +02:00
int i = com.strlen(file);
2007-10-03 22:00:00 +02:00
2008-06-04 22:00:00 +02:00
if (i + com.strlen(p) + 1 >= PROGDEFS_MAX_SIZE)
2007-10-03 22:00:00 +02:00
return;
for(s = p; *s; s++, i++)
{
if(flags & NEED_CRC) CRC_ProcessByte(crc, *s);
if(flags & NEED_CPY) file[i] = *s;
}
if(flags & NEED_CPY) file[i] = '\0';
}
/*
====================
PR_WriteProgdefs
write advanced progdefs.h into disk
====================
*/
2008-07-24 22:00:00 +02:00
word PR_WriteProgdefs( void )
2007-10-03 22:00:00 +02:00
{
char file[PROGDEFS_MAX_SIZE];
char header_name[MAX_QPATH];
2008-07-24 22:00:00 +02:00
char lwr_header[MAX_QPATH];
char lwr_prefix[8], upr_prefix[8];
char array_size[8]; // g-cont: mega name!
2007-10-04 22:00:00 +02:00
int f = 0, k = 1;
2008-08-09 22:00:00 +02:00
string path;
2008-07-24 22:00:00 +02:00
def_t *d;
2007-10-03 22:00:00 +02:00
word crc;
2008-07-24 22:00:00 +02:00
CRC_Init( &crc );
memset( file, 0, PROGDEFS_MAX_SIZE );
2007-11-20 22:00:00 +01:00
FS_FileBase( progsoutname, header_name );
2008-06-04 22:00:00 +02:00
com.strupr( header_name, header_name ); // as part of define name
2008-07-24 22:00:00 +02:00
com.strlwr( header_name, lwr_header ); // as part of head comment
// convert progsoutname to prefix
// using special case for "server", because "se" prefix looks ugly
if(!com.stricmp( header_name, "server" )) com.strncpy( lwr_prefix, "sv", 8 );
else com.strnlwr( header_name, lwr_prefix, 3 );
com.strnupr( lwr_prefix, upr_prefix, 3 );
ADD1("//=======================================================================\n");
ADD1("// Copyright XashXT Group 2008 <20>\n");
ADD2(va("// %s_edict.h - %s prvm edict\n", lwr_prefix, lwr_header ));
ADD1("//=======================================================================\n");
ADD2(va("#ifndef %s_EDICT_H\n#define %s_EDICT_H\n", upr_prefix, upr_prefix ));
ADD1("\nstruct");
ADD2(va(" %s_globalvars_s", lwr_prefix ));
2007-10-03 22:00:00 +02:00
ADD1(va("\n{"));
ADD2(va("\n"));
ADD1(va("\tint\tpad[%i];\n", RESERVED_OFS));
// write globalvars_t
2008-07-24 22:00:00 +02:00
for( d = pr.def_head.next; d; d = d->next )
2007-10-03 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
if( !com.strcmp (d->name, "end_sys_globals")) break;
if( d->ofs < RESERVED_OFS ) continue;
if( com.strchr( d->name, '[' )) continue; // skip arrays
if( d->arraysize > 1 ) com.snprintf( array_size, 8, "[%i]", d->arraysize );
else array_size[0] = '\0'; // clear array
2007-10-03 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
switch( d->type->type )
2007-10-03 22:00:00 +02:00
{
case ev_float:
2008-07-24 22:00:00 +02:00
ADD1(va("\tfloat\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
case ev_vector:
2008-07-24 22:00:00 +02:00
ADD1(va("\tvec3_t\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
d = d->next->next->next; // skip the elements
break;
case ev_string:
2008-07-24 22:00:00 +02:00
ADD1(va("\tstring_t\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
case ev_function:
2008-07-24 22:00:00 +02:00
ADD1(va("\tfunc_t\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
case ev_entity:
2008-07-24 22:00:00 +02:00
ADD1(va("\tint\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
case ev_integer:
2008-07-24 22:00:00 +02:00
ADD1(va("\tint\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
default:
2008-07-24 22:00:00 +02:00
ADD1(va("\tint\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
}
}
2008-07-24 22:00:00 +02:00
ADD1("};\n\n");
2007-10-03 22:00:00 +02:00
// write entvars_t
2008-07-24 22:00:00 +02:00
ADD1("struct");
ADD2(va(" %s_entvars_s", lwr_prefix ));
2007-10-03 22:00:00 +02:00
ADD1("\n{\n");
2008-07-24 22:00:00 +02:00
// write entvars_t
for( d = pr.def_head.next; d; d = d->next )
2007-10-03 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
if( !com.strcmp (d->name, "end_sys_fields")) break;
if( d->type->type != ev_field ) continue;
if( com.strchr( d->name, '[' )) continue; // skip arrays
if( d->arraysize > 1 ) com.snprintf( array_size, 8, "[%i]", d->arraysize );
else array_size[0] = '\0'; // clear array
switch( d->type->aux_type->type )
2007-10-03 22:00:00 +02:00
{
case ev_float:
2008-07-24 22:00:00 +02:00
ADD1(va("\tfloat\t%s%s;\n",d->name, array_size ));
2007-10-03 22:00:00 +02:00
break;
case ev_vector:
2008-07-24 22:00:00 +02:00
ADD1(va("\tvec3_t\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
d = d->next->next->next; // skip the elements
break;
case ev_string:
2008-07-24 22:00:00 +02:00
ADD1(va("\tstring_t\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
case ev_function:
2008-07-24 22:00:00 +02:00
ADD1(va("\tfunc_t\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
case ev_entity:
2008-07-24 22:00:00 +02:00
ADD1(va("\tint\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
case ev_integer:
2008-07-24 22:00:00 +02:00
ADD1(va("\tint\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
default:
2008-07-24 22:00:00 +02:00
ADD1(va("\tint\t%s%s;\n",d->name, array_size));
2007-10-03 22:00:00 +02:00
break;
}
}
2008-07-24 22:00:00 +02:00
ADD1("};\n\n");
ADD2(va("#define PROG_CRC_%s\t\t%i\n", header_name, crc ));
ADD2(va("\n#endif//%s_EDICT_H", upr_prefix ));
2007-10-03 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
if( ForcedCRC ) crc = ForcedCRC; // potentially backdoor
2007-10-03 22:00:00 +02:00
2008-07-19 22:00:00 +02:00
if( host_instance == HOST_NORMAL || host_instance == HOST_DEDICATED )
{
// make sure what progs file will be placed into right directory
2008-07-24 22:00:00 +02:00
com.snprintf( progsoutname, MAX_SYSPATH, "%s/%s.dat", GI->vprogs_dir, header_name );
2008-07-19 22:00:00 +02:00
}
switch( crc )
2007-10-03 22:00:00 +02:00
{
2008-08-18 22:00:00 +02:00
case 12094:
2007-11-20 22:00:00 +01:00
PR_Message("Xash3D unmodified server.dat\n");
2008-06-04 22:00:00 +02:00
if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "server.dat");
2008-01-28 22:00:00 +01:00
break;
2008-07-30 22:00:00 +02:00
case 3720:
2008-05-23 22:00:00 +02:00
PR_Message("Xash3D unmodified client.dat\n");
2008-06-04 22:00:00 +02:00
if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "client.dat");
2008-05-23 22:00:00 +02:00
break;
2008-07-24 22:00:00 +02:00
case 2158:
2008-01-28 22:00:00 +01:00
PR_Message("Xash3D unmodified uimenu.dat\n");
2008-06-04 22:00:00 +02:00
if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "uimenu.dat");
2007-11-20 22:00:00 +01:00
break;
2007-10-08 22:00:00 +02:00
default:
2007-10-17 22:00:00 +02:00
PR_Message("Custom progs crc %d\n", crc );
2008-06-04 22:00:00 +02:00
if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "progs.dat");
2008-08-09 22:00:00 +02:00
if( host_instance != HOST_QCCLIB ) break;
com.snprintf( path, MAX_STRING, "../%s_edict.h", lwr_prefix );
PR_Message( "writing %s\n", path ); // auto-determine
FS_WriteFile( path, file, com.strlen(file));
2007-10-03 22:00:00 +02:00
break;
}
return crc;
}
2007-10-08 22:00:00 +02:00
void PR_WriteDAT( void )
{
ddef_t *dd;
2007-11-18 22:00:00 +01:00
file_t *f;
2007-10-08 22:00:00 +02:00
dprograms_t progs; // header
char element[MAX_NAME];
2008-07-24 22:00:00 +02:00
int i, crc, progsize, num_ref;
2007-10-08 22:00:00 +02:00
def_t *def, *comp_x, *comp_y, *comp_z;
2008-07-24 22:00:00 +02:00
crc = PR_WriteProgdefs(); // write progdefs.h
2007-10-08 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
progs.flags = 0;
2007-10-08 22:00:00 +02:00
2007-11-25 22:00:00 +01:00
if(numstatements > MAX_STATEMENTS) PR_ParseError(ERR_INTERNAL, "Too many statements - %i\n", numstatements );
if(strofs > MAX_STRINGS) PR_ParseError(ERR_INTERNAL, "Too many strings - %i\n", strofs );
2007-10-08 22:00:00 +02:00
PR_UnmarshalLocals();
2008-07-24 22:00:00 +02:00
if(opt_compstrings) progs.flags |= COMP_STRINGS;
if(opt_compfunctions)
2007-10-08 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
progs.flags |= COMP_FUNCTIONS;
progs.flags |= COMP_STATEMENTS;
2007-10-08 22:00:00 +02:00
}
2008-07-24 22:00:00 +02:00
if(opt_compress_other)
{
progs.flags |= COMP_DEFS;
progs.flags |= COMP_FIELDS;
progs.flags |= COMP_GLOBALS;
}
// compression of blocks?
if( opt_writelinenums ) progs.flags |= COMP_LINENUMS;
if( opt_writetypes ) progs.flags |= COMP_TYPES;
2007-10-08 22:00:00 +02:00
// part of how compilation works. This def is always present, and never used.
def = PR_GetDef(NULL, "end_sys_globals", NULL, false, 0);
if(def) def->references++;
def = PR_GetDef(NULL, "end_sys_fields", NULL, false, 0);
if(def) def->references++;
for (def = pr.def_head.next; def; def = def->next)
{
if (def->type->type == ev_vector || (def->type->type == ev_field && def->type->aux_type->type == ev_vector))
{
// do the references, so we don't get loadsa not referenced VEC_HULL_MINS_x
2008-06-04 22:00:00 +02:00
com.sprintf(element, "%s_x", def->name);
2007-10-08 22:00:00 +02:00
comp_x = PR_GetDef(NULL, element, def->scope, false, 0);
2008-06-04 22:00:00 +02:00
com.sprintf(element, "%s_y", def->name);
2007-10-08 22:00:00 +02:00
comp_y = PR_GetDef(NULL, element, def->scope, false, 0);
2008-06-04 22:00:00 +02:00
com.sprintf(element, "%s_z", def->name);
2007-10-08 22:00:00 +02:00
comp_z = PR_GetDef(NULL, element, def->scope, false, 0);
num_ref = def->references;
if (comp_x && comp_y && comp_z)
{
num_ref += comp_x->references;
num_ref += comp_y->references;
num_ref += comp_z->references;
if (!def->references)
{
if (!comp_x->references || !comp_y->references || !comp_z->references)
num_ref = 0; // one of these vars is useless...
}
def->references = num_ref;
if (!num_ref) num_ref = 1;
if (comp_x) comp_x->references = num_ref;
if (comp_y) comp_y->references = num_ref;
if (comp_z) comp_z->references = num_ref;
}
}
if (def->references <= 0)
{
if(def->local) PR_Warning(WARN_NOTREFERENCED, strings + def->s_file, def->s_line, "'%s' : unreferenced local variable", def->name);
if (opt_unreferenced && def->type->type != ev_field) continue;
}
if (def->type->type == ev_function)
{
2008-08-13 22:00:00 +02:00
if( opt_function_names && functions[G_FUNCTION(def->ofs)].first_statement < 0 )
2007-10-08 22:00:00 +02:00
{
def->name = "";
}
2008-08-13 22:00:00 +02:00
if( !def->timescalled )
2007-10-08 22:00:00 +02:00
{
2008-08-13 22:00:00 +02:00
if( def->references <= 1 )
2007-10-08 22:00:00 +02:00
PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, "%s is never directly called or referenced (spawn function or dead code)", def->name);
}
2008-08-13 22:00:00 +02:00
if( opt_stripfunctions && def->timescalled >= def->references - 1 )
2007-10-08 22:00:00 +02:00
{
// make sure it's not copied into a different var.
// if it ever does self.think then it could be needed for saves.
// if it's only ever called explicitly, the engine doesn't need to know.
continue;
}
}
2008-08-13 22:00:00 +02:00
else if( def->type->type == ev_field )
2007-10-08 22:00:00 +02:00
{
dd = &fields[numfielddefs];
dd->type = def->type->aux_type->type;
2008-08-13 22:00:00 +02:00
dd->s_name = PR_CopyString( def->name, opt_noduplicatestrings );
2007-10-08 22:00:00 +02:00
dd->ofs = G_INT(def->ofs);
numfielddefs++;
}
else if ((def->scope||def->constant) && (def->type->type != ev_string || opt_constant_names_strings))
{
if (opt_constant_names) continue;
}
dd = &qcc_globals[numglobaldefs];
numglobaldefs++;
if (opt_writetypes) dd->type = def->type-qcc_typeinfo;
else dd->type = def->type->type;
if ( def->saved && ((!def->initialized || def->type->type == ev_function) && def->type->type != ev_field && def->scope == NULL))
dd->type |= DEF_SAVEGLOBAL;
if (def->shared) dd->type |= DEF_SHARED;
2008-06-04 22:00:00 +02:00
if (opt_locals && (def->scope || !com.strcmp(def->name, "IMMEDIATE")))
2007-10-08 22:00:00 +02:00
{
dd->s_name = 0;
}
else dd->s_name = PR_CopyString (def->name, opt_noduplicatestrings );
dd->ofs = def->ofs;
}
2007-11-25 22:00:00 +01:00
if(numglobaldefs > MAX_GLOBALS) PR_ParseError(ERR_INTERNAL, "Too many globals - %i\n", numglobaldefs );
2007-10-08 22:00:00 +02:00
strofs = (strofs + 3) & ~3;
PR_Message("Linking...\n");
2008-08-06 22:00:00 +02:00
if( host_instance == HOST_QCCLIB )
2008-07-19 22:00:00 +02:00
{
// don't flood into engine console
MsgDev(D_INFO, "%6i strofs (of %i)\n", strofs, MAX_STRINGS);
MsgDev(D_INFO, "%6i numstatements (of %i)\n", numstatements, MAX_STATEMENTS);
MsgDev(D_INFO, "%6i numfunctions (of %i)\n", numfunctions, MAX_FUNCTIONS);
MsgDev(D_INFO, "%6i numglobaldefs (of %i)\n", numglobaldefs, MAX_GLOBALS);
MsgDev(D_INFO, "%6i numfielddefs (%i unique) (of %i)\n", numfielddefs, pr.size_fields, MAX_FIELDS);
MsgDev(D_INFO, "%6i numpr_globals (of %i)\n", numpr_globals, MAX_REGS);
}
2007-11-18 22:00:00 +01:00
f = FS_Open( progsoutname, "wb" );
2008-07-19 22:00:00 +02:00
if( !f ) PR_ParseError( ERR_INTERNAL, "Couldn't open file %s", progsoutname );
2007-11-20 22:00:00 +01:00
FS_Write(f, &progs, sizeof(progs));
FS_Write(f, "\r\n\r\n", 4);
2008-06-04 22:00:00 +02:00
FS_Write(f, v_copyright, com.strlen(v_copyright) + 1);
2007-11-20 22:00:00 +01:00
FS_Write(f, "\r\n\r\n", 4);
2007-10-08 22:00:00 +02:00
2007-11-20 22:00:00 +01:00
progs.ofs_strings = FS_Tell(f);
2007-10-08 22:00:00 +02:00
progs.numstrings = strofs;
2008-07-24 22:00:00 +02:00
PR_WriteBlock(f, progs.ofs_strings, strings, strofs, progs.flags & COMP_STRINGS);
2007-10-08 22:00:00 +02:00
2007-11-20 22:00:00 +01:00
progs.ofs_statements = FS_Tell(f);
2007-10-08 22:00:00 +02:00
progs.numstatements = numstatements;
2008-07-24 22:00:00 +02:00
for (i = 0; i < numstatements; i++)
2007-10-08 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
statements[i].op = LittleLong(statements[i].op);
statements[i].a = LittleLong(statements[i].a);
statements[i].b = LittleLong(statements[i].b);
statements[i].c = LittleLong(statements[i].c);
2007-10-08 22:00:00 +02:00
}
2008-07-24 22:00:00 +02:00
PR_WriteBlock(f, progs.ofs_statements, statements, progs.numstatements*sizeof(dstatement_t), progs.flags & COMP_STATEMENTS);
2007-10-08 22:00:00 +02:00
2007-11-20 22:00:00 +01:00
progs.ofs_functions = FS_Tell(f);
2007-10-08 22:00:00 +02:00
progs.numfunctions = numfunctions;
for (i = 0; i < numfunctions; i++)
{
functions[i].first_statement = LittleLong (functions[i].first_statement);
functions[i].parm_start = LittleLong (functions[i].parm_start);
functions[i].s_name = LittleLong (functions[i].s_name);
functions[i].s_file = LittleLong (functions[i].s_file);
functions[i].numparms = LittleLong ((functions[i].numparms > MAX_PARMS) ? MAX_PARMS : functions[i].numparms);
functions[i].locals = LittleLong (functions[i].locals);
}
2008-07-24 22:00:00 +02:00
PR_WriteBlock(f, progs.ofs_functions, functions, progs.numfunctions*sizeof(dfunction_t), progs.flags & COMP_FUNCTIONS);
2007-10-08 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
progs.ofs_globaldefs = FS_Tell(f);
progs.numglobaldefs = numglobaldefs;
for (i = 0; i < numglobaldefs; i++)
2007-10-08 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
qcc_globals[i].type = LittleLong(qcc_globals[i].type);
qcc_globals[i].ofs = LittleLong(qcc_globals[i].ofs);
qcc_globals[i].s_name = LittleLong(qcc_globals[i].s_name);
}
2007-10-08 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
PR_WriteBlock(f, progs.ofs_globaldefs, qcc_globals, progs.numglobaldefs*sizeof(ddef_t), progs.flags & COMP_DEFS);
2007-10-08 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
progs.ofs_fielddefs = FS_Tell(f);
progs.numfielddefs = numfielddefs;
2007-10-08 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
for (i = 0; i < numfielddefs; i++)
{
fields[i].type = LittleLong(fields[i].type);
fields[i].ofs = LittleLong(fields[i].ofs);
fields[i].s_name = LittleLong(fields[i].s_name);
2007-10-08 22:00:00 +02:00
}
2008-07-24 22:00:00 +02:00
PR_WriteBlock(f, progs.ofs_fielddefs, fields, progs.numfielddefs*sizeof(ddef_t), progs.flags & COMP_FIELDS);
2007-11-20 22:00:00 +01:00
progs.ofs_globals = FS_Tell(f);
2007-10-08 22:00:00 +02:00
progs.numglobals = numpr_globals;
for (i = 0; (uint) i < numpr_globals; i++) ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
2008-07-24 22:00:00 +02:00
PR_WriteBlock(f, progs.ofs_globals, pr_globals, numpr_globals*4, progs.flags & COMP_GLOBALS);
2007-10-08 22:00:00 +02:00
if(opt_writetypes)
{
for (i = 0; i < numtypeinfos; i++)
{
if (qcc_typeinfo[i].aux_type) qcc_typeinfo[i].aux_type = (type_t*)(qcc_typeinfo[i].aux_type - qcc_typeinfo);
if (qcc_typeinfo[i].next) qcc_typeinfo[i].next = (type_t*)(qcc_typeinfo[i].next - qcc_typeinfo);
qcc_typeinfo[i].name = (char *)PR_CopyString(qcc_typeinfo[i].name, true );
}
}
2008-07-24 22:00:00 +02:00
progs.ident = VPROGSHEADER32;
progs.version = VPROGS_VERSION;
progs.ofssources = 0;
2007-10-08 22:00:00 +02:00
progs.ofslinenums = 0;
2007-11-27 22:00:00 +01:00
progs.ident = 0;
2007-10-08 22:00:00 +02:00
progs.ofsbodylessfuncs = 0;
progs.numbodylessfuncs = 0;
progs.ofs_types = 0;
progs.numtypes = 0;
2008-07-24 22:00:00 +02:00
progs.ofsbodylessfuncs = FS_Tell(f);
progs.numbodylessfuncs = PR_WriteBodylessFuncs(f);
2007-10-08 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
if( opt_writelinenums )
2007-10-08 22:00:00 +02:00
{
2008-07-24 22:00:00 +02:00
progs.ofslinenums = FS_Tell(f);
PR_WriteBlock(f, progs.ofslinenums, statement_linenums, numstatements*sizeof(int), progs.flags & COMP_LINENUMS);
}
2007-10-08 22:00:00 +02:00
2008-07-24 22:00:00 +02:00
if( opt_writetypes )
{
progs.ofs_types = FS_Tell(f);
progs.numtypes = numtypeinfos;
PR_WriteBlock(f, progs.ofs_types, qcc_typeinfo, progs.numtypes*sizeof(type_t), progs.flags & COMP_TYPES);
2007-10-08 22:00:00 +02:00
}
2008-07-24 22:00:00 +02:00
PR_WriteSourceFiles( f, &progs, opt_writesources );
2007-10-08 22:00:00 +02:00
2007-11-20 22:00:00 +01:00
progsize = ((FS_Tell(f)+3) & ~3);
2007-10-08 22:00:00 +02:00
progs.entityfields = pr.size_fields;
progs.crc = crc;
// byte swap the header and write it out
for (i = 0; i < sizeof(progs)/4; i++)((int *)&progs)[i] = LittleLong (((int *)&progs)[i]);
2007-11-20 22:00:00 +01:00
FS_Seek(f, 0, SEEK_SET);
FS_Write(f, &progs, sizeof(progs));
2008-07-19 22:00:00 +02:00
if( asmfile ) FS_Close(asmfile);
2007-10-08 22:00:00 +02:00
2008-08-06 22:00:00 +02:00
if( host_instance == HOST_QCCLIB )
2008-07-19 22:00:00 +02:00
MsgDev(D_INFO, "Writing %s, total size %i bytes\n", progsoutname, progsize );
FS_Close( f );
2007-10-08 22:00:00 +02:00
}
2007-10-03 22:00:00 +02:00
/*
=======================
PR_UnmarshalLocals
marshalled locals remaps all the functions to use the range MAX_REGS
onwards for the offset to thier locals.
this function remaps all the locals back into the function.
=======================
*/
void PR_UnmarshalLocals( void )
{
def_t *def;
uint ofs;
uint maxo;
int i;
ofs = numpr_globals;
maxo = ofs;
for (def = pr.def_head.next ; def ; def = def->next)
{
if (def->ofs >= MAX_REGS) //unmap defs.
{
def->ofs = def->ofs + ofs - MAX_REGS;
if (maxo < def->ofs)
maxo = def->ofs;
}
}
for (i = 0; i < numfunctions; i++)
{
if (functions[i].parm_start == MAX_REGS)
functions[i].parm_start = ofs;
}
PR_RemapOffsets(0, numstatements, MAX_REGS, MAX_REGS + maxo-numpr_globals + 3, ofs);
numpr_globals = maxo+3;
if (numpr_globals > MAX_REGS)
2007-10-04 22:00:00 +02:00
PR_ParseError(ERR_INTERNAL, "Too many globals are in use to unmarshal all locals");
2007-10-03 22:00:00 +02:00
2007-10-08 22:00:00 +02:00
if (maxo-ofs) PR_Message("Total of %i marshalled globals\n", maxo-ofs);
2007-10-03 22:00:00 +02:00
}
2008-07-19 22:00:00 +02:00
byte *PR_LoadFile( char *filename, bool crash, int type )
2007-10-03 22:00:00 +02:00
{
2008-07-19 22:00:00 +02:00
int length;
2007-10-08 22:00:00 +02:00
cachedsourcefile_t *newfile;
2008-07-19 22:00:00 +02:00
string fullname;
byte *file;
char *path;
// NOTE: in-game we can't use ../pathes, but root directory always
// ahead over ../pathes, so tranlate path from
// ../common/svc_user.h to source/common/svc_user.h
if( host_instance == HOST_NORMAL || host_instance == HOST_DEDICATED )
{
if((path = strstr( filename, ".." )))
{
path += 2; // skip ..
com.snprintf( fullname, MAX_STRING, "%s%s", GI->source_dir, path );
}
else com.snprintf( fullname, MAX_STRING, "%s/%s/%s", GI->source_dir, sourcedir, filename );
}
else com.strncpy( fullname, filename, MAX_STRING );
file = FS_LoadFile( fullname, &length );
2007-10-08 22:00:00 +02:00
2008-07-19 22:00:00 +02:00
if( !file || !length )
2007-10-17 22:00:00 +02:00
{
2008-07-19 22:00:00 +02:00
if( crash ) PR_ParseError(ERR_INTERNAL, "Couldn't open file %s", filename);
2007-10-17 22:00:00 +02:00
else return NULL;
}
2008-07-19 22:00:00 +02:00
newfile = (cachedsourcefile_t*)Qalloc(sizeof(cachedsourcefile_t));
2007-10-08 22:00:00 +02:00
newfile->next = sourcefile;
sourcefile = newfile; // make chain
2008-07-19 22:00:00 +02:00
com.strcpy(sourcefile->filename, fullname );
2007-10-08 22:00:00 +02:00
sourcefile->file = file;
sourcefile->type = type;
sourcefile->size = length;
2007-10-03 22:00:00 +02:00
2007-10-08 22:00:00 +02:00
return sourcefile->file;
2007-10-03 22:00:00 +02:00
}
2008-07-19 22:00:00 +02:00
bool PR_Include( char *filename )
2007-10-03 22:00:00 +02:00
{
2007-10-08 22:00:00 +02:00
char *newfile;
char fname[512];
char *opr_file_p;
string_t os_file, os_file2;
int opr_source_line;
char *ocompilingfile;
includechunk_t *oldcurrentchunk;
ocompilingfile = compilingfile;
os_file = s_file;
os_file2 = s_file2;
opr_source_line = pr_source_line;
opr_file_p = pr_file_p;
oldcurrentchunk = currentchunk;
2008-06-04 22:00:00 +02:00
com.strcpy(fname, filename);
2008-07-19 22:00:00 +02:00
newfile = QCC_LoadFile( fname, true );
2007-10-08 22:00:00 +02:00
currentchunk = NULL;
pr_file_p = newfile;
2008-07-19 22:00:00 +02:00
PR_CompileFile( newfile, fname );
2007-10-08 22:00:00 +02:00
currentchunk = oldcurrentchunk;
compilingfile = ocompilingfile;
s_file = os_file;
s_file2 = os_file2;
pr_source_line = opr_source_line;
pr_file_p = opr_file_p;
2007-10-03 22:00:00 +02:00
2007-10-08 22:00:00 +02:00
return true;
2007-10-03 22:00:00 +02:00
}
2007-11-20 22:00:00 +01:00
void PR_WriteBlock(file_t *f, fs_offset_t pos, const void *data, size_t blocksize, bool compress)
2007-10-03 22:00:00 +02:00
{
2007-11-20 22:00:00 +01:00
vfile_t *h;
int len = 0;
2007-10-03 22:00:00 +02:00
if (compress)
{
2007-11-20 22:00:00 +01:00
h = VFS_Open( f, "wz" );
VFS_Write( h, data, blocksize ); // write into buffer
FS_Write(f, &len, sizeof(int)); // first four bytes it's a compressed filesize
f = VFS_Close(h); // deflate, then write into disk
len = LittleLong(FS_Tell(f) - pos); // calculate complength
FS_Seek(f, pos, SEEK_SET); // seek back to start block...
FS_Write(f, &len, sizeof(int)); // ... and write real complength value.
FS_Seek(f, 0, SEEK_END); // return
2007-10-03 22:00:00 +02:00
}
2007-11-20 22:00:00 +01:00
else FS_Write(f, data, blocksize); // just write data
2007-10-17 22:00:00 +02:00
}
byte *PR_CreateProgsSRC( void )
{
2007-12-11 22:00:00 +01:00
search_t *qc = FS_Search( "*", true );
2007-10-17 22:00:00 +02:00
const char *datname = "unknown.dat\n"; // outname will be set by PR_WriteProgdefs
byte *newprogs_src = NULL;
char headers[2][MAX_QPATH]; // contains filename with struct description
2007-12-11 22:00:00 +01:00
char searchmask[8][16];
int i, k, j = 0;
bool have_entvars = 0;
bool have_globals = 0;
// hard-coded table! don't change!
2008-06-04 22:00:00 +02:00
com.strcpy(searchmask[0], "qh" ); // quakec header
com.strcpy(searchmask[1], "h" ); // c-style header
com.strcpy(searchmask[2], "qc" ); // quakec sources
com.strcpy(searchmask[3], "c" ); // c-style sources
2007-12-11 22:00:00 +01:00
if(!qc)
2007-10-17 22:00:00 +02:00
{
PR_ParseError(ERR_INTERNAL, "Couldn't open file progs.src" );
return NULL;
}
memset(headers, '/0', 2 * MAX_QPATH);
for(i = 0; i < qc->numfilenames; i++)
{
2007-12-11 22:00:00 +01:00
// search by mask
for( k = 0; k < 8; k++)
2007-10-17 22:00:00 +02:00
{
2007-12-11 22:00:00 +01:00
// skip blank mask
2008-06-04 22:00:00 +02:00
if(!com.strlen(searchmask[k])) continue;
2007-12-11 22:00:00 +01:00
if(!com.stricmp(searchmask[k], FS_FileExtension(qc->filenames[i]))) // validate ext
2007-10-17 22:00:00 +02:00
{
2007-12-11 22:00:00 +01:00
if(Com_LoadScript( qc->filenames[i], NULL, 0 ))
2007-10-17 22:00:00 +02:00
{
2007-12-11 22:00:00 +01:00
while ( 1 )
{
// parse all sources for "end_sys_globals"
if(!Com_GetToken( true )) break; //EOF
if(Com_MatchToken( "end_sys_globals" ))
{
com.strncpy(headers[0], qc->filenames[i], MAX_QPATH );
have_globals = true;
}
else if(Com_MatchToken( "end_sys_fields" ))
{
com.strncpy(headers[1], qc->filenames[i], MAX_QPATH );
have_entvars = true;
}
if(have_globals && have_entvars)
goto buildnewlist; // end of parsing
}
2007-10-17 22:00:00 +02:00
}
2007-12-11 22:00:00 +01:00
}
2007-10-17 22:00:00 +02:00
}
2007-12-11 22:00:00 +01:00
}
2007-10-17 22:00:00 +02:00
2007-12-11 22:00:00 +01:00
// globals and locals not declared
PR_ParseError(ERR_INTERNAL, "Couldn't open file progs.src" );
return NULL;
2007-10-17 22:00:00 +02:00
buildnewlist:
2008-06-04 22:00:00 +02:00
newprogs_src = Qrealloc(newprogs_src, j + com.strlen(datname) + 1); // outfile name
Mem_Copy(newprogs_src + j, (char *)datname, com.strlen(datname));
j += com.strlen(datname) + 1; // null term
2007-10-17 22:00:00 +02:00
// file contains "sys_globals" and possible "sys_fields"
2008-06-04 22:00:00 +02:00
newprogs_src = Qrealloc(newprogs_src, j + com.strlen(headers[0]) + 2); // first file
com.strncat(newprogs_src, va("%s\n", headers[0]), com.strlen(headers[0]) + 1);
j += com.strlen(headers[0]) + 2; //null term
2007-10-17 22:00:00 +02:00
if(STRCMP(headers[0], headers[1] ))
{
// file contains sys_fields description
2008-06-04 22:00:00 +02:00
newprogs_src = Qrealloc(newprogs_src, j + com.strlen(headers[1]) + 2); // second file (option)
com.strncat(newprogs_src, va("%s\n", headers[1]), com.strlen(headers[1]) + 1);
j += com.strlen(headers[1]) + 2; //null term
2007-10-17 22:00:00 +02:00
}
2007-12-11 22:00:00 +01:00
// add headers
for(i = 0; i < qc->numfilenames; i++)
{
for( k = 0; k < 2; k++)
{
// skip blank mask
2008-06-04 22:00:00 +02:00
if(!com.strlen(searchmask[k])) continue;
2007-12-11 22:00:00 +01:00
if(!com.stricmp(searchmask[k], FS_FileExtension(qc->filenames[i]))) // validate ext
{
2008-06-04 22:00:00 +02:00
if(!com.strcmp(qc->filenames[i], headers[0]) || !com.strcmp(qc->filenames[i], headers[1]))
2007-12-11 22:00:00 +01:00
break; //we already have it, just skip
2008-06-04 22:00:00 +02:00
newprogs_src = Qrealloc( newprogs_src, j + com.strlen(qc->filenames[i]) + 2);
com.strncat(newprogs_src, va("%s\n", qc->filenames[i]), com.strlen(qc->filenames[i]) + 1);
j += com.strlen(qc->filenames[i]) + 2;
2007-12-11 22:00:00 +01:00
}
}
}
2007-10-17 22:00:00 +02:00
// add other sources
for(i = 0; i < qc->numfilenames; i++)
{
2007-12-11 22:00:00 +01:00
for( k = 2; k < 8; k++)
{
// skip blank mask
2008-06-04 22:00:00 +02:00
if(!com.strlen(searchmask[k])) continue;
2007-12-11 22:00:00 +01:00
if(!com.stricmp(searchmask[k], FS_FileExtension(qc->filenames[i]))) // validate ext
{
2008-06-04 22:00:00 +02:00
if(!com.strcmp(qc->filenames[i], headers[0]) || !com.strcmp(qc->filenames[i], headers[1]))
2007-12-11 22:00:00 +01:00
break; //we already have it, just skip
2008-06-04 22:00:00 +02:00
newprogs_src = Qrealloc( newprogs_src, j + com.strlen(qc->filenames[i]) + 2);
com.strncat(newprogs_src, va("%s\n", qc->filenames[i]), com.strlen(qc->filenames[i]) + 1);
j += com.strlen(qc->filenames[i]) + 2;
2007-12-11 22:00:00 +01:00
}
}
2007-10-17 22:00:00 +02:00
}
2008-01-28 22:00:00 +01:00
Mem_Free( qc ); // free search
2007-11-20 22:00:00 +01:00
FS_WriteFile("progs.src", newprogs_src, j );
2007-10-17 22:00:00 +02:00
return newprogs_src;
2007-10-03 22:00:00 +02:00
}