mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-24 05:11:42 +00:00
7c16afe7e9
qfpreqcc progs
1137 lines
24 KiB
C
1137 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
|
|
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) {
|
|
printf ("function %s was not defined\n", d->name);
|
|
errors = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
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\
|
|
");
|
|
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 (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
|
|
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 ();
|
|
if (yyparse () || pr_error_count)
|
|
return 1;
|
|
fclose (yyin);
|
|
#ifdef USE_CPP
|
|
if (!no_cpp) {
|
|
if (unlink (tempname)) {
|
|
perror ("unlink");
|
|
exit (1);
|
|
}
|
|
}
|
|
#endif
|
|
#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;
|
|
}
|