mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-24 05:11:42 +00:00
c218ede288
another builtin by name, and returns it. Soon I'll change all our new builtins to by allocated dynamically, as well as changing the number checkfunction uses, and happily break everything that uses them :D
1144 lines
24 KiB
C
1144 lines
24 KiB
C
/*
|
|
qfcc.c
|
|
|
|
QuakeForge Code Compiler (main program)
|
|
|
|
Copyright (C) 1996-1997 id Software, Inc.
|
|
Copyright (C) 2001 Jeff Teunissen <deek@dusknet.dhs.org>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public
|
|
License along with this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
$Id$
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
# include <sys/wait.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#include <QF/crc.h>
|
|
#include <QF/hash.h>
|
|
#include <QF/qendian.h>
|
|
#include <QF/sys.h>
|
|
|
|
#include "qfcc.h"
|
|
|
|
options_t options;
|
|
|
|
char sourcedir[1024];
|
|
char destfile[1024];
|
|
char debugfile[1024];
|
|
|
|
float pr_globals[MAX_REGS];
|
|
int numpr_globals;
|
|
|
|
char strings[MAX_STRINGS];
|
|
int strofs;
|
|
|
|
dstatement_t statements[MAX_STATEMENTS];
|
|
int numstatements;
|
|
int statement_linenums[MAX_STATEMENTS];
|
|
|
|
dfunction_t functions[MAX_FUNCTIONS];
|
|
int numfunctions;
|
|
|
|
ddef_t globals[MAX_GLOBALS];
|
|
int numglobaldefs;
|
|
int num_localdefs;
|
|
|
|
ddef_t fields[MAX_FIELDS];
|
|
int numfielddefs;
|
|
|
|
char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];
|
|
int precache_sounds_block[MAX_SOUNDS];
|
|
int numsounds;
|
|
|
|
char precache_models[MAX_MODELS][MAX_DATA_PATH];
|
|
int precache_models_block[MAX_SOUNDS];
|
|
int nummodels;
|
|
|
|
char precache_files[MAX_FILES][MAX_DATA_PATH];
|
|
int precache_files_block[MAX_SOUNDS];
|
|
int numfiles;
|
|
|
|
/*
|
|
WriteFiles
|
|
|
|
Generates files.dat, which contains all of the data files actually used by
|
|
the game, to be processed by qfiles
|
|
*/
|
|
void
|
|
WriteFiles (void)
|
|
{
|
|
FILE *f;
|
|
int i;
|
|
char filename[1024];
|
|
|
|
sprintf (filename, "%s%cfiles.dat", sourcedir, PATH_SEPARATOR);
|
|
f = fopen (filename, "w");
|
|
if (!f)
|
|
Error ("Couldn't open %s", filename);
|
|
|
|
fprintf (f, "%i\n", numsounds);
|
|
for (i = 0; i < numsounds; i++)
|
|
fprintf (f, "%i %s\n", precache_sounds_block[i], precache_sounds[i]);
|
|
|
|
fprintf (f, "%i\n", nummodels);
|
|
for (i = 0; i < nummodels; i++)
|
|
fprintf (f, "%i %s\n", precache_models_block[i], precache_models[i]);
|
|
|
|
fprintf (f, "%i\n", numfiles);
|
|
for (i = 0; i < numfiles; i++)
|
|
fprintf (f, "%i %s\n", precache_files_block[i], precache_files[i]);
|
|
|
|
fclose (f);
|
|
}
|
|
|
|
/*
|
|
CopyString
|
|
|
|
Return an offset from the string heap
|
|
*/
|
|
static hashtab_t *strings_tab;
|
|
|
|
static const char *
|
|
stings_get_key (void *_str, void *unsued)
|
|
{
|
|
return (char*)_str;
|
|
}
|
|
|
|
int
|
|
CopyString (const char *str)
|
|
{
|
|
int old;
|
|
|
|
if (!strings_tab) {
|
|
strings_tab = Hash_NewTable (16381, stings_get_key, 0, 0);
|
|
}
|
|
old = strofs;
|
|
strcpy (strings + strofs, str);
|
|
strofs += strlen (str) + 1;
|
|
Hash_Add (strings_tab, strings + old);
|
|
return old;
|
|
}
|
|
|
|
int
|
|
ReuseString (const char *str)
|
|
{
|
|
char *s;
|
|
|
|
if (!strings_tab)
|
|
return CopyString (str);
|
|
s = Hash_Find (strings_tab, str);
|
|
if (s)
|
|
return s - strings;
|
|
return CopyString (str);
|
|
}
|
|
|
|
void
|
|
PrintStrings (void)
|
|
{
|
|
int i, l, j;
|
|
|
|
for (i = 0; i < strofs; i += l) {
|
|
l = strlen (strings + i) + 1;
|
|
printf ("%5i : ", i);
|
|
for (j = 0; j < l; j++) {
|
|
if (strings[i + j] == '\n') {
|
|
putchar ('\\');
|
|
putchar ('n');
|
|
} else {
|
|
putchar (strings[i + j]);
|
|
}
|
|
}
|
|
printf ("\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
PrintFunctions (void)
|
|
{
|
|
int i, j;
|
|
dfunction_t *d;
|
|
|
|
for (i = 0; i < numfunctions; i++) {
|
|
d = &functions[i];
|
|
printf ("%s : %s : %i %i (", strings + d->s_file, strings + d->s_name,
|
|
d->first_statement, d->parm_start);
|
|
for (j = 0; j < d->numparms; j++)
|
|
printf ("%i ", d->parm_size[j]);
|
|
printf (")\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
PrintFields (void)
|
|
{
|
|
int i;
|
|
ddef_t *d;
|
|
|
|
for (i = 0; i < numfielddefs; i++) {
|
|
d = &fields[i];
|
|
printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
|
|
}
|
|
}
|
|
|
|
void
|
|
PrintGlobals (void)
|
|
{
|
|
int i;
|
|
ddef_t *d;
|
|
|
|
for (i = 0; i < numglobaldefs; i++) {
|
|
d = &globals[i];
|
|
printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
InitData (void)
|
|
{
|
|
int i;
|
|
|
|
numstatements = 1;
|
|
strofs = 1;
|
|
numfunctions = 1;
|
|
numglobaldefs = 1;
|
|
numfielddefs = 1;
|
|
|
|
def_ret.ofs = OFS_RETURN;
|
|
for (i = 0; i < MAX_PARMS; i++)
|
|
def_parms[i].ofs = OFS_PARM0 + 3 * i;
|
|
}
|
|
|
|
|
|
void
|
|
WriteData (int crc)
|
|
{
|
|
def_t *def;
|
|
ddef_t *dd;
|
|
dprograms_t progs;
|
|
pr_debug_header_t debug;
|
|
FILE *h;
|
|
int i;
|
|
|
|
for (def = pr.def_head.def_next; def; def = def->def_next) {
|
|
if (def->scope)
|
|
continue;
|
|
if (def->type->type == ev_func) {
|
|
// df = &functions[numfunctions];
|
|
// numfunctions++;
|
|
} else if (def->type->type == ev_field) {
|
|
dd = &fields[numfielddefs];
|
|
numfielddefs++;
|
|
dd->type = def->type->aux_type->type;
|
|
dd->s_name = ReuseString (def->name);
|
|
dd->ofs = G_INT (def->ofs);
|
|
}
|
|
|
|
dd = &globals[numglobaldefs];
|
|
numglobaldefs++;
|
|
dd->type = def->type->type;
|
|
|
|
if (!def->initialized
|
|
&& def->type->type != ev_func
|
|
&& def->type->type != ev_field && def->scope == NULL)
|
|
dd->type |= DEF_SAVEGLOBAL;
|
|
dd->s_name = ReuseString (def->name);
|
|
dd->ofs = def->ofs;
|
|
}
|
|
|
|
// PrintStrings ();
|
|
// PrintFunctions ();
|
|
// PrintFields ();
|
|
// PrintGlobals ();
|
|
strofs = (strofs + 3) & ~3;
|
|
|
|
if (options.quiet < 2) {
|
|
printf ("%6i strofs\n", strofs);
|
|
printf ("%6i statements\n", numstatements);
|
|
printf ("%6i functions\n", numfunctions);
|
|
printf ("%6i globaldefs\n", numglobaldefs);
|
|
printf ("%6i locals size\n", num_localdefs);
|
|
printf ("%6i fielddefs\n", numfielddefs);
|
|
printf ("%6i pr_globals\n", numpr_globals);
|
|
}
|
|
|
|
h = SafeOpenWrite (destfile);
|
|
SafeWrite (h, &progs, sizeof (progs));
|
|
|
|
progs.ofs_strings = ftell (h);
|
|
progs.numstrings = strofs;
|
|
SafeWrite (h, strings, strofs);
|
|
|
|
progs.ofs_statements = ftell (h);
|
|
progs.numstatements = numstatements;
|
|
for (i = 0; i < numstatements; i++) {
|
|
statements[i].op = LittleShort (statements[i].op);
|
|
statements[i].a = LittleShort (statements[i].a);
|
|
statements[i].b = LittleShort (statements[i].b);
|
|
statements[i].c = LittleShort (statements[i].c);
|
|
}
|
|
SafeWrite (h, statements, numstatements * sizeof (dstatement_t));
|
|
|
|
progs.ofs_functions = ftell (h);
|
|
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);
|
|
functions[i].locals = LittleLong (functions[i].locals);
|
|
}
|
|
SafeWrite (h, functions, numfunctions * sizeof (dfunction_t));
|
|
|
|
progs.ofs_globaldefs = ftell (h);
|
|
progs.numglobaldefs = numglobaldefs;
|
|
for (i = 0; i < numglobaldefs; i++) {
|
|
globals[i].type = LittleShort (globals[i].type);
|
|
globals[i].ofs = LittleShort (globals[i].ofs);
|
|
globals[i].s_name = LittleLong (globals[i].s_name);
|
|
}
|
|
SafeWrite (h, globals, numglobaldefs * sizeof (ddef_t));
|
|
|
|
progs.ofs_fielddefs = ftell (h);
|
|
progs.numfielddefs = numfielddefs;
|
|
for (i = 0; i < numfielddefs; i++) {
|
|
fields[i].type = LittleShort (fields[i].type);
|
|
fields[i].ofs = LittleShort (fields[i].ofs);
|
|
fields[i].s_name = LittleLong (fields[i].s_name);
|
|
}
|
|
SafeWrite (h, fields, numfielddefs * sizeof (ddef_t));
|
|
|
|
progs.ofs_globals = ftell (h);
|
|
progs.numglobals = numpr_globals;
|
|
|
|
for (i = 0; i < numpr_globals; i++)
|
|
((int *) pr_globals)[i] = LittleLong (((int *) pr_globals)[i]);
|
|
|
|
SafeWrite (h, pr_globals, numpr_globals * 4);
|
|
|
|
if (options.quiet < 2)
|
|
printf ("%6i TOTAL SIZE\n", (int) ftell (h));
|
|
|
|
progs.entityfields = pr.size_fields;
|
|
|
|
progs.version = options.version;
|
|
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]);
|
|
|
|
fseek (h, 0, SEEK_SET);
|
|
SafeWrite (h, &progs, sizeof (progs));
|
|
fclose (h);
|
|
|
|
if (!options.debug) {
|
|
return;
|
|
}
|
|
|
|
h = SafeOpenRead (destfile);
|
|
|
|
debug.version = LittleLong (PROG_DEBUG_VERSION);
|
|
CRC_Init (&debug.crc);
|
|
while ((i = fgetc (h)) != EOF)
|
|
CRC_ProcessByte (&debug.crc, i);
|
|
fclose (h);
|
|
debug.crc = LittleShort (debug.crc);
|
|
debug.you_tell_me_and_we_will_both_know = 0;
|
|
|
|
h = SafeOpenWrite (debugfile);
|
|
SafeWrite (h, &debug, sizeof (debug));
|
|
|
|
debug.auxfunctions = LittleLong (ftell (h));
|
|
debug.num_auxfunctions = LittleLong (num_auxfunctions);
|
|
for (i = 0; i < num_auxfunctions; i++) {
|
|
auxfunctions[i].function = LittleLong (auxfunctions[i].function);
|
|
auxfunctions[i].source_line = LittleLong (auxfunctions[i].source_line);
|
|
auxfunctions[i].line_info = LittleLong (auxfunctions[i].line_info);
|
|
auxfunctions[i].local_defs = LittleLong (auxfunctions[i].local_defs);
|
|
auxfunctions[i].num_locals = LittleLong (auxfunctions[i].num_locals);
|
|
}
|
|
SafeWrite (h, auxfunctions, num_auxfunctions * sizeof (auxfunctions[0]));
|
|
|
|
debug.linenos = LittleLong (ftell (h));
|
|
debug.num_linenos = LittleLong (num_linenos);
|
|
for (i = 0; i < num_linenos; i++) {
|
|
linenos[i].fa.addr = LittleLong (linenos[i].fa.addr);
|
|
linenos[i].line = LittleLong (linenos[i].line);
|
|
}
|
|
SafeWrite (h, linenos, num_linenos * sizeof (linenos[0]));
|
|
|
|
debug.locals = LittleLong (ftell (h));
|
|
debug.num_locals = LittleLong (num_locals);
|
|
for (i = 0; i < num_locals; i++) {
|
|
locals[i].type = LittleShort (locals[i].type);
|
|
locals[i].ofs = LittleShort (locals[i].ofs);
|
|
locals[i].s_name = LittleLong (locals[i].s_name);
|
|
}
|
|
SafeWrite (h, locals, num_locals * sizeof (locals[0]));
|
|
|
|
fseek (h, 0, SEEK_SET);
|
|
SafeWrite (h, &debug, sizeof (debug));
|
|
fclose (h);
|
|
}
|
|
|
|
|
|
/*
|
|
PR_String
|
|
|
|
Returns a string suitable for printing (no newlines, max 60 chars length)
|
|
*/
|
|
char *
|
|
PR_String (char *string)
|
|
{
|
|
static char buf[80];
|
|
char *s;
|
|
|
|
s = buf;
|
|
*s++ = '"';
|
|
while (string && *string) {
|
|
|
|
if (s == buf + sizeof (buf) - 2)
|
|
break;
|
|
|
|
if (*string == '\n') {
|
|
*s++ = '\\';
|
|
*s++ = 'n';
|
|
} else if (*string == '"') {
|
|
*s++ = '\\';
|
|
*s++ = '"';
|
|
} else {
|
|
*s++ = *string;
|
|
}
|
|
string++;
|
|
|
|
if (s - buf > 60) {
|
|
*s++ = '.';
|
|
*s++ = '.';
|
|
*s++ = '.';
|
|
break;
|
|
}
|
|
}
|
|
*s++ = '"';
|
|
*s++ = 0;
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
|
|
def_t *
|
|
PR_DefForFieldOfs (gofs_t ofs)
|
|
{
|
|
def_t *d;
|
|
|
|
for (d = pr.def_head.def_next; d; d = d->def_next) {
|
|
if (d->type->type != ev_field)
|
|
continue;
|
|
if (*((int *) &pr_globals[d->ofs]) == ofs)
|
|
return d;
|
|
}
|
|
|
|
Error ("PR_DefForFieldOfs: couldn't find %i", ofs);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
PR_ValueString
|
|
|
|
Return a string describing *data in a type-specific manner
|
|
*/
|
|
char *
|
|
PR_ValueString (etype_t type, void *val)
|
|
{
|
|
static char line[256];
|
|
def_t *def;
|
|
dfunction_t *f;
|
|
|
|
switch (type) {
|
|
case ev_string:
|
|
sprintf (line, "%s", PR_String (strings + *(int *) val));
|
|
break;
|
|
case ev_entity:
|
|
sprintf (line, "entity %i", *(int *) val);
|
|
break;
|
|
case ev_func:
|
|
if (!(f = functions + *(int *) val))
|
|
sprintf (line, "undefined function");
|
|
else
|
|
sprintf (line, "%s()", strings + f->s_name);
|
|
break;
|
|
case ev_field:
|
|
def = PR_DefForFieldOfs (*(int *) val);
|
|
sprintf (line, ".%s", def->name);
|
|
break;
|
|
case ev_void:
|
|
sprintf (line, "void");
|
|
break;
|
|
case ev_float:
|
|
sprintf (line, "%5.1f", *(float *) val);
|
|
break;
|
|
case ev_vector:
|
|
sprintf (line, "'%5.1f %5.1f %5.1f'", ((float *) val)[0],
|
|
((float *) val)[1], ((float *) val)[2]);
|
|
break;
|
|
case ev_pointer:
|
|
sprintf (line, "pointer");
|
|
break;
|
|
default:
|
|
sprintf (line, "bad type %i", type);
|
|
break;
|
|
}
|
|
|
|
return line;
|
|
}
|
|
|
|
/*
|
|
PR_GlobalString
|
|
|
|
Return a string with a description and the contents of a global, padded
|
|
to 20 field width
|
|
*/
|
|
char *
|
|
PR_GlobalStringNoContents (gofs_t ofs)
|
|
{
|
|
int i;
|
|
def_t *def;
|
|
void *val;
|
|
static char line[128];
|
|
|
|
val = (void *) &pr_globals[ofs];
|
|
def = pr_global_defs[ofs];
|
|
if (!def) {
|
|
// Error ("PR_GlobalString: no def for %i", ofs);
|
|
sprintf (line, "%i(\?\?\?)", ofs);
|
|
} else {
|
|
sprintf (line, "%i(%s)", ofs, def->name);
|
|
}
|
|
|
|
for (i = strlen (line); i < 16; i++) {
|
|
strcat (line, " ");
|
|
}
|
|
strcat (line, " ");
|
|
|
|
return line;
|
|
}
|
|
|
|
char *
|
|
PR_GlobalString (gofs_t ofs)
|
|
{
|
|
char *s;
|
|
int i;
|
|
def_t *def;
|
|
void *val;
|
|
static char line[128];
|
|
|
|
val = (void *) &pr_globals[ofs];
|
|
|
|
if (!(def = pr_global_defs[ofs]))
|
|
return PR_GlobalStringNoContents (ofs);
|
|
|
|
if (def->initialized && def->type->type != ev_func) {
|
|
s = PR_ValueString (def->type->type, &pr_globals[ofs]);
|
|
sprintf (line, "%i(%s)", ofs, s);
|
|
} else {
|
|
sprintf (line, "%i(%s)", ofs, def->name);
|
|
}
|
|
|
|
for (i = strlen (line); i < 16; i++) {
|
|
strcat (line, " ");
|
|
}
|
|
strcat (line, " ");
|
|
|
|
return line;
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_PrintOfs
|
|
============
|
|
*/
|
|
void
|
|
PR_PrintOfs (gofs_t ofs)
|
|
{
|
|
printf ("%s\n", PR_GlobalString (ofs));
|
|
}
|
|
|
|
/*
|
|
=================
|
|
PR_PrintStatement
|
|
=================
|
|
*/
|
|
void
|
|
PR_PrintStatement (dstatement_t *s)
|
|
{
|
|
int i;
|
|
opcode_t *op;
|
|
|
|
op = PR_Opcode (s->op);
|
|
printf ("%4i : %4i : %s ", (int) (s - statements),
|
|
statement_linenums[s - statements], op->opname);
|
|
|
|
for (i = strlen (op->opname); i < 10; i++) {
|
|
printf (" ");
|
|
}
|
|
|
|
if (s->op == OP_IF || s->op == OP_IFNOT) {
|
|
printf ("%sbranch %i", PR_GlobalString (s->a), s->b);
|
|
} else if (s->op == OP_GOTO) {
|
|
printf ("branch %i", s->a);
|
|
} else if ((unsigned) (s->op - OP_STORE_F) < 6) {
|
|
printf ("%s", PR_GlobalString (s->a));
|
|
printf ("%s", PR_GlobalStringNoContents (s->b));
|
|
} else {
|
|
if (s->a)
|
|
printf ("%s", PR_GlobalString (s->a));
|
|
if (s->b)
|
|
printf ("%s", PR_GlobalString (s->b));
|
|
if (s->c)
|
|
printf ("%s", PR_GlobalStringNoContents (s->c));
|
|
}
|
|
printf ("\n");
|
|
}
|
|
|
|
|
|
/*
|
|
PR_PrintDefs
|
|
*/
|
|
void
|
|
PR_PrintDefs (void)
|
|
{
|
|
def_t *d;
|
|
|
|
for (d = pr.def_head.def_next; d; d = d->def_next)
|
|
PR_PrintOfs (d->ofs);
|
|
}
|
|
|
|
|
|
/*
|
|
PR_BeginCompilation
|
|
|
|
called before compiling a batch of files, clears the pr struct
|
|
*/
|
|
void
|
|
PR_BeginCompilation (void *memory, int memsize)
|
|
{
|
|
int i;
|
|
|
|
pr.memory = memory;
|
|
pr.max_memory = memsize;
|
|
|
|
numpr_globals = RESERVED_OFS;
|
|
pr.def_tail = &pr.def_head;
|
|
|
|
for (i = 0; i < RESERVED_OFS; i++)
|
|
pr_global_defs[i] = &def_void;
|
|
|
|
// link the function type in so state forward declarations match proper type
|
|
pr.types = &type_function;
|
|
type_function.next = NULL;
|
|
pr_error_count = 0;
|
|
}
|
|
|
|
void
|
|
PR_RelocateRefs (def_t *def)
|
|
{
|
|
statref_t *ref;
|
|
for (ref = def->refs; ref; ref = ref->next) {
|
|
switch (ref->field) {
|
|
case 0:
|
|
ref->statement->a = def->ofs;
|
|
break;
|
|
case 1:
|
|
ref->statement->b = def->ofs;
|
|
break;
|
|
case 2:
|
|
ref->statement->c = def->ofs;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
PR_FinishCompilation
|
|
|
|
called after all files are compiled to check for errors.
|
|
Returns false if errors were detected.
|
|
*/
|
|
qboolean
|
|
PR_FinishCompilation (void)
|
|
{
|
|
def_t *d;
|
|
qboolean errors = false;
|
|
function_t *f;
|
|
def_t *def;
|
|
expr_t e;
|
|
|
|
// check to make sure all functions prototyped have code
|
|
if (options.undefined_function_warning)
|
|
for (d = pr.def_head.def_next; d; d = d->def_next) {
|
|
if (d->type->type == ev_func && !d->scope) { // function args ok
|
|
// f = G_FUNCTION(d->ofs);
|
|
// if (!f || (!f->code && !f->builtin))
|
|
if (!d->initialized) {
|
|
warning (0, "function %s was not defined\n", d->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errors)
|
|
return !errors;
|
|
|
|
if (options.debug) {
|
|
e.type = ex_string;
|
|
e.e.string_val = debugfile;
|
|
PR_ReuseConstant (&e, PR_GetDef (&type_string, ".debug_file", 0,
|
|
&numpr_globals));
|
|
}
|
|
|
|
for (def = pr.def_head.def_next; def; def = def->def_next) {
|
|
if (def->scope)
|
|
continue;
|
|
PR_RelocateRefs (def);
|
|
}
|
|
|
|
for (f = pr_functions; f; f = f->next) {
|
|
if (f->builtin)
|
|
continue;
|
|
if (f->def->num_locals > num_localdefs)
|
|
num_localdefs = f->def->num_locals;
|
|
f->dfunc->parm_start = numpr_globals;
|
|
for (def = f->def->scope_next; def; def = def->scope_next) {
|
|
def->ofs += numpr_globals;
|
|
PR_RelocateRefs (def);
|
|
}
|
|
}
|
|
numpr_globals += num_localdefs;
|
|
|
|
return !errors;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
PR_WriteProgdefs
|
|
|
|
Writes the global and entity structures out.
|
|
Returns a crc of the header, to be stored in the progs file for comparison
|
|
at load time.
|
|
*/
|
|
int
|
|
PR_WriteProgdefs (char *filename)
|
|
{
|
|
def_t *d;
|
|
FILE *f;
|
|
unsigned short crc;
|
|
int c;
|
|
|
|
if (!options.quiet)
|
|
printf ("writing %s\n", filename);
|
|
f = fopen (filename, "w");
|
|
|
|
// print global vars until the first field is defined
|
|
fprintf (f, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{\tint\tpad[%i];\n", RESERVED_OFS);
|
|
|
|
for (d = pr.def_head.def_next; d; d = d->def_next) {
|
|
if (!strcmp (d->name, "end_sys_globals"))
|
|
break;
|
|
|
|
switch (d->type->type) {
|
|
case ev_float:
|
|
fprintf (f, "\tfloat\t%s;\n", d->name);
|
|
break;
|
|
case ev_vector:
|
|
fprintf (f, "\tvec3_t\t%s;\n", d->name);
|
|
d = d->def_next->def_next->def_next; // skip the elements
|
|
break;
|
|
case ev_string:
|
|
fprintf (f, "\tstring_t\t%s;\n", d->name);
|
|
break;
|
|
case ev_func:
|
|
fprintf (f, "\tfunc_t\t%s;\n", d->name);
|
|
break;
|
|
case ev_entity:
|
|
fprintf (f, "\tint\t%s;\n", d->name);
|
|
break;
|
|
default:
|
|
fprintf (f, "\tint\t%s;\n", d->name);
|
|
break;
|
|
}
|
|
}
|
|
fprintf (f, "} globalvars_t;\n\n");
|
|
|
|
// print all fields
|
|
fprintf (f, "typedef struct\n{\n");
|
|
for (d = pr.def_head.def_next; d; d = d->def_next) {
|
|
if (!strcmp (d->name, "end_sys_fields"))
|
|
break;
|
|
|
|
if (d->type->type != ev_field)
|
|
continue;
|
|
|
|
switch (d->type->aux_type->type) {
|
|
case ev_float:
|
|
fprintf (f, "\tfloat\t%s;\n", d->name);
|
|
break;
|
|
case ev_vector:
|
|
fprintf (f, "\tvec3_t\t%s;\n", d->name);
|
|
d = d->def_next->def_next->def_next; // skip the elements
|
|
break;
|
|
case ev_string:
|
|
fprintf (f, "\tstring_t\t%s;\n", d->name);
|
|
break;
|
|
case ev_func:
|
|
fprintf (f, "\tfunc_t\t%s;\n", d->name);
|
|
break;
|
|
case ev_entity:
|
|
fprintf (f, "\tint\t%s;\n", d->name);
|
|
break;
|
|
default:
|
|
fprintf (f, "\tint\t%s;\n", d->name);
|
|
break;
|
|
}
|
|
}
|
|
fprintf (f, "} entvars_t;\n\n");
|
|
|
|
fclose (f);
|
|
|
|
// do a crc of the file
|
|
CRC_Init (&crc);
|
|
f = fopen (filename, "r+");
|
|
while ((c = fgetc (f)) != EOF)
|
|
CRC_ProcessByte (&crc, (byte) c);
|
|
|
|
fprintf (f, "#define PROGHEADER_CRC %i\n", crc);
|
|
fclose (f);
|
|
|
|
return crc;
|
|
}
|
|
|
|
|
|
void
|
|
PrintFunction (const char *name)
|
|
{
|
|
int i;
|
|
dstatement_t *ds;
|
|
dfunction_t *df;
|
|
|
|
for (i = 0; i < numfunctions; i++)
|
|
if (!strcmp (name, strings + functions[i].s_name))
|
|
break;
|
|
|
|
if (i == numfunctions)
|
|
Error ("No function names \"%s\"", name);
|
|
|
|
df = functions + i;
|
|
|
|
printf ("Statements for %s:\n", name);
|
|
ds = statements + df->first_statement;
|
|
|
|
while (1) {
|
|
PR_PrintStatement (ds);
|
|
if (!ds->op)
|
|
break;
|
|
ds++;
|
|
}
|
|
}
|
|
|
|
void
|
|
PR_PrintFunction (def_t *def)
|
|
{
|
|
def_t *d;
|
|
statref_t *r;
|
|
|
|
printf ("%s\n", def->name);
|
|
for (d = def->scope_next; d; d = d->scope_next) {
|
|
printf ("%s: %d %d %d\n",
|
|
d->name ? d->name : "<temp>",
|
|
d->ofs, d->type->type, type_size[d->type->type]);
|
|
for (r = d->refs; r; r = r->next)
|
|
printf (" %d", r->statement - statements);
|
|
printf ("\n");
|
|
}
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
main
|
|
|
|
The nerve center of our little operation
|
|
*/
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
char *src;
|
|
char filename[1024];
|
|
int p, crc;
|
|
double start, stop;
|
|
int no_cpp = 0;
|
|
|
|
start = Sys_DoubleTime ();
|
|
|
|
myargc = argc;
|
|
myargv = argv;
|
|
|
|
options.version = PROG_VERSION;
|
|
|
|
if (CheckParm ("-h") || CheckParm ("--help")) {
|
|
printf ("%s - A compiler for the QuakeC language\n", argv[0]);
|
|
printf ("Usage: %s [options]\n", argv[0]);
|
|
printf (
|
|
"Options: \n"
|
|
" -s, --source <dir> look for progs.src in directory <dir>\n"
|
|
" -h, --help display this help and exit\n"
|
|
" -V, --version output version information and exit\n"
|
|
" --cow allow assignment to initialized globals\n"
|
|
" --id only support id (progs version 6) features\n"
|
|
" --warn=error treat warnings as errors\n"
|
|
" --undefined-function-warning warn when a function isn't defined\n"
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
if (CheckParm ("-V") || CheckParm ("--version")) {
|
|
printf ("%s version %s\n", PACKAGE, VERSION);
|
|
return 1;
|
|
}
|
|
|
|
if ((p = CheckParm ("--source")) && p < argc - 1) {
|
|
strcpy (sourcedir, argv[p + 1]);
|
|
} else {
|
|
if ((p = CheckParm ("-s")) && p < argc - 1) {
|
|
strcpy (sourcedir, argv[p + 1]);
|
|
} else {
|
|
strcpy (sourcedir, ".");
|
|
}
|
|
}
|
|
|
|
if (CheckParm ("--quiet=2")) { //FIXME: getopt, need getopt </#5>
|
|
options.quiet = 2;
|
|
}
|
|
|
|
if (CheckParm ("--quiet")) {
|
|
options.quiet = 1;
|
|
}
|
|
|
|
if (CheckParm ("--cow")) {
|
|
options.cow = 1;
|
|
}
|
|
|
|
if (CheckParm ("--id")) {
|
|
options.version = PROG_ID_VERSION;
|
|
}
|
|
|
|
if (CheckParm ("--debug")) {
|
|
options.debug = 1;
|
|
}
|
|
|
|
if (CheckParm ("--no-cpp")) {
|
|
no_cpp = 1;
|
|
}
|
|
|
|
// FIXME eww, really must go to getopt
|
|
if (CheckParm ("--warn=error")) {
|
|
options.warn_error = 1;
|
|
}
|
|
|
|
if (CheckParm ("--undefined-function-warning")) {
|
|
options.undefined_function_warning = 1;
|
|
}
|
|
|
|
if (strcmp (sourcedir, ".")) {
|
|
printf ("Source directory: %s\n", sourcedir);
|
|
}
|
|
|
|
PR_Opcode_Init_Tables ();
|
|
|
|
InitData ();
|
|
|
|
sprintf (filename, "%s/progs.src", sourcedir);
|
|
LoadFile (filename, (void *) &src);
|
|
|
|
if (!(src = COM_Parse (src)))
|
|
Error ("No destination filename. qfcc --help for info.\n");
|
|
|
|
strcpy (destfile, com_token);
|
|
if (!options.quiet) {
|
|
printf ("outputfile: %s\n", destfile);
|
|
}
|
|
if (options.debug) {
|
|
char *s;
|
|
strcpy (debugfile, com_token);
|
|
|
|
s = debugfile + strlen (debugfile);
|
|
while (s-- > debugfile) {
|
|
if (*s == '.')
|
|
break;
|
|
if (*s == '/' || *s == '\\') {
|
|
s = debugfile + strlen (debugfile);
|
|
break;
|
|
}
|
|
}
|
|
strcpy (s, ".sym");
|
|
if (!options.quiet)
|
|
printf ("debug file: %s\n", debugfile);
|
|
}
|
|
|
|
pr_dumpasm = false;
|
|
|
|
PR_BeginCompilation (malloc (0x100000), 0x100000);
|
|
|
|
// compile all the files
|
|
while ((src = COM_Parse (src))) {
|
|
#ifdef NEW_PARSER
|
|
#ifdef USE_CPP
|
|
pid_t pid;
|
|
char *temp1;
|
|
char *temp2 = strrchr (argv[0], PATH_SEPARATOR);
|
|
char tempname[1024];
|
|
int tempfd;
|
|
#endif
|
|
int error;
|
|
extern FILE *yyin;
|
|
int yyparse(void);
|
|
extern void clear_frame_macros (void);
|
|
//extern int yydebug;
|
|
//yydebug = 1;
|
|
|
|
sprintf (filename, "%s%c%s", sourcedir, PATH_SEPARATOR, com_token);
|
|
if (!options.quiet)
|
|
printf ("compiling %s\n", filename);
|
|
|
|
#ifdef USE_CPP
|
|
if (!no_cpp) {
|
|
temp1 = getenv ("TMPDIR");
|
|
if ((!temp1) || (!temp1[0])) {
|
|
temp1 = getenv ("TEMP");
|
|
if ((!temp1) || (!temp1[0])) {
|
|
temp1 = "/tmp";
|
|
}
|
|
}
|
|
|
|
snprintf (tempname, sizeof (tempname), "%s%c%sXXXXXX", temp1,
|
|
PATH_SEPARATOR, temp2 ? temp2 + 1 : argv[0]);
|
|
tempfd = mkstemp (tempname);
|
|
|
|
if ((pid = fork ()) == -1) {
|
|
perror ("fork");
|
|
return 1;
|
|
}
|
|
|
|
if (!pid) { // we're a child, check for abuse
|
|
execlp ("cpp", "-D__QFCC__=1", "-o", tempname, filename, NULL);
|
|
printf ("Child shouldn't reach here\n");
|
|
exit (1);
|
|
} else { // give parental guidance (or bury it in the back yard)
|
|
int status;
|
|
pid_t rc;
|
|
|
|
// printf ("pid = %d\n", pid);
|
|
if ((rc = waitpid (0, &status, 0 | WUNTRACED)) != pid) {
|
|
if (rc == -1) {
|
|
perror ("wait");
|
|
exit (1);
|
|
}
|
|
printf ("*** Uhh, dude, the wrong child (%d) just died.\n"
|
|
"*** Don't ask me, I can't figure it out either.\n",
|
|
rc);
|
|
exit (1);
|
|
}
|
|
if (WIFEXITED (status)) {
|
|
if (WEXITSTATUS (status)) {
|
|
printf ("cpp returned error code %d",
|
|
WEXITSTATUS (status));
|
|
exit (1);
|
|
}
|
|
} else {
|
|
printf ("cpp returned prematurely.");
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
yyin = fdopen (tempfd, "r+t");
|
|
} else {
|
|
yyin = fopen (filename, "rt");
|
|
}
|
|
#else
|
|
yyin = fopen (filename, "rt");
|
|
#endif
|
|
s_file = ReuseString (filename);
|
|
pr_source_line = 1;
|
|
clear_frame_macros ();
|
|
error = yyparse () || pr_error_count;
|
|
fclose (yyin);
|
|
#ifdef USE_CPP
|
|
if (!no_cpp) {
|
|
if (unlink (tempname)) {
|
|
perror ("unlink");
|
|
exit (1);
|
|
}
|
|
}
|
|
#endif
|
|
if (error)
|
|
return 1;
|
|
#else
|
|
char *src2;
|
|
sprintf (filename, "%s%c%s", sourcedir, PATH_SEPARATOR, com_token);
|
|
if (!options.quiet)
|
|
printf ("compiling %s\n", filename);
|
|
LoadFile (filename, (void *) &src2);
|
|
if (!PR_CompileFile (src2, filename))
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
if (!PR_FinishCompilation ())
|
|
Error ("compilation errors");
|
|
|
|
// write progdefs.h
|
|
crc = PR_WriteProgdefs ("progdefs.h");
|
|
|
|
// write data file
|
|
WriteData (crc);
|
|
|
|
// write files.dat
|
|
WriteFiles ();
|
|
|
|
stop = Sys_DoubleTime ();
|
|
if (options.quiet < 2)
|
|
printf ("Compilation time: %.3f seconds.\n", (stop - start));
|
|
return 0;
|
|
}
|