#define	LIBQBUILD_CORE
#include "../include/libqbuild.h"
#include "qcc.h"

/*=========================================================================== */

char sourcedir[NAMELEN_PATH];					/* 256 */
char destfile[NAMELEN_PATH];					/* 256 */

float *pr_globals = 0;						/*[MAX_REGS];           // 65536 */
int numpr_globals;						/* 4 */

char *strings = 0;						/*[MAX_STRINGS];        // 500000 */
int strofs;							/* 4 */

dstatement_t *statements = 0;					/*[MAX_STATEMENTS];     // 65536 * 8 = 524288 */
int numstatements;						/* 4 */
int *statement_linenums = 0;					/*[MAX_STATEMENTS];     // 65536 * 4 = 262144 */

dfunction_t *functions = 0;					/*[MAX_FUNCTIONS];      // 8192 * (28 + 8) = 294912 */
int numfunctions;						/* 4 */

ddef_t *globals = 0;						/*[MAX_GLOBALS];        // 16384 * 10 = 163840 */
int numglobaldefs;						/* 4 */

ddef_t *fields = 0;						/*[MAX_FIELDS];         // 1024 * 10 = 10240 */
int numfielddefs;						/* 4 */

char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];		/* 1024 * 64 = 65536 */
int precache_sounds_block[MAX_SOUNDS];				/* 1024 * 4 = 4096 */
int numsounds;							/* 4 */

char precache_models[MAX_MODELS][MAX_DATA_PATH];		/* 1024 * 64 = 65536 */
int precache_models_block[MAX_SOUNDS];				/* 1024 * 4 = 4096 */
int nummodels;							/* 4 */

char precache_files[MAX_FILES][MAX_DATA_PATH];			/* 1024 * 64 = 65536 */
int precache_files_block[MAX_SOUNDS];				/* 1024 * 4 = 4096 */
int numfiles;							/* 4 */

/*=========================================================================== */

int pr_source_line;

char *pr_file_p;

/* start of current source line */
char *pr_line_start;

int pr_bracelevel;

char pr_token[2048];
token_type_t pr_token_type;
type_t *pr_immediate_type;
eval_t pr_immediate;

char pr_immediate_string[2048];

int pr_error_count;

char *pr_punctuation[] =
/* longer symbols must be before a shorter partial match */
{"&&", "||", "<=", ">=", "==", "!=", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", ".", "<", ">", "#", "&", "|", NULL};

extern def_t def_void;
extern def_t def_string;
extern def_t def_float;
extern def_t def_vector;
extern def_t def_entity;
extern def_t def_field;
extern def_t def_function;
extern def_t def_pointer;

/* simple types.  function types are dynamically allocated */
type_t type_void =
{ev_void, &def_void};
type_t type_string =
{ev_string, &def_string};
type_t type_float =
{ev_float, &def_float};
type_t type_vector =
{ev_vector, &def_vector};
type_t type_entity =
{ev_entity, &def_entity};
type_t type_field =
{ev_field, &def_field};
type_t type_function =
{ev_function, &def_function, NULL, &type_void};

/* type_function is a void() function used for state defs */
type_t type_pointer =
{ev_pointer, &def_pointer};

type_t type_floatfield =
{ev_field, &def_field, NULL, &type_float};

int type_size[8] =
{1, 1, 1, 3, 1, 1, 1, 1};

def_t def_void =
{&type_void, "temp"};
def_t def_string =
{&type_string, "temp"};
def_t def_float =
{&type_float, "temp"};
def_t def_vector =
{&type_vector, "temp"};
def_t def_entity =
{&type_entity, "temp"};
def_t def_field =
{&type_field, "temp"};
def_t def_function =
{&type_function, "temp"};
def_t def_pointer =
{&type_pointer, "temp"};

def_t def_ret, def_parms[MAX_PARMS];

def_t *def_for_type[8] =
{&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer};

/*=========================================================================== */

pr_info_t pr;
def_t *pr_global_defs[MAX_REGS];

					       /* to find def for a global variable */

int pr_edict_size;

char pr_parm_names[MAX_PARMS][MAX_NAME];

/*======================================== */

/* the function being parsed, or NULL */
def_t *pr_scope;

bool pr_dumpasm;

/* filename for function definition */
string_t s_file;

/* for tracking local variables vs temps */
int locals_end;

/* longjump with this on parse error */
jmp_buf pr_parse_abort;

char pr_framemacros[MAX_FRAMES][16];
int pr_nummacros;

/*======================================== */

opcode_t pr_opcodes[] =
{
  {"<DONE>", "DONE", -1, FALSE, &def_entity, &def_field, &def_void},

  {"*", "MUL_F", 2, FALSE, &def_float, &def_float, &def_float},
  {"*", "MUL_V", 2, FALSE, &def_vector, &def_vector, &def_float},
  {"*", "MUL_FV", 2, FALSE, &def_float, &def_vector, &def_vector},
  {"*", "MUL_VF", 2, FALSE, &def_vector, &def_float, &def_vector},

  {"/", "DIV", 2, FALSE, &def_float, &def_float, &def_float},

  {"+", "ADD_F", 3, FALSE, &def_float, &def_float, &def_float},
  {"+", "ADD_V", 3, FALSE, &def_vector, &def_vector, &def_vector},

  {"-", "SUB_F", 3, FALSE, &def_float, &def_float, &def_float},
  {"-", "SUB_V", 3, FALSE, &def_vector, &def_vector, &def_vector},

  {"==", "EQ_F", 4, FALSE, &def_float, &def_float, &def_float},
  {"==", "EQ_V", 4, FALSE, &def_vector, &def_vector, &def_float},
  {"==", "EQ_S", 4, FALSE, &def_string, &def_string, &def_float},
  {"==", "EQ_E", 4, FALSE, &def_entity, &def_entity, &def_float},
  {"==", "EQ_FNC", 4, FALSE, &def_function, &def_function, &def_float},

  {"!=", "NE_F", 4, FALSE, &def_float, &def_float, &def_float},
  {"!=", "NE_V", 4, FALSE, &def_vector, &def_vector, &def_float},
  {"!=", "NE_S", 4, FALSE, &def_string, &def_string, &def_float},
  {"!=", "NE_E", 4, FALSE, &def_entity, &def_entity, &def_float},
  {"!=", "NE_FNC", 4, FALSE, &def_function, &def_function, &def_float},

  {"<=", "LE", 4, FALSE, &def_float, &def_float, &def_float},
  {">=", "GE", 4, FALSE, &def_float, &def_float, &def_float},
  {"<", "LT", 4, FALSE, &def_float, &def_float, &def_float},
  {">", "GT", 4, FALSE, &def_float, &def_float, &def_float},

  {".", "INDIRECT", 1, FALSE, &def_entity, &def_field, &def_float},
  {".", "INDIRECT", 1, FALSE, &def_entity, &def_field, &def_vector},
  {".", "INDIRECT", 1, FALSE, &def_entity, &def_field, &def_string},
  {".", "INDIRECT", 1, FALSE, &def_entity, &def_field, &def_entity},
  {".", "INDIRECT", 1, FALSE, &def_entity, &def_field, &def_field},
  {".", "INDIRECT", 1, FALSE, &def_entity, &def_field, &def_function},

  {".", "ADDRESS", 1, FALSE, &def_entity, &def_field, &def_pointer},

  {"=", "STORE_F", 5, TRUE, &def_float, &def_float, &def_float},
  {"=", "STORE_V", 5, TRUE, &def_vector, &def_vector, &def_vector},
  {"=", "STORE_S", 5, TRUE, &def_string, &def_string, &def_string},
  {"=", "STORE_ENT", 5, TRUE, &def_entity, &def_entity, &def_entity},
  {"=", "STORE_FLD", 5, TRUE, &def_field, &def_field, &def_field},
  {"=", "STORE_FNC", 5, TRUE, &def_function, &def_function, &def_function},

  {"=", "STOREP_F", 5, TRUE, &def_pointer, &def_float, &def_float},
  {"=", "STOREP_V", 5, TRUE, &def_pointer, &def_vector, &def_vector},
  {"=", "STOREP_S", 5, TRUE, &def_pointer, &def_string, &def_string},
  {"=", "STOREP_ENT", 5, TRUE, &def_pointer, &def_entity, &def_entity},
  {"=", "STOREP_FLD", 5, TRUE, &def_pointer, &def_field, &def_field},
  {"=", "STOREP_FNC", 5, TRUE, &def_pointer, &def_function, &def_function},

  {"<RETURN>", "RETURN", -1, FALSE, &def_void, &def_void, &def_void},

  {"!", "NOT_F", -1, FALSE, &def_float, &def_void, &def_float},
  {"!", "NOT_V", -1, FALSE, &def_vector, &def_void, &def_float},
  {"!", "NOT_S", -1, FALSE, &def_vector, &def_void, &def_float},
  {"!", "NOT_ENT", -1, FALSE, &def_entity, &def_void, &def_float},
  {"!", "NOT_FNC", -1, FALSE, &def_function, &def_void, &def_float},

  {"<IF>", "IF", -1, FALSE, &def_float, &def_float, &def_void},
  {"<IFNOT>", "IFNOT", -1, FALSE, &def_float, &def_float, &def_void},

  /* calls returns REG_RETURN */
  {"<CALL0>", "CALL0", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL1>", "CALL1", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL2>", "CALL2", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL3>", "CALL3", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL4>", "CALL4", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL5>", "CALL5", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL6>", "CALL6", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL7>", "CALL7", -1, FALSE, &def_function, &def_void, &def_void},
  {"<CALL8>", "CALL8", -1, FALSE, &def_function, &def_void, &def_void},

  {"<STATE>", "STATE", -1, FALSE, &def_float, &def_float, &def_void},

  {"<GOTO>", "GOTO", -1, FALSE, &def_float, &def_void, &def_void},

  {"&&", "AND", 6, FALSE, &def_float, &def_float, &def_float},
  {"||", "OR", 6, FALSE, &def_float, &def_float, &def_float},

  {"&", "BITAND", 2, FALSE, &def_float, &def_float, &def_float},
  {"|", "BITOR", 2, FALSE, &def_float, &def_float, &def_float},

  {NULL}
};

def_t *PR_Expression(register int priority);

def_t junkdef;

/*
 * ===========================================================================
 * qcc
 * ===========================================================================
 */

/* CopyString returns an offset from the string heap */
int CopyString(register char *str)
{
  int old;

  old = strofs;
  __strcpy(strings + strofs, str);
  strofs += __strlen(str) + 1;
  return old;
}

void PrintStrings(void)
{
  int i, l, j;

  for (i = 0; i < strofs; i += l) {
    l = __strlen(strings + i) + 1;
    mprintf("%5i : ", i);
    for (j = 0; j < l; j++) {
      if (strings[i + j] == '\n') {
	putchar('\\');
	putchar('n');
      }
      else
	putchar(strings[i + j]);
    }
    mprintf("\n");
  }
}

void PrintFunctions(void)
{
  int i, j;
  dfunction_t *d;

  for (i = 0; i < numfunctions; i++) {
    d = &functions[i];
    mprintf("%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++)
      mprintf("%i ", d->parm_size[j]);
    mprintf(")\n");
  }
}

void PrintFields(void)
{
  int i;
  ddef_t *d;

  for (i = 0; i < numfielddefs; i++) {
    d = &fields[i];
    mprintf("%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];
    mprintf("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
  }
}

/*=========================================================================== */

void PrecacheSound(register def_t * e, register int ch)
{
  char *n;
  int i;

  if (!e->ofs)
    return;
  n = G_STRING(e->ofs);
  for (i = 0; i < numsounds; i++)
    if (!__strcmp(n, precache_sounds[i]))
      return;
  if (numsounds == MAX_SOUNDS)
    eprintf("PrecacheSound: numsounds == MAX_SOUNDS");
  __strcpy(precache_sounds[i], n);
  if (ch >= '1' && ch <= '9')
    precache_sounds_block[i] = ch - '0';
  else
    precache_sounds_block[i] = 1;
  numsounds++;
}

void PrecacheModel(register def_t * e, register int ch)
{
  char *n;
  int i;

  if (!e->ofs)
    return;
  n = G_STRING(e->ofs);
  for (i = 0; i < nummodels; i++)
    if (!__strcmp(n, precache_models[i]))
      return;
  if (numsounds == MAX_SOUNDS)
    eprintf("PrecacheModels: numsounds == MAX_SOUNDS");
  __strcpy(precache_models[i], n);
  if (ch >= '1' && ch <= '9')
    precache_models_block[i] = ch - '0';
  else
    precache_models_block[i] = 1;
  nummodels++;
}

void PrecacheFile(register def_t * e, register int ch)
{
  char *n;
  int i;

  if (!e->ofs)
    return;
  n = G_STRING(e->ofs);
  for (i = 0; i < numfiles; i++)
    if (!__strcmp(n, precache_files[i]))
      return;
  if (numfiles == MAX_FILES)
    eprintf("PrecacheFile: numfiles == MAX_FILES");
  __strcpy(precache_files[i], n);
  if (ch >= '1' && ch <= '9')
    precache_files_block[i] = ch - '0';
  else
    precache_files_block[i] = 1;
  numfiles++;
}

/*=========================================================================== */

bool ExitData(void)
{
  kfree();

  return FALSE;
}

bool InitData(void)
{
  int i;

  if (!(pr_globals = (float *)kmalloc(MAX_REGS * sizeof(float))))
      return ExitData();
  if (!(strings = (char *)kmalloc(MAX_STRINGS * sizeof(char))))
      return ExitData();

  if (!(statements = (dstatement_t *) kmalloc(MAX_STATEMENTS * sizeof(dstatement_t))))
    return ExitData();
  if (!(statement_linenums = (int *)kmalloc(MAX_STATEMENTS * sizeof(int))))
      return ExitData();

  if (!(functions = (dfunction_t *) kmalloc(MAX_FUNCTIONS * sizeof(dfunction_t))))
    return ExitData();
  if (!(globals = (ddef_t *) kmalloc(MAX_GLOBALS * sizeof(ddef_t))))
    return ExitData();
  if (!(fields = (ddef_t *) kmalloc(MAX_FIELDS * sizeof(ddef_t))))
    return ExitData();

  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;

  return TRUE;
}

void WriteData(register int crc)
{
  def_t *def;
  ddef_t *dd;
  dprograms_t progs;
  HANDLE h;
  int i;

  for (def = pr.def_head; def; def = def->next) {
    if (def->type->type == ev_function) {
      /*
       * 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 = CopyString(def->name);
      dd->ofs = G_INT(def->ofs);
    }
    dd = &globals[numglobaldefs];
    numglobaldefs++;
    dd->type = def->type->type;
    if (!def->initialized
	&& def->type->type != ev_function
	&& def->type->type != ev_field
	&& def->scope == NULL)
      dd->type |= DEF_SAVEGLOBGAL;
    dd->s_name = CopyString(def->name);
    dd->ofs = def->ofs;
  }

  /*
   * PrintStrings ();
   * PrintFunctions ();
   * PrintFields ();
   * PrintGlobals ();
   */
  strofs = (strofs + 3) & ~3;

  mprintf("%6i strofs\n", strofs);
  mprintf("%6i numstatements\n", numstatements);
  mprintf("%6i numfunctions\n", numfunctions);
  mprintf("%6i numglobaldefs\n", numglobaldefs);
  mprintf("%6i numfielddefs\n", numfielddefs);
  mprintf("%6i numpr_globals\n", numpr_globals);

  if ((h = __open(destfile, H_READWRITE_BINARY_OLD)) > 0) {
    __write(h, &progs, sizeof(progs));

    progs.ofs_strings = __ltell(h);
    progs.numstrings = strofs;
    __write(h, strings, strofs);

    progs.ofs_statements = __ltell(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);
    }
    __write(h, statements, numstatements * sizeof(dstatement_t));

    progs.ofs_functions = __ltell(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);
    }
    __write(h, functions, numfunctions * sizeof(dfunction_t));

    progs.ofs_globaldefs = __ltell(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);
    }
    __write(h, globals, numglobaldefs * sizeof(ddef_t));

    progs.ofs_fielddefs = __ltell(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);
    }
    __write(h, fields, numfielddefs * sizeof(ddef_t));

    progs.ofs_globals = __ltell(h);
    progs.numglobals = numpr_globals;
    for (i = 0; i < numpr_globals; i++)
      ((int *)pr_globals)[i] = LittleLong(((int *)pr_globals)[i]);
    __write(h, pr_globals, numpr_globals * 4);

    mprintf("%6i TOTAL SIZE\n", (int)__ltell(h));

    progs.entityfields = pr.size_fields;
    progs.version = PROG_VERSION;
    progs.crc = crc;

    /* unsigned char swap the header and write it out */
    for (i = 0; i < sizeof(progs) / 4; i++)
      ((int *)&progs)[i] = LittleLong(((int *)&progs)[i]);
    __lseek(h, 0, SEEK_SET);
    __write(h, &progs, sizeof(progs));

    __close(h);
  }
}

bool ReadData(register HANDLE srcFile)
{
  dprograms_t progs;
  int i;
  bool retval = FALSE;

  while (1) {
    if (__read(srcFile, &progs, sizeof(progs)) != sizeof(progs))
      break;

    __lseek(srcFile, LittleLong(progs.ofs_strings), SEEK_SET);
    strofs = LittleLong(progs.numstrings);
    if (!(strings = (char *)kmalloc(strofs)))
      break;
    if (__read(srcFile, strings, strofs) != strofs)
      break;

    __lseek(srcFile, LittleLong(progs.ofs_statements), SEEK_SET);
    numstatements = LittleLong(progs.numstatements);
    if (!(statements = (dstatement_t *) kmalloc(numstatements * sizeof(dstatement_t))))
      break;
    if (!(statement_linenums = (int *)kmalloc(numstatements * sizeof(int))))
        break;

    if (__read(srcFile, statements, numstatements * sizeof(dstatement_t)) != (numstatements * sizeof(dstatement_t)))
      break;
    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);
    }

    __lseek(srcFile, LittleLong(progs.ofs_functions), SEEK_SET);
    numfunctions = LittleLong(progs.numfunctions);
    if (!(functions = (dfunction_t *) kmalloc(numfunctions * sizeof(dfunction_t))))
      break;
    if (__read(srcFile, functions, numfunctions * sizeof(dfunction_t)) != (numfunctions * sizeof(dfunction_t)))
      break;
    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].locals = LittleLong(functions[i].locals);
      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);
    }

    __lseek(srcFile, LittleLong(progs.ofs_globaldefs), SEEK_SET);
    numglobaldefs = LittleLong(progs.numglobaldefs);
    if (!(globals = (ddef_t *) kmalloc(numglobaldefs * sizeof(ddef_t))))
      break;
    if (__read(srcFile, globals, numglobaldefs * sizeof(ddef_t)) != (numglobaldefs * sizeof(ddef_t)))
      break;
    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);
    }

    __lseek(srcFile, LittleLong(progs.ofs_fielddefs), SEEK_SET);
    numfielddefs = LittleLong(progs.numfielddefs);
    if (!(fields = (ddef_t *) kmalloc(numfielddefs * sizeof(ddef_t))))
      break;
    if (__read(srcFile, fields, numfielddefs * sizeof(ddef_t)) != (numfielddefs * sizeof(ddef_t)))
      break;
    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);
    }

    __lseek(srcFile, LittleLong(progs.ofs_globals), SEEK_SET);
    numpr_globals = LittleLong(progs.numglobals);
    if (!(pr_globals = (float *)kmalloc(numpr_globals * sizeof(float))))
        break;

    if (__read(srcFile, pr_globals, numpr_globals * 4) != (numpr_globals * 4))
      break;
    for (i = 0; i < numpr_globals; i++)
      ((int *)pr_globals)[i] = LittleLong(((int *)pr_globals)[i]);

    printf("total size is %6i\n", (int)__ltell(srcFile));
    printf("version code is %i\n", LittleLong(progs.version));
    printf("crc is %i\n", LittleLong(progs.crc));
    printf("%6i strofs\n", strofs);
    printf("%6i numstatements\n", numstatements);
    printf("%6i numfunctions\n", numfunctions);
    printf("%6i numglobaldefs\n", numglobaldefs);
    printf("%6i numfielddefs\n", numfielddefs);
    printf("%6i numpr_globals\n", numpr_globals);
    printf("--------------------------\n");
    retval = TRUE;
    break;
  }

  return retval;
}

bool ShowData(register HANDLE srcFile)
{
  dprograms_t progs;

  if (__read(srcFile, &progs, sizeof(progs)) == sizeof(progs)) {
    strofs = LittleLong(progs.numstrings);
    numstatements = LittleLong(progs.numstatements);
    numfunctions = LittleLong(progs.numfunctions);
    numglobaldefs = LittleLong(progs.numglobaldefs);
    numfielddefs = LittleLong(progs.numfielddefs);
    numpr_globals = LittleLong(progs.numglobals);

    __lseek(srcFile, 0, SEEK_END);
    printf("total size is %6i\n", (int)__ltell(srcFile));
    printf("version code is %i\n", LittleLong(progs.version));
    printf("crc is %i\n", LittleLong(progs.crc));
    printf("%6i strofs\n", strofs);
    printf("%6i numstatements\n", numstatements);
    printf("%6i numfunctions\n", numfunctions);
    printf("%6i numglobaldefs\n", numglobaldefs);
    printf("%6i numfielddefs\n", numfielddefs);
    printf("%6i numpr_globals\n", numpr_globals);
    printf("--------------------------\n");
    return TRUE;
  }
  else
    return FALSE;
}

/*
 * 
 * ============
 * WriteFiles
 * 
 * Generates files.dat, which contains all of the
 * data files actually used by the game, to be
 * processed by qfiles.exe
 * ============
 */
void WriteFiles(void)
{
  FILE *f;
  int i;
  char filename[NAMELEN_PATH];

  sprintf(filename, "%sfiles.dat", sourcedir);
  f = __fopen(filename, "w");
  if (!f)
    eprintf("Couldn't open %s", filename);
  else {
    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);
  }
}

/*=========================================================================== */

/*
 * ============
 * PR_ParseError
 * 
 * Aborts the current file load
 * ============
 */
void PR_ParseError(register char *error,...)
{
  va_list argptr;
  char string[1024];

  va_start(argptr, error);
  vsprintf(string, error, argptr);
  va_end(argptr);

  mprintf("%s:%i:%s\n", strings + s_file, pr_source_line, string);

  longjmp(pr_parse_abort, 1);
}

/*
 * ==============
 * PR_PrintNextLine
 * ==============
 */
void PR_PrintNextLine(void)
{
  char *t;

  mprintf("%3i:", pr_source_line);
  for (t = pr_line_start; *t && *t != '\n'; t++)
    mprintf("%c", *t);
  mprintf("\n");
}

/*
 * ==============
 * PR_NewLine
 * 
 * Call at start of file and when *pr_file_p == '\n'
 * ==============
 */
void PR_NewLine(void)
{
  bool m;

  if (*pr_file_p == '\n') {
    pr_file_p++;
    m = TRUE;
  }
  else
    m = FALSE;

  pr_source_line++;
  pr_line_start = pr_file_p;

  /*
   * if (pr_dumpasm)
   * PR_PrintNextLine ();
   */
  if (m)
    pr_file_p--;
}

/*
 * ==============
 * PR_LexString
 * 
 * Parses a quoted string
 * ==============
 */
void PR_LexString(void)
{
  int c;
  int len;

  len = 0;
  pr_file_p++;
  do {
    c = *pr_file_p++;
    if (!c)
      PR_ParseError("EOF inside quote");
    if (c == '\n')
      PR_ParseError("newline inside quote");
    if (c == '\\') {						/* escape char */
      c = *pr_file_p++;
      if (!c)
	PR_ParseError("EOF inside quote");
      if (c == 'n')
	c = '\n';
      else if (c == '"')
	c = '"';
      else
	PR_ParseError("Unknown escape char");
    }
    else if (c == '\"') {
      pr_token[len] = 0;
      pr_token_type = tt_immediate;
      pr_immediate_type = &type_string;
      __strcpy(pr_immediate_string, pr_token);
      return;
    }
    pr_token[len] = c;
    len++;
  } while (1);
}

/*
 * ==============
 * PR_LexNumber
 * ==============
 */
float PR_LexNumber(void)
{
  int c;
  int len;

  len = 0;
  c = *pr_file_p;
  do {
    pr_token[len] = c;
    len++;
    pr_file_p++;
    c = *pr_file_p;
  } while ((c >= '0' && c <= '9') || c == '.');
  pr_token[len] = 0;
  return atof(pr_token);
}

/*
 * ==============
 * PR_LexWhitespace
 * ==============
 */
void PR_LexWhitespace(void)
{
  int c;

  while (1) {
    /* skip whitespace */
    while ((c = *pr_file_p) <= ' ') {
      if (c == '\n')
	PR_NewLine();
      if (c == 0)
	return;							/* end of file */

      pr_file_p++;
    }

    /* skip // comments */
    if (c == '/' && pr_file_p[1] == '/') {
      while (*pr_file_p && *pr_file_p != '\n')
	pr_file_p++;
      PR_NewLine();
      pr_file_p++;
      continue;
    }

    /* skip * * comments */
    if (c == '/' && pr_file_p[1] == '*') {
      do {
	pr_file_p++;
	if (pr_file_p[0] == '\n')
	  PR_NewLine();
	if (pr_file_p[1] == 0)
	  return;
      } while (pr_file_p[-1] != '*' || pr_file_p[0] != '/');
      pr_file_p++;
      continue;
    }
    break;							/* a real character has been found */

  }
}

/*
 * ==============
 * PR_LexVector
 * 
 * Parses a single quoted vector
 * ==============
 */
void PR_LexVector(void)
{
  int i;

  pr_file_p++;
  pr_token_type = tt_immediate;
  pr_immediate_type = &type_vector;
  for (i = 0; i < 3; i++) {
    pr_immediate.vector[i] = PR_LexNumber();
    PR_LexWhitespace();
  }
  if (*pr_file_p != '\'')
    PR_ParseError("Bad vector");
  pr_file_p++;
}

/*
 * ==============
 * PR_LexName
 * 
 * Parses an identifier
 * ==============
 */
void PR_LexName(void)
{
  int c;
  int len;

  len = 0;
  c = *pr_file_p;
  do {
    pr_token[len] = c;
    len++;
    pr_file_p++;
    c = *pr_file_p;
  } while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9'));
  pr_token[len] = 0;
  pr_token_type = tt_name;
}

/*
 * ==============
 * PR_LexPunctuation
 * ==============
 */
void PR_LexPunctuation(void)
{
  int i;
  int len;
  char *p;

  pr_token_type = tt_punct;

  for (i = 0; (p = pr_punctuation[i]) != NULL; i++) {
    len = __strlen(p);
    if (!__strncmp(p, pr_file_p, len)) {
      __strcpy(pr_token, p);
      if (p[0] == '{')
	pr_bracelevel++;
      else if (p[0] == '}')
	pr_bracelevel--;
      pr_file_p += len;
      return;
    }
  }

  PR_ParseError("Unknown punctuation");
}

void PR_ClearGrabMacros(void)
{
  pr_nummacros = 0;
}

void PR_FindMacro(void)
{
  int i;

  for (i = 0; i < pr_nummacros; i++)
    if (!__strcmp(pr_token, pr_framemacros[i])) {
      sprintf(pr_token, "%d", i);
      pr_token_type = tt_immediate;
      pr_immediate_type = &type_float;
      pr_immediate._float = i;
      return;
    }
  PR_ParseError("Unknown frame macro $%s", pr_token);
}

/* just parses text, returning FALSE if an eol is reached */
bool PR_SimpleGetToken(void)
{
  int c;
  int i;

  /* skip whitespace */
  while ((c = *pr_file_p) <= ' ') {
    if (c == '\n' || c == 0)
      return FALSE;
    pr_file_p++;
  }

  i = 0;
  while ((c = *pr_file_p) > ' ' && c != ',' && c != ';') {
    pr_token[i] = c;
    i++;
    pr_file_p++;
  }
  pr_token[i] = 0;
  return TRUE;
}

void PR_ParseFrame(void)
{
  while (PR_SimpleGetToken()) {
    __strcpy(pr_framemacros[pr_nummacros], pr_token);
    pr_nummacros++;
  }
}

/*
 * ==============
 * PR_Lex
 * 
 * Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type
 * ==============
 */
void PR_LexGrab(void);
void PR_Lex(void)
{
  int c;

  pr_token[0] = 0;

  if (!pr_file_p) {
    pr_token_type = tt_eof;
    return;
  }

  PR_LexWhitespace();

  c = *pr_file_p;

  if (!c) {
    pr_token_type = tt_eof;
    return;
  }

  /* handle quoted strings as a unit */
  if (c == '\"') {
    PR_LexString();
    return;
  }

  /* handle quoted vectors as a unit */
  if (c == '\'') {
    PR_LexVector();
    return;
  }

  /* if the first character is a valid identifier, parse until a non-id */
  /* character is reached */
  if ((c >= '0' && c <= '9') || (c == '-' && pr_file_p[1] >= '0' && pr_file_p[1] <= '9')) {
    pr_token_type = tt_immediate;
    pr_immediate_type = &type_float;
    pr_immediate._float = PR_LexNumber();
    return;
  }

  if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') {
    PR_LexName();
    return;
  }

  if (c == '$') {
    PR_LexGrab();
    return;
  }

  /* parse symbol strings until a non-symbol is found */
  PR_LexPunctuation();
}

/*
 * ==============
 * PR_LexGrab
 * 
 * Deals with counting sequence numbers and replacing frame macros
 * ==============
 */
void PR_LexGrab(void)
{
  pr_file_p++;							/* skip the $ */

  if (!PR_SimpleGetToken())
    PR_ParseError("hanging $");

  /* check for $frame */
  if (!__strcmp(pr_token, "frame")) {
    PR_ParseFrame();
    PR_Lex();
  }
  /* ignore other known $commands */
  else if (!__strcmp(pr_token, "cd")
	   || !__strcmp(pr_token, "origin")
	   || !__strcmp(pr_token, "base")
	   || !__strcmp(pr_token, "flags")
	   || !__strcmp(pr_token, "scale")
	   || !__strcmp(pr_token, "skin")) {			/* skip to end of line */

    while (PR_SimpleGetToken());
    PR_Lex();
  }
  /* look for a frame name macro */
  else
    PR_FindMacro();
}

/*
 * =============
 * PR_Expect
 * 
 * Issues an error if the current token isn't equal to string
 * Gets the next token
 * =============
 */
void PR_Expect(register char *string)
{
  if (__strcmp(string, pr_token))
    PR_ParseError("expected %s, found %s", string, pr_token);
  PR_Lex();
}

/*
 * =============
 * PR_Check
 * 
 * Returns TRUE and gets the next token if the current token equals string
 * Returns FALSE and does nothing otherwise
 * =============
 */
bool PR_Check(register char *string)
{
  if (__strcmp(string, pr_token))
    return FALSE;

  PR_Lex();
  return TRUE;
}

/*
 * ============
 * PR_ParseName
 * 
 * Checks to see if the current token is a valid name
 * ============
 */
char *PR_ParseName(void)
{
  static char ident[MAX_NAME];

  if (pr_token_type != tt_name)
    PR_ParseError("not a name");
  if (__strlen(pr_token) >= MAX_NAME - 1)
    PR_ParseError("name too long");
  __strcpy(ident, pr_token);
  PR_Lex();

  return ident;
}

/*
 * ============
 * PR_FindType
 * 
 * Returns a preexisting complex type that matches the parm, or allocates
 * a new one and copies it out.
 * ============
 */
type_t *PR_FindType(register type_t * type)
{
  def_t *def;
  type_t *check;
  int i;

  for (check = pr.types; check; check = check->next) {
    if (check->type != type->type
	|| check->aux_type != type->aux_type
	|| check->num_parms != type->num_parms)
      continue;

    for (i = 0; i < type->num_parms; i++)
      if (check->parm_types[i] != type->parm_types[i])
	break;

    if (i == type->num_parms)
      return check;
  }

  /* allocate a new one */
  check = (type_t *) kmalloc(sizeof(*check));
  *check = *type;
  check->next = pr.types;
  pr.types = check;

  /* allocate a generic def for the type, so fields can reference it */
  def = (def_t *) kmalloc(sizeof(def_t));
  def->name = "COMPLEX TYPE";
  def->type = check;
  check->def = def;
  return check;
}

/*
 * ============
 * PR_SkipToSemicolon
 * 
 * For error recovery, also pops out of nested braces
 * ============
 */
void PR_SkipToSemicolon(void)
{
  do {
    if (!pr_bracelevel && PR_Check(";"))
      return;
    PR_Lex();
  } while (pr_token[0]);					/* eof will return a null token */

}

/*
 * ============
 * PR_ParseType
 * 
 * Parses a variable type, including field and functions types
 * ============
 */
type_t *PR_ParseType(void)
{
  type_t new;
  type_t *type;
  char *name;

  if (PR_Check(".")) {
    __bzero(&new, sizeof(new));
    new.type = ev_field;
    new.aux_type = PR_ParseType();
    return PR_FindType(&new);
  }

  if (!__strcmp(pr_token, "float"))
    type = &type_float;
  else if (!__strcmp(pr_token, "vector"))
    type = &type_vector;
  else if (!__strcmp(pr_token, "float"))
    type = &type_float;
  else if (!__strcmp(pr_token, "entity"))
    type = &type_entity;
  else if (!__strcmp(pr_token, "string"))
    type = &type_string;
  else if (!__strcmp(pr_token, "void"))
    type = &type_void;
  else {
    PR_ParseError("\"%s\" is not a type", pr_token);
    type = &type_float;						/* shut up compiler warning */
  }
  PR_Lex();

  if (!PR_Check("("))
    return type;

  /* function type */
  __bzero(&new, sizeof(new));
  new.type = ev_function;
  new.aux_type = type;						/* return type */

  new.num_parms = 0;
  if (!PR_Check(")")) {
    if (PR_Check("..."))
      new.num_parms = -1;					/* variable args */
    else
      do {
	type = PR_ParseType();
	name = PR_ParseName();
	__strcpy(pr_parm_names[new.num_parms], name);
	new.parm_types[new.num_parms] = type;
	new.num_parms++;
      } while (PR_Check(","));

    PR_Expect(")");
  }

  return PR_FindType(&new);
}
/*
 * ============
 * PR_Statement
 * 
 * Emits a primitive statement, returning the var it places it's value in
 * ============
 */
def_t *PR_Statement(register opcode_t * op, register def_t * var_a, register def_t * var_b)
{
  dstatement_t *statement;
  def_t *var_c;

  statement = &statements[numstatements];
  numstatements++;

  statement_linenums[statement - statements] = pr_source_line;
  statement->op = op - pr_opcodes;
  statement->a = var_a ? var_a->ofs : 0;
  statement->b = var_b ? var_b->ofs : 0;
  if (op->type_c == &def_void || op->right_associative) {
    var_c = NULL;
    statement->c = 0;						/* ifs, gotos, and assignments */
    /* don't need vars allocated */

  }
  else {							/* allocate result space */

    var_c = (def_t *) kmalloc(sizeof(def_t));
/*__bzero(var_c, sizeof(def_t)); */
    var_c->ofs = numpr_globals;
    var_c->type = op->type_c->type;

    statement->c = numpr_globals;
    numpr_globals += type_size[op->type_c->type->type];
  }

  if (op->right_associative)
    return var_a;
  return var_c;
}

/*
 * ============
 * PR_ParseImmediate
 * 
 * Looks for a preexisting constant
 * ============
 */
#if 0
def_t *PR_ParseImmediate(void)
{
  def_t *cn;

  /* check for a constant with the same value */
  for (cn = pr.def_head.next; cn; cn = cn->next) {
    if (!cn->initialized)
      continue;
    if (cn->type != pr_immediate_type)
      continue;
    if (pr_immediate_type == &type_string) {
      if (!__strcmp(G_STRING(cn->ofs), pr_immediate_string)) {
	PR_Lex();
	return cn;
      }
    }
    else if (pr_immediate_type == &type_float) {
      if (G_FLOAT(cn->ofs) == pr_immediate._float) {
	PR_Lex();
	return cn;
      }
    }
    else if (pr_immediate_type == &type_vector) {
      if ((G_FLOAT(cn->ofs) == pr_immediate.vector[0])
	  && (G_FLOAT(cn->ofs + 1) == pr_immediate.vector[1])
	  && (G_FLOAT(cn->ofs + 2) == pr_immediate.vector[2])) {
	PR_Lex();
	return cn;
      }
    }
    else
      PR_ParseError("weird immediate type");
  }

  /* allocate a new one */
  cn = (def_t *) kmalloc(sizeof(def_t));
  cn->next = NULL;

  pr.def_tail->next = cn;

  pr.def_tail = cn;

  cn->search_next = pr.search;

  pr.search = cn;

  cn->type = pr_immediate_type;
  cn->name = "IMMEDIATE";
  cn->initialized = 1;
  cn->scope = NULL;						/* always share immediates */

  /* copy the immediate to the global area */
  cn->ofs = numpr_globals;
  pr_global_defs[cn->ofs] = cn;
  numpr_globals += type_size[pr_immediate_type->type];
  if (pr_immediate_type == &type_string)
    pr_immediate.string = CopyString(pr_immediate_string);

  memcpy(pr_globals + cn->ofs, &pr_immediate, 4 * type_size[pr_immediate_type->type]);

  PR_Lex();

  return cn;
}
#else
def_t *PR_ParseImmediate(void)
{
#undef cacheSize
#undef cacheMask
#define cacheSize  32
#define cacheMask  (cacheSize - 1)
  static def_t *stringCache[cacheSize];
  static int stringTop = 0;
  static def_t *floatCache[cacheSize];
  static int floatTop = 0;
  static def_t *vectorCache[cacheSize];
  static int vectorTop = 0;
  int sub;
  def_t *cn;

  if (pr_immediate_type == &type_string) {
    /* See if constant is in cache. */
    sub = stringTop;
    do {
      cn = stringCache[sub];
      if (!cn)
	break;
      if (!__strcmp(G_STRING(cn->ofs), pr_immediate_string)) {
	PR_Lex();
	stringCache[sub] = stringCache[stringTop];
	stringCache[stringTop] = cn;
	return cn;
      }
      sub = (sub - 1) & cacheMask;
    }
    while (sub != stringTop);

    /* See if constant is in main list. */
    for (cn = pr.def_tail; cn; cn = cn->prev) {
      if (!cn->initialized || cn->type != pr_immediate_type)
	continue;
      if (!__strcmp(G_STRING(cn->ofs), pr_immediate_string)) {
	PR_Lex();
	stringTop = (stringTop + 1) & cacheMask;
	stringCache[stringTop] = cn;
	return cn;
      }
    }
  }
  else if (pr_immediate_type == &type_float) {
    /* See if constant is in cache. */
    sub = floatTop;
    do {
      cn = floatCache[sub];
      if (!cn)
	break;
      if (G_FLOAT(cn->ofs) == pr_immediate._float) {
	PR_Lex();
	floatCache[sub] = floatCache[floatTop];
	floatCache[floatTop] = cn;
	return cn;
      }
      sub = (sub - 1) & cacheMask;
    }
    while (sub != floatTop);

    /* See if constant is in main list. */
    for (cn = pr.def_tail; cn; cn = cn->prev) {
      if (!cn->initialized || cn->type != pr_immediate_type)
	continue;
      if (G_FLOAT(cn->ofs) == pr_immediate._float) {
	PR_Lex();
	floatTop = (floatTop + 1) & cacheMask;
	floatCache[floatTop] = cn;
	return cn;
      }
    }
  }
  else if (pr_immediate_type == &type_vector) {
    /* See if constant is in cache. */
    sub = vectorTop;
    do {
      cn = vectorCache[sub];
      if (!cn)
	break;
      if ((G_FLOAT(cn->ofs) == pr_immediate.vector[0])
	  && (G_FLOAT(cn->ofs + 1) == pr_immediate.vector[1])
	  && (G_FLOAT(cn->ofs + 2) == pr_immediate.vector[2])) {
	PR_Lex();
	vectorCache[sub] = vectorCache[vectorTop];
	vectorCache[vectorTop] = cn;
	return cn;
      }
      sub = (sub - 1) & cacheMask;
    }
    while (sub != vectorTop);

    /* See if constant is in main list. */
    for (cn = pr.def_tail; cn; cn = cn->prev) {
      if (!cn->initialized || cn->type != pr_immediate_type)
	continue;
      if ((G_FLOAT(cn->ofs) == pr_immediate.vector[0])
	  && (G_FLOAT(cn->ofs + 1) == pr_immediate.vector[1])
	  && (G_FLOAT(cn->ofs + 2) == pr_immediate.vector[2])) {
	PR_Lex();
	vectorTop = (vectorTop + 1) & cacheMask;
	vectorCache[vectorTop] = cn;
	return cn;
      }
    }
  }
  else {
    PR_ParseError("weird immediate type");
  }

  /* allocate a new one */
  cn = (def_t *) kmalloc(sizeof(def_t));
  cn->next = NULL;
  if (!pr.def_head) {
    cn->prev = NULL;
    pr.def_head = pr.def_tail = cn;
  }
  else {
    pr.def_tail->next = cn;
    cn->prev = pr.def_tail;
    pr.def_tail = cn;
  }
  cn->type = pr_immediate_type;
  cn->name = "IMMEDIATE";
  cn->initialized = 1;
  cn->scope = NULL;						/* always share immediates */

  /* Copy the immediate to the global area */
  cn->ofs = numpr_globals;
  pr_global_defs[cn->ofs] = cn;

  /* Put immediate into cache. */
  if (pr_immediate_type == &type_string) {
    stringTop = (stringTop + 1) & cacheMask;
    stringCache[stringTop] = cn;
  }
  else if (pr_immediate_type == &type_float) {
    floatTop = (floatTop + 1) & cacheMask;
    floatCache[floatTop] = cn;
  }
  else {
    vectorTop = (vectorTop + 1) & cacheMask;
    vectorCache[vectorTop] = cn;
  }

  numpr_globals += type_size[pr_immediate_type->type];
  if (pr_immediate_type == &type_string)
    pr_immediate.string = CopyString(pr_immediate_string);

  __memcpy(pr_globals + cn->ofs, &pr_immediate, 4 * type_size[pr_immediate_type->type]);

  PR_Lex();

  return cn;
}
#endif

/*
 * ============
 * PR_ParseFunctionCall
 * ============
 */
def_t *PR_ParseFunctionCall(register def_t * func)
{
  def_t *e;
  int arg;
  type_t *t;

  t = func->type;

  if (t->type != ev_function)
    PR_ParseError("not a function");

  /* copy the arguments to the global parameter variables */
  arg = 0;
  if (!PR_Check(")")) {
    do {
      if (t->num_parms != -1 && arg >= t->num_parms)
	PR_ParseError("too many parameters");
      e = PR_Expression(TOP_PRIORITY);

      if (arg == 0 && func->name) {
	/* save information for model and sound caching */
	if (!__strncmp(func->name, "precache_sound", 14))
	  PrecacheSound(e, func->name[14]);
	else if (!__strncmp(func->name, "precache_model", 14))
	  PrecacheModel(e, func->name[14]);
	else if (!__strncmp(func->name, "precache_file", 13))
	  PrecacheFile(e, func->name[13]);
      }

      if (t->num_parms != -1 && (e->type != t->parm_types[arg]))
	PR_ParseError("type mismatch on parm %i", arg);
      /* a vector copy will copy everything */
      def_parms[arg].type = t->parm_types[arg];
      PR_Statement(&pr_opcodes[OP_STORE_V], e, &def_parms[arg]);
      arg++;
    } while (PR_Check(","));

    if (t->num_parms != -1 && arg != t->num_parms)
      PR_ParseError("too few parameters");
    PR_Expect(")");
  }
  if (arg > 8)
    PR_ParseError("More than eight parameters");

  PR_Statement(&pr_opcodes[OP_CALL0 + arg], func, 0);

  def_ret.type = t->aux_type;
  return &def_ret;
}

/*
 * ============
 * PR_GetDef
 * 
 * If type is NULL, it will match any type
 * If allocate is TRUE, a new def will be allocated if it can't be found
 * ============
 */
#if 0
def_t *PR_GetDef(register type_t * type, register char *name, register def_t * scope, register bool allocate)
{
  def_t *def, **old;
  char element[MAX_NAME];

  /* see if the name is already in use */
  old = &pr.search;
  for (def = *old; def; old = &def->search_next, def = *old)
    if (!__strcmp(def->name, name)) {
      if (def->scope && def->scope != scope)
	continue;						/* in a different function */

      if (type && def->type != type)
	PR_ParseError("Type mismatch on redeclaration of %s", name);

      /* move to head of list to find fast next time */
      *old = def->search_next;

      def->search_next = pr.search;

      pr.search = def;

      return def;
    }

  if (!allocate)
    return NULL;

  /* allocate a new def */
  def = (def_t *) kmalloc(sizeof(def_t));
  /*bzero(def, sizeof(*def)); */
  def->next = NULL;
  pr.def_tail->next = def;

  pr.def_tail = def;

  def->search_next = pr.search;

  pr.search = def;

  /*def->name = (char *)kmalloc(__strlen(name) + 1); */
/*__strcpy(def->name, name); */
  def->name = smalloc(name);
  def->type = type;

  def->scope = scope;

  def->ofs = numpr_globals;
  pr_global_defs[numpr_globals] = def;

  /*
   * make automatic defs for the vectors elements
   * .origin can be accessed as .origin_x, .origin_y, and .origin_z
   */
  if (type->type == ev_vector) {
    sprintf(element, "%s_x", name);
    PR_GetDef(&type_float, element, scope, TRUE);

    sprintf(element, "%s_y", name);
    PR_GetDef(&type_float, element, scope, TRUE);

    sprintf(element, "%s_z", name);
    PR_GetDef(&type_float, element, scope, TRUE);
  }
  else
    numpr_globals += type_size[type->type];

  if (type->type == ev_field) {
    *(int *)&pr_globals[def->ofs] = pr.size_fields;

    if (type->aux_type->type == ev_vector) {
      sprintf(element, "%s_x", name);
      PR_GetDef(&type_floatfield, element, scope, TRUE);

      sprintf(element, "%s_y", name);
      PR_GetDef(&type_floatfield, element, scope, TRUE);

      sprintf(element, "%s_z", name);
      PR_GetDef(&type_floatfield, element, scope, TRUE);
    }
    else
      pr.size_fields += type_size[type->aux_type->type];
  }

  /*      if (pr_dumpasm) */
  /*              PR_PrintOfs (def->ofs); */

  return def;
}
#else
def_t *PR_GetDef(register type_t * type, register char *name, register def_t * scope, register bool allocate)
{
#undef cacheSize
#undef cacheMask
#define cacheSize  128
#define cacheMask  (cacheSize - 1)
  static def_t *cache[cacheSize];
  static int top = 0;
  def_t *def;
  int len;
  char element[MAX_NAME];

  /* See if name is in cache. */
  int sub = top;

  do {
    def = cache[sub];
    if (!def)
      break;
    if (!def->scope || def->scope == scope) {
      if (!__strcmp(def->name, name)) {
	if (type && def->type != type)
	  PR_ParseError(
			 "Type mismatch on redeclaration of %s", name);
	cache[sub] = cache[top];
	cache[top] = def;
	return def;
      }
    }
    sub = (sub - 1) & cacheMask;
  }
  while (sub != top);

  /* See if name is in main list. */
  for (def = pr.def_tail; def; def = def->prev) {
    if (!def->scope || def->scope == scope) {
      if (!__strcmp(def->name, name)) {
	if (type && def->type != type)
	  PR_ParseError(
			 "Type mismatch on redeclaration of %s", name);

	/* Put entry in cache. */
	top = (top + 1) & cacheMask;
	cache[top] = def;
	return def;
      }
    }
  }

  if (!allocate)
    return NULL;

  /* allocate a new def */
  len = __strlen(name) + 1;
  def = malloc(sizeof(def_t) + len);
  def->next = NULL;
  if (!pr.def_head) {
    def->prev = NULL;
    pr.def_head = pr.def_tail = def;
  }
  else {
    pr.def_tail->next = def;
    def->prev = pr.def_tail;
    pr.def_tail = def;
  }
  def->name = (char *)def + sizeof(def_t);
  memcpy(def->name, name, len);
  def->type = type;
  def->initialized = 0;
  def->scope = scope;
  def->ofs = numpr_globals;
  pr_global_defs[numpr_globals] = def;

  /* Put entry in cache. */
  top = (top + 1) & cacheMask;
  cache[top] = def;

  /*
   * make automatic defs for the vectors elements
   * .origin can be accessed as .origin_x, .origin_y, and .origin_z
   */
  if (type->type == ev_vector) {
    sprintf(element, "%s_x", name);
    PR_GetDef(&type_float, element, scope, TRUE);

    sprintf(element, "%s_y", name);
    PR_GetDef(&type_float, element, scope, TRUE);

    sprintf(element, "%s_z", name);
    PR_GetDef(&type_float, element, scope, TRUE);
  }
  else
    numpr_globals += type_size[type->type];

  if (type->type == ev_field) {
    *(int *)&pr_globals[def->ofs] = pr.size_fields;

    if (type->aux_type->type == ev_vector) {
      sprintf(element, "%s_x", name);
      PR_GetDef(&type_floatfield, element, scope, TRUE);

      sprintf(element, "%s_y", name);
      PR_GetDef(&type_floatfield, element, scope, TRUE);

      sprintf(element, "%s_z", name);
      PR_GetDef(&type_floatfield, element, scope, TRUE);
    }
    else
      pr.size_fields += type_size[type->aux_type->type];
  }

  /*      if (pr_dumpasm) */
  /*              PR_PrintOfs (def->ofs); */

  return def;
}
#endif

/*
 * ============
 * PR_ParseValue
 * 
 * Returns the global ofs for the current token
 * ============
 */
def_t *PR_ParseValue(void)
{
  def_t *d;
  char *name;

  /* if the token is an immediate, allocate a constant for it */
  if (pr_token_type == tt_immediate)
    return PR_ParseImmediate();

  name = PR_ParseName();

  /* look through the defs */
  d = PR_GetDef(NULL, name, pr_scope, FALSE);
  if (!d)
    PR_ParseError("Unknown value \"%s\"", name);
  return d;
}

/*
 * ============
 * PR_Term
 * ============
 */
def_t *PR_Term(void)
{
  def_t *e, *e2;
  etype_t t;

  if (PR_Check("!")) {
    e = PR_Expression(NOT_PRIORITY);
    t = e->type->type;
    if (t == ev_float)
      e2 = PR_Statement(&pr_opcodes[OP_NOT_F], e, 0);
    else if (t == ev_string)
      e2 = PR_Statement(&pr_opcodes[OP_NOT_S], e, 0);
    else if (t == ev_entity)
      e2 = PR_Statement(&pr_opcodes[OP_NOT_ENT], e, 0);
    else if (t == ev_vector)
      e2 = PR_Statement(&pr_opcodes[OP_NOT_V], e, 0);
    else if (t == ev_function)
      e2 = PR_Statement(&pr_opcodes[OP_NOT_FNC], e, 0);
    else {
      e2 = NULL;						/* shut up compiler warning; */

      PR_ParseError("type mismatch for !");
    }
    return e2;
  }

  if (PR_Check("(")) {
    e = PR_Expression(TOP_PRIORITY);
    PR_Expect(")");
    return e;
  }

  return PR_ParseValue();
}

/*
 * ==============
 * PR_Expression
 * ==============
 */

def_t *PR_Expression(register int priority)
{
  opcode_t *op, *oldop;
  def_t *e, *e2;
  etype_t type_a, type_b, type_c;

  if (priority == 0)
    return PR_Term();

  e = PR_Expression(priority - 1);

  while (1) {
    if (priority == 1 && PR_Check("("))
      return PR_ParseFunctionCall(e);

    for (op = pr_opcodes; op->name; op++) {
      if (op->priority != priority)
	continue;
      if (!PR_Check(op->name))
	continue;
      if (op->right_associative) {
	/* if last statement is an indirect, change it to an address of */
	if ((unsigned)(statements[numstatements - 1].op - OP_LOAD_F) < 6) {
	  statements[numstatements - 1].op = OP_ADDRESS;
	  def_pointer.type->aux_type = e->type;
	  e->type = def_pointer.type;
	}
	e2 = PR_Expression(priority);
      }
      else
	e2 = PR_Expression(priority - 1);

      /* type check */
      type_a = e->type->type;
      type_b = e2->type->type;

      if (op->name[0] == '.') {					/* field access gets type from field */
	if (e2->type->aux_type)
	  type_c = e2->type->aux_type->type;
	else
	  type_c = -1;						/* not a field */

      }
      else
	type_c = ev_void;

      oldop = op;
      while (type_a != op->type_a->type->type
	     || type_b != op->type_b->type->type
	     || (type_c != ev_void && type_c != op->type_c->type->type)) {
	op++;
	if (!op->name || __strcmp(op->name, oldop->name))
	  PR_ParseError("type mismatch for %s", oldop->name);
      }

      if (type_a == ev_pointer && type_b != e->type->aux_type->type)
	PR_ParseError("type mismatch for %s", op->name);

      if (op->right_associative)
	e = PR_Statement(op, e2, e);
      else
	e = PR_Statement(op, e, e2);

      if (type_c != ev_void)					/* field access gets type from field */
	e->type = e2->type->aux_type;

      break;
    }
    if (!op->name)
      break;							/* next token isn't at this priority level */

  }

  return e;
}

/*
 * ============
 * PR_ParseStatement
 * 
 * ============
 */
void PR_ParseDefs(void);
void PR_ParseStatement(void)
{
  def_t *e;
  dstatement_t *patch1, *patch2;

  if (PR_Check("{")) {
    do {
      PR_ParseStatement();
    } while (!PR_Check("}"));
    return;
  }

  if (PR_Check("return")) {
    if (PR_Check(";")) {
      PR_Statement(&pr_opcodes[OP_RETURN], 0, 0);
      return;
    }
    e = PR_Expression(TOP_PRIORITY);
    PR_Expect(";");
    PR_Statement(&pr_opcodes[OP_RETURN], e, 0);
    return;
  }

  if (PR_Check("while")) {
    PR_Expect("(");
    patch2 = &statements[numstatements];
    e = PR_Expression(TOP_PRIORITY);
    PR_Expect(")");
    patch1 = &statements[numstatements];
    PR_Statement(&pr_opcodes[OP_IFNOT], e, 0);
    PR_ParseStatement();
    junkdef.ofs = patch2 - &statements[numstatements];
    PR_Statement(&pr_opcodes[OP_GOTO], &junkdef, 0);
    patch1->b = &statements[numstatements] - patch1;
    return;
  }

  if (PR_Check("do")) {
    patch1 = &statements[numstatements];
    PR_ParseStatement();
    PR_Expect("while");
    PR_Expect("(");
    e = PR_Expression(TOP_PRIORITY);
    PR_Expect(")");
    PR_Expect(";");
    junkdef.ofs = patch1 - &statements[numstatements];
    PR_Statement(&pr_opcodes[OP_IF], e, &junkdef);
    return;
  }

  if (PR_Check("local")) {
    PR_ParseDefs();
    locals_end = numpr_globals;
    return;
  }

  if (PR_Check("if")) {
    PR_Expect("(");
    e = PR_Expression(TOP_PRIORITY);
    PR_Expect(")");

    patch1 = &statements[numstatements];
    PR_Statement(&pr_opcodes[OP_IFNOT], e, 0);

    PR_ParseStatement();

    if (PR_Check("else")) {
      patch2 = &statements[numstatements];
      PR_Statement(&pr_opcodes[OP_GOTO], 0, 0);
      patch1->b = &statements[numstatements] - patch1;
      PR_ParseStatement();
      patch2->a = &statements[numstatements] - patch2;
    }
    else
      patch1->b = &statements[numstatements] - patch1;

    return;
  }

  PR_Expression(TOP_PRIORITY);
  PR_Expect(";");
}

/*
 * ==============
 * PR_ParseState
 * 
 * States are special functions made for convenience.  They automatically
 * set frame, nextthink (implicitly), and think (allowing forward definitions).
 * 
 * // void() name = [framenum, nextthink] {code}
 * // expands to:
 * // function void name ()
 * // {
 * //           self.frame=framenum;
 * //           self.nextthink = time + 0.1;
 * //           self.think = nextthink
 * //           <code>
 * // };
 * ==============
 */
void PR_ParseState(void)
{
  char *name;
  def_t *s1, *def;

  if (pr_token_type != tt_immediate || pr_immediate_type != &type_float)
    PR_ParseError("state frame must be a number");
  s1 = PR_ParseImmediate();

  PR_Expect(",");

  name = PR_ParseName();
  def = PR_GetDef(&type_function, name, 0, TRUE);

  PR_Expect("]");

  PR_Statement(&pr_opcodes[OP_STATE], s1, def);
}

/*
 * ============
 * PR_ParseImmediateStatements
 * 
 * Parse a function body
 * ============
 */
function_t *PR_ParseImmediateStatements(register type_t * type)
{
  int i;
  function_t *f;
  def_t *defs[MAX_PARMS];

  f = (function_t *) kmalloc(sizeof(function_t));

  /*
   * check for builtin function definition #1, #2, etc
   */
  if (PR_Check("#")) {
    if (pr_token_type != tt_immediate
	|| pr_immediate_type != &type_float
	|| pr_immediate._float != (int)pr_immediate._float)
      PR_ParseError("Bad builtin immediate");
    f->builtin = (int)pr_immediate._float;
    PR_Lex();
    return f;
  }

  f->builtin = 0;
  /*
   * define the parms
   */
  for (i = 0; i < type->num_parms; i++) {
    defs[i] = PR_GetDef(type->parm_types[i], pr_parm_names[i], pr_scope, TRUE);
    f->parm_ofs[i] = defs[i]->ofs;
    if (i > 0 && f->parm_ofs[i] < f->parm_ofs[i - 1])
      PR_ParseError("bad parm order");
  }

  f->code = numstatements;

  /*
   * check for a state opcode
   */
  if (PR_Check("["))
    PR_ParseState();

  /*
   * parse regular statements
   */
  PR_Expect("{");

  while (!PR_Check("}"))
    PR_ParseStatement();

  /* emit an end of statements opcode */
  PR_Statement(pr_opcodes, 0, 0);

  return f;
}

/*
 * ================
 * PR_ParseDefs
 * 
 * Called at the outer layer and when a local statement is hit
 * ================
 */
void PR_ParseDefs(void)
{
  char *name;
  type_t *type;
  def_t *def;
  function_t *f;
  dfunction_t *df;
  int i;
  int locals_start;

  type = PR_ParseType();

  if (pr_scope && (type->type == ev_field || type->type == ev_function))
    PR_ParseError("Fields and functions must be global");

  do {
    name = PR_ParseName();

    def = PR_GetDef(type, name, pr_scope, TRUE);

    /* check for an initialization */
    if (PR_Check("=")) {
      if (def->initialized)
	PR_ParseError("%s redeclared", name);

      if (type->type == ev_function) {
	locals_start = locals_end = numpr_globals;
	pr_scope = def;
	f = PR_ParseImmediateStatements(type);
	pr_scope = NULL;
	def->initialized = 1;
	G_FUNCTION(def->ofs) = numfunctions;
	f->def = def;
	/*                              if (pr_dumpasm) */
	/*                                      PR_PrintFunction (def); */

	/* fill in the dfunction */
	df = &functions[numfunctions];
	numfunctions++;
	if (f->builtin)
	  df->first_statement = -f->builtin;
	else
	  df->first_statement = f->code;
	df->s_name = CopyString(f->def->name);
	df->s_file = s_file;
	df->numparms = f->def->type->num_parms;
	df->locals = locals_end - locals_start;
	df->parm_start = locals_start;
	for (i = 0; i < df->numparms; i++)
	  df->parm_size[i] = type_size[f->def->type->parm_types[i]->type];

	continue;
      }
      else if (pr_immediate_type != type)
	PR_ParseError("wrong immediate type for %s", name);

      def->initialized = 1;
      memcpy(pr_globals + def->ofs, &pr_immediate, 4 * type_size[pr_immediate_type->type]);
      PR_Lex();
    }

  } while (PR_Check(","));

  PR_Expect(";");
}

/*
 * ============
 * PR_CompileFile
 * 
 * compiles the 0 terminated text, adding defintions to the pr structure
 * ============
 */
bool PR_CompileFile(register char *string, register char *filename)
{
  if (!pr.memory) {
    eprintf("PR_CompileFile: Didn't clear");
    return FALSE;
  }

  PR_ClearGrabMacros();						/* clear the frame macros */

  pr_file_p = string;
  s_file = CopyString(filename);

  pr_source_line = 0;

  PR_NewLine();

  PR_Lex();							/* read first token */

  while (pr_token_type != tt_eof) {
    if (setjmp(pr_parse_abort)) {
      if (++pr_error_count > MAX_ERRORS)
	return FALSE;
      PR_SkipToSemicolon();
      if (pr_token_type == tt_eof)
	return FALSE;
    }

    pr_scope = NULL;						/* outside all functions */

    PR_ParseDefs();
  }

  return (pr_error_count == 0);
}
/*
 * ===============
 * PR_String
 * 
 * Returns a string suitable for printing (no newlines, max 60 chars length)
 * ===============
 */
char *PR_String(register 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(register gofs_t ofs)
{
  def_t *d;

  for (d = pr.def_head; d; d = d->next) {
    if (d->type->type != ev_field)
      continue;
    if (*((int *)&pr_globals[d->ofs]) == ofs)
      return d;
  }
  eprintf("PR_DefForFieldOfs: couldn't find %i", ofs);
  return NULL;
}

/*
 * ============
 * PR_ValueString
 * 
 * Returns a string describing *data in a type specific manner
 * =============
 */
char *PR_ValueString(register etype_t type, register 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_function:
      f = functions + *(int *)val;
      if (!f)
	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, "%g", *(float *)val);
      break;
    case ev_vector:
      sprintf(line, "'%g %g %g'", ((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
 * 
 * Returns a string with a description and the contents of a global,
 * padded to 20 field width
 * ============
 */
char *PR_GlobalStringNoContents(register 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);

  i = __strlen(line);
  for (; i < 16; i++)
    __strcat(line, " ");
  __strcat(line, " ");

  return line;
}

char *PR_GlobalString(register gofs_t ofs)
{
  char *s;
  int i;
  def_t *def;

  /*void *val; */
  static char line[128];

  /*val = (void *)&pr_globals[ofs]; */
  def = pr_global_defs[ofs];
  if (!def)
    return PR_GlobalStringNoContents(ofs);
  if (def->initialized && def->type->type != ev_function) {
    s = PR_ValueString(def->type->type, &pr_globals[ofs]);
    sprintf(line, "%i(%s)", ofs, s);
  }
  else
    sprintf(line, "%i(%s)", ofs, def->name);

  i = __strlen(line);
  for (; i < 16; i++)
    __strcat(line, " ");
  __strcat(line, " ");

  return line;
}

/*
 * ============
 * PR_PrintOfs
 * ============
 */
void PR_PrintOfs(register gofs_t ofs)
{
  mprintf("%s\n", PR_GlobalString(ofs));
}

/*
 * =================
 * PR_PrintStatement
 * =================
 */
void PR_PrintStatement(register dstatement_t * s)
{
  int i;

  mprintf("%4i : %4i : %s ", (int)(s - statements), statement_linenums[s - statements], pr_opcodes[s->op].opname);
  i = __strlen(pr_opcodes[s->op].opname);
  for (; i < 10; i++)
    mprintf(" ");

  if (s->op == OP_IF || s->op == OP_IFNOT)
    mprintf("%sbranch %i", PR_GlobalString(s->a), s->b);
  else if (s->op == OP_GOTO) {
    mprintf("branch %i", s->a);
  }
  else if ((unsigned)(s->op - OP_STORE_F) < 6) {
    mprintf("%s", PR_GlobalString(s->a));
    mprintf("%s", PR_GlobalStringNoContents(s->b));
  }
  else {
    if (s->a)
      mprintf("%s", PR_GlobalString(s->a));
    if (s->b)
      mprintf("%s", PR_GlobalString(s->b));
    if (s->c)
      mprintf("%s", PR_GlobalStringNoContents(s->c));
  }
  mprintf("\n");
}

/*
 * ============
 * PR_PrintDefs
 * ============
 */
void PR_PrintDefs(void)
{
  def_t *d;

  for (d = pr.def_head; d; d = d->next)
    PR_PrintOfs(d->ofs);
}

/*
 * ==============
 * PR_BeginCompilation
 * 
 * called before compiling a batch of files, clears the pr struct
 * ==============
 */
void PR_BeginCompilation(register void *memory, register int memsize)
{
  int i;

  pr.memory = memory;
  pr.max_memory = memsize;

  numpr_globals = RESERVED_OFS;
  pr.def_tail = NULL;

  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;
}

/*
 * ==============
 * PR_FinishCompilation
 * 
 * called after all files are compiled to check for errors
 * Returns FALSE if errors were detected.
 * ==============
 */
bool PR_FinishCompilation(void)
{
  def_t *d;
  bool errors;

  errors = FALSE;

  /* check to make sure all functions prototyped have code */
  for (d = pr.def_head; d; d = d->next) {
    /* function parms are ok */
    if (d->type->type == ev_function && !d->scope) {
      /* f = G_FUNCTION(d->ofs); */
      /* if (!f || (!f->code && !f->builtin) ) */
      if (!d->initialized) {
	eprintf("function %s was not defined\n", d->name);
	errors = TRUE;
      }
    }
  }

  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(register char *filename)
{
  def_t *d;
  FILE *f;
  unsigned short int crc;
  int c;

  mprintf("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; d; d = d->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->next->next->next;				/* skip the elements */

	break;
      case ev_string:
	fprintf(f, "\tstring_t\t%s;\n", d->name);
	break;
      case ev_function:
	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; d; d = d->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->next->next->next;				/* skip the elements */

	break;
      case ev_string:
	fprintf(f, "\tstring_t\t%s;\n", d->name);
	break;
      case ev_function:
	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, (unsigned char)c);

  fprintf(f, "#define PROGHEADER_CRC %i\n", crc);
  fclose(f);

  return crc;
}

void PrintFunction(register 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)
    eprintf("No function names \"%s\"", name);
  else {
    df = functions + i;

    mprintf("Statements for %s:\n", name);
    ds = statements + df->first_statement;
    while (1) {
      PR_PrintStatement(ds);
      if (!ds->op)
	break;
      ds++;
    }
  }
}

/*============================================================================ */

char com_token[1024];
bool com_eof;

/*
 * ==============
 * COM_Parse
 * 
 * Parse a token out of a string
 * ==============
 */
char *COM_Parse(register char *data)
{
  int c;
  int len;

  len = 0;
  com_token[0] = 0;

  if (!data)
    return NULL;

  /* skip whitespace */
skipwhite:
  while ((c = *data) <= ' ') {
    if (c == 0) {
      com_eof = TRUE;
      return NULL;						/* end of file; */

    }
    data++;
  }

  /* skip // comments */
  if (c == '/' && data[1] == '/') {
    while (*data && *data != '\n')
      data++;
    goto skipwhite;
  }

  /* handle quoted strings specially */
  if (c == '\"') {
    data++;
    do {
      c = *data++;
      if (c == '\"') {
	com_token[len] = 0;
	return data;
      }
      com_token[len] = c;
      len++;
    } while (1);
  }

  /* parse single characters */
  if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':') {
    com_token[len] = c;
    len++;
    com_token[len] = 0;
    return data + 1;
  }

  /* parse a regular word */
  do {
    com_token[len] = c;
    data++;
    len++;
    c = *data;
    if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':')
      break;
  } while (c > 32);

  com_token[len] = 0;
  return data;
}

/*=========================================================================== */
/*unqcc */
/*=========================================================================== */

FILE *DEC_ofile;
FILE *DEC_progssrc;

#ifdef	QC_PROFILE
FILE *DEC_profile;

#endif
char *DEC_FilesSeen[1024];
int DEC_FileCtr = 0;
char *DEC_Profiles[MAX_FUNCTIONS];

char *type_names[8] =
{"void", "string", "float", "vector", "entity", "ev_field", "void()", "ev_pointer"};

char *builtins[79] =
{
  NULL,
  "void (vector ang)",
  "void (entity e, vector o)",
  "void (entity e, string m)",
  "void (entity e, vector min, vector max)",
  NULL,
  "void ()",
  "float ()",
  "void (entity e, float chan, string samp, float vol, float atten)",
  "vector (vector v)",
  "void (string e)",
  "void (string e)",
  "float (vector v)",
  "float (vector v)",
  "entity ()",
  "void (entity e)",
  "void (vector v1, vector v2, float nomonsters, entity forent)",
  "entity ()",
  "entity (entity start, .string fld, string match)",
  "string (string s)",
  "string (string s)",
  "void (entity client, string s)",
  "entity (vector org, float rad)",
  "void (string s)",
  "void (entity client, string s)",
  "void (string s)",
  "string (float f)",
  "string (vector v)",
  "void ()",
  "void ()",
  "void ()",
  "void (entity e)",
  "float (float yaw, float dist)",
  NULL,
  "float (float yaw, float dist)",
  "void (float style, string value)",
  "float (float v)",
  "float (float v)",
  "float (float v)",
  NULL,
  "float (entity e)",
  "float (vector v)",
  NULL,
  "float (float f)",
  "vector (entity e, float speed)",
  "float (string s)",
  "void (string s)",
  "entity (entity e)",
  "void (vector o, vector d, float color, float count)",
  "void ()",
  NULL,
  "vector (vector v)",
  "void (float to, float f)",
  "void (float to, float f)",
  "void (float to, float f)",
  "void (float to, float f)",
  "void (float to, float f)",
  "void (float to, float f)",
  "void (float to, string s)",
  "void (float to, entity s)",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "void (float step)",
  "string (string s)",
  "void (entity e)",
  "void (string s)",
  NULL,
  "void (string var, string val)",
  "void (entity client, string s)",
  "void (vector pos, string samp, float vol, float atten)",
  "string (string s)",
  "string (string s)",
  "string (string s)",
  "void (entity e)"
};

int DEC_GetFunctionIdxByName(register char *name)
{
  int i;

  for (i = 1; i < numfunctions; i++)
    if (!__strcmp(name, strings + functions[i].s_name)) {
      break;
    }
  return i;
}

int DEC_AlreadySeen(register char *fname)
{
  int i;
  char *new;

  if (DEC_FileCtr > 1000)
    eprintf("DEC_AlreadySeen - too many source files.");
  else {
    for (i = 0; i < DEC_FileCtr; i++) {
      if (!__strcmp(fname, DEC_FilesSeen[i]))
	return 1;
    }

    /*new = (char *)kmalloc(__strlen(fname) + 1); */
/*__strcpy(new, fname); */
    new = smalloc(fname);
    DEC_FilesSeen[DEC_FileCtr] = new;
    DEC_FileCtr++;

    mprintf("decompiling %s\n", fname);
  }
  return 0;
}

ddef_t *DEC_GetParameter(register gofs_t ofs)
{
  int i;
  ddef_t *def;

  def = NULL;

  for (i = 0; i < numglobaldefs; i++) {
    def = &globals[i];

    if (def->ofs == ofs) {
      return def;
    }
  }

  return NULL;
}

/*
 * fix: same as PR_String
 */
char *DEC_String(register char *string)
{
  static char buf[255];
  char *s;
  int c = 1;

  s = buf;
  *s++ = '"';
  while (string && *string) {
    if (c == sizeof(buf) - 2)
      break;
    if (*string == '\n') {
      *s++ = '\\';
      *s++ = 'n';
      c++;
    }
    else if (*string == '"') {
      *s++ = '\\';
      *s++ = '"';
      c++;
    }
    else {
      *s++ = *string;
      c++;
    }
    string++;
    if (c > sizeof(buf) - 10) {
      *s++ = '.';
      *s++ = '.';
      *s++ = '.';
      c += 3;
      break;
    }
  }
  *s++ = '"';
  *s++ = 0;
  return buf;
}

/*
 * fix: same as PR_ValueString
 */
char *DEC_ValueString(register etype_t type, register void *val)
{
  static char line[1024];

  line[0] = '\0';

  switch (type) {
    case ev_string:
      sprintf(line, "%s", DEC_String(strings + *(int *)val));
      break;
    case ev_void:
      sprintf(line, "void");
      break;
    case ev_float:
      sprintf(line, "%g", *(float *)val);
      break;
    case ev_vector:
      sprintf(line, "'%g %g %g'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]);
      break;
    default:
      sprintf(line, "bad type %i", type);
      break;
  }

  return line;
}

char *DEC_PrintParameter(register ddef_t * def)
{
  static char line[128];

  line[0] = '0';

  if (!__strcmp(strings + def->s_name, "IMMEDIATE")) {
    sprintf(line, "%s", DEC_ValueString(def->type, &pr_globals[def->ofs]));
  }
  else
    sprintf(line, "%s %s", type_names[def->type], strings + def->s_name);

  return line;
}

void DEC_CalcProfiles(void)
{
  int i, j, ps;
  static char fname[512];
  static char line[512];
  char *new;
  dfunction_t *df;
  dstatement_t *ds, *rds;
  ddef_t *par;
  unsigned short dom;

  for (i = 1; i < numfunctions; i++) {
    df = functions + i;
    fname[0] = '\0';
    line[0] = '\0';
    DEC_Profiles[i] = NULL;

    if (df->first_statement <= 0) {
      sprintf(fname, "%s %s", builtins[-df->first_statement], strings + functions[i].s_name);
    }
    else {
      ds = statements + df->first_statement;
      rds = NULL;

      /* find a return statement, to determine the result type */

      while (1) {
	dom = (ds->op) % 100;
	if (!dom)
	  break;
	if (dom == OP_RETURN) {
	  rds = ds;
	  /*                  break;  */
	}
	ds++;
      }

      /* print the return type  */
      if ((rds != NULL) && (rds->a != 0)) {
	par = DEC_GetParameter(rds->a);

	if (par) {
	  sprintf(fname, "%s ", type_names[par->type]);
	}
	else {
	  sprintf(fname, "float /* ERROR: Could not determine return type */ ");
	}
      }
      else {
	sprintf(fname, "\nvoid ");
      }
      __strcat(fname, "(");

      /* determine overall parameter size */

      for (j = 0, ps = 0; j < df->numparms; j++)
	ps += df->parm_size[j];

      if (ps > 0) {
	for (j = df->parm_start; j < (df->parm_start) + ps; j++) {
	  line[0] = '\0';
	  par = DEC_GetParameter(j);

	  if (!par)
	    eprintf("DEC_CalcProfiles - No parameter names with offset %i.", j);
	  else {
	    if (par->type == ev_vector)
	      j += 2;

	    if (j < (df->parm_start) + ps - 1) {
	      sprintf(line, "%s, ", DEC_PrintParameter(par));
	    }
	    else {
	      sprintf(line, "%s", DEC_PrintParameter(par));
	    }
	    __strcat(fname, line);
	  }
	}
      }
      __strcat(fname, ") ");
      line[0] = '\0';
      sprintf(line, strings + functions[i].s_name);
      __strcat(fname, line);
    }

    if (i >= MAX_FUNCTIONS)
      eprintf("DEC_CalcProfiles - too many functions.");
    else {
      /*new = (char *)kmalloc(__strlen(fname) + 1); */
/*__strcpy(new, fname); */
      new = smalloc(fname);
      DEC_Profiles[i] = new;
    }
  }
}

char *DEC_Global(register gofs_t ofs, register def_t * req_t)
{
  int i;
  ddef_t *def;
  static char line[256];
  char *res;
  char found = 0;

  line[0] = '\0';

  def = NULL;

  for (i = 0; i < numglobaldefs; i++) {
    def = &globals[i];
    if (def->ofs == ofs) {
      oprintf("DEC_Global - Found %i at %i.\n", ofs, (int)def);
      found = 1;
      break;
    }
  }

  if (found) {
    if (!__strcmp(strings + def->s_name, "IMMEDIATE"))
      sprintf(line, "%s", DEC_ValueString(def->type, &pr_globals[def->ofs]));
    else {

      sprintf(line, "%s", strings + def->s_name);
      if (def->type == ev_vector && req_t == &def_float)
	__strcat(line, "_x");
    }
    /*res = (char *)kmalloc(__strlen(line) + 1); */
/*__strcpy(res, line); */
    res = smalloc(line);

    oprintf("DEC_Global - Found \"%s\"(%i) at %i.\n", line, ofs, (int)def);
    return res;

  }
  return NULL;
}

gofs_t DEC_ScaleIndex(register dfunction_t * df, register gofs_t ofs)
{
  gofs_t nofs = 0;

  if (ofs > RESERVED_OFS)
    nofs = ofs - df->parm_start + RESERVED_OFS;
  else
    nofs = ofs;

  return nofs;
}

#define MAX_NO_LOCAL_IMMEDIATES 1024

char *DEC_Immediate(register dfunction_t * df, register gofs_t ofs, register int fun, register char *new)
{
  int i;
  gofs_t nofs;
  static char *IMMEDIATES[MAX_NO_LOCAL_IMMEDIATES];

  /*
   * free 'em all 
   */
  if (fun == 0) {
    oprintf("DEC_Immediate - Initializing function environment.\n");
    for (i = 0; i < MAX_NO_LOCAL_IMMEDIATES; i++)
      if (IMMEDIATES[i])
	tfree(IMMEDIATES[i]);
    bzero(&IMMEDIATES, MAX_NO_LOCAL_IMMEDIATES * sizeof(char *));

    return NULL;
  }

  nofs = DEC_ScaleIndex(df, ofs);
  oprintf("DEC_Immediate - Index scale: %i -> %i.\n", ofs, nofs);

  /*
   * check consistency 
   */
  if ((nofs <= 0) || (nofs > MAX_NO_LOCAL_IMMEDIATES - 1))
    eprintf("DEC_Immediate - Index (%i) out of bounds.\n", nofs);
  else {
    /*
     * insert at nofs 
     */
    if (fun == 1) {
      if (IMMEDIATES[nofs])
	tfree(IMMEDIATES[nofs]);
      IMMEDIATES[nofs] = smalloc(new);
      oprintf("DEC_Immediate - Putting \"%s\" at index %i.\n", new, nofs);
    }

    /*
     * get from nofs 
     */
    if (fun == 2) {
      if (IMMEDIATES[nofs]) {
	oprintf("DEC_Immediate - Reading \"%s\" at index %i.\n", IMMEDIATES[nofs], nofs);
	return smalloc(IMMEDIATES[nofs]);
      }
      else
	eprintf("DEC_Immediate - %i not defined.", nofs);
    }
  }

  return NULL;
}

char *DEC_Get(register dfunction_t * df, register gofs_t ofs, register def_t * req_t)
{
  char *arg1 = NULL;

  arg1 = DEC_Global(ofs, req_t);

  if (arg1 == NULL)
    arg1 = DEC_Immediate(df, ofs, 2, NULL);

  /*
   * if (arg1)
   * mprintf("DEC_Get - found \"%s\".\n",arg1);
   */

  return arg1;
}

void DEC_Indent(register int c)
{
  int i;

  if (c < 0)
    c = 0;

  for (i = 0; i < c; i++) {
    fprintf(DEC_ofile, "   ");
  }
}

void DEC_DecompileStatement(register dfunction_t * df, register dstatement_t * s, register int *indent)
{
  static char line[512];
  static char fnam[512];
  char *arg1, *arg2, *arg3;
  int nargs, i, j;
  dstatement_t *t;
  unsigned short dom, doc, ifc, tom;
  def_t *typ1, *typ2, *typ3;

  dstatement_t *k;
  int dum;

  arg1 = arg2 = arg3 = NULL;

  line[0] = '\0';
  fnam[0] = '\0';

  dom = s->op;

  doc = dom / 10000;
  ifc = (dom % 10000) / 100;

  /*
   * use program flow information 
   */
  for (i = 0; i < ifc; i++) {
    (*indent)--;
    fprintf(DEC_ofile, "\n");
    DEC_Indent(*indent);
    fprintf(DEC_ofile, "}\n");
  }
  for (i = 0; i < doc; i++) {
    DEC_Indent(*indent);
    fprintf(DEC_ofile, "do {\n\n");
    (*indent)++;
  }

  /*
   * remove all program flow information 
   */
  s->op %= 100;

  typ1 = pr_opcodes[s->op].type_a;
  typ2 = pr_opcodes[s->op].type_b;
  typ3 = pr_opcodes[s->op].type_c;

  /*
   * mprintf("DEC_DecompileStatement - decompiling %i (%i):\n",(int)(s - statements),dom);
   * DEC_PrintStatement (s);  
   */
  /* states are handled at top level */
  if (s->op == OP_DONE || s->op == OP_STATE) {
  }
  else if (s->op == OP_RETURN) {
    DEC_Indent(*indent);
    fprintf(DEC_ofile, "return ");

    if (s->a) {
      arg1 = DEC_Get(df, s->a, typ1);
      fprintf(DEC_ofile, "( %s )", arg1);
    }

    fprintf(DEC_ofile, ";\n");
  }
  else if ((OP_MUL_F <= s->op && s->op <= OP_SUB_V) ||
	   (OP_EQ_F <= s->op && s->op <= OP_GT) ||
	   (OP_AND <= s->op && s->op <= OP_BITOR)) {
    arg1 = DEC_Get(df, s->a, typ1);
    arg2 = DEC_Get(df, s->b, typ2);
    arg3 = DEC_Global(s->c, typ3);

    if (arg3) {
      DEC_Indent(*indent);
      fprintf(DEC_ofile, "%s = %s %s %s;\n", arg3, arg1, pr_opcodes[s->op].name, arg2);
    }
    else {
      sprintf(line, "(%s %s %s)", arg1, pr_opcodes[s->op].name, arg2);
      DEC_Immediate(df, s->c, 1, line);
    }
  }
  else if (OP_LOAD_F <= s->op && s->op <= OP_ADDRESS) {
    arg1 = DEC_Get(df, s->a, typ1);
    arg2 = DEC_Get(df, s->b, typ2);
    arg3 = DEC_Global(s->c, typ3);

    if (arg3) {
      DEC_Indent(*indent);
      fprintf(DEC_ofile, "%s = %s.%s;\n", arg3, arg1, arg2);
    }
    else {
      sprintf(line, "%s.%s", arg1, arg2);
      DEC_Immediate(df, s->c, 1, line);
    }
  }
  else if (OP_STORE_F <= s->op && s->op <= OP_STORE_FNC) {
    arg1 = DEC_Get(df, s->a, typ1);
    arg3 = DEC_Global(s->b, typ2);

    if (arg3) {
      DEC_Indent(*indent);
      fprintf(DEC_ofile, "%s = %s;\n", arg3, arg1);
    }
    else {
      sprintf(line, "%s", arg1);
      DEC_Immediate(df, s->b, 1, line);
    }
  }
  else if (OP_STOREP_F <= s->op && s->op <= OP_STOREP_FNC) {
    arg1 = DEC_Get(df, s->a, typ1);
    arg2 = DEC_Get(df, s->b, typ2);

    DEC_Indent(*indent);
    fprintf(DEC_ofile, "%s = %s;\n", arg2, arg1);
  }
  else if (OP_NOT_F <= s->op && s->op <= OP_NOT_FNC) {
    arg1 = DEC_Get(df, s->a, typ1);
    sprintf(line, "!%s", arg1);
    DEC_Immediate(df, s->c, 1, line);
  }
  else if (OP_CALL0 <= s->op && s->op <= OP_CALL8) {
    nargs = s->op - OP_CALL0;
    arg1 = DEC_Get(df, s->a, NULL);
    sprintf(line, "%s (", arg1);
    sprintf(fnam, "%s", arg1);

    for (i = 0; i < nargs; i++) {
      typ1 = NULL;
      j = 4 + 3 * i;

      if (arg1) {
	tfree(arg1);
	arg1 = 0;
      }

      arg1 = DEC_Get(df, j, typ1);
      __strcat(line, arg1);

#ifndef DONT_USE_DIRTY_TRICKS
      if (!__strcmp(fnam, "WriteCoord"))
	if (!__strcmp(arg1, "org") || !__strcmp(arg1, "trace_endpos") || !__strcmp(arg1, "p1") || !__strcmp(arg1, "p2") || !__strcmp(arg1, "o"))
	  __strcat(line, "_x");
#endif
      if (i < nargs - 1)
	__strcat(line, ",");
    }
    __strcat(line, ")");
    DEC_Immediate(df, 1, 1, line);

    /*
     * if ( ( ( (s+1)->a != 1) && ( (s+1)->b != 1) && 
     * ( (s+2)->a != 1) && ( (s+2)->b != 1) ) || 
     * ( ((s+1)->op) % 100 == OP_CALL0 ) ) {
     * DEC_Indent(*indent);
     * fprintf(DEC_ofile,"%s;\n",line);
     * }
     */
    /* this SUCKS!!!!!!!!!!!!! */
    if ((((s + 1)->a != 1) && ((s + 1)->b != 1) &&
	 ((s + 2)->a != 1) && ((s + 2)->b != 1)) ||
	((((s + 1)->op) % 100 == OP_CALL0) && ((((s + 2)->a != 1)) || ((s + 2)->b != 1)))) {
      DEC_Indent(*indent);
      fprintf(DEC_ofile, "%s;\n", line);
    }
  }
  else if (s->op == OP_IF || s->op == OP_IFNOT) {
    arg1 = DEC_Get(df, s->a, NULL);
    arg2 = DEC_Global(s->a, NULL);

    if (s->op == OP_IFNOT) {
      if (s->b < 1) {
	eprintf("Found a negative IFNOT jump.");
	return;
      }

      /* get instruction right before the target */
      t = s + s->b - 1;
      tom = t->op % 100;

      if (tom != OP_GOTO) {
	/* pure if */
	DEC_Indent(*indent);
	fprintf(DEC_ofile, "if ( %s ) {\n\n", arg1);
	(*indent)++;
      }
      else {
	if (t->a > 0) {
	  /* ite */
	  DEC_Indent(*indent);
	  fprintf(DEC_ofile, "if ( %s ) {\n\n", arg1);
	  (*indent)++;
	}
	else {
	  if ((t->a + s->b) > 1) {
	    /* pure if  */
	    DEC_Indent(*indent);
	    fprintf(DEC_ofile, "if ( %s ) {\n\n", arg1);
	    (*indent)++;
	  }
	  else {
	    dum = 1;
	    for (k = t + (t->a); k < s; k++) {
	      tom = k->op % 100;
	      if (tom == OP_GOTO || tom == OP_IF || tom == OP_IFNOT)
		dum = 0;
	    }
	    if (dum) {
	      /* while  */
	      DEC_Indent(*indent);
	      fprintf(DEC_ofile, "while ( %s ) {\n\n", arg1);
	      (*indent)++;
	    }
	    else {
	      /* pure if  */
	      DEC_Indent(*indent);
	      fprintf(DEC_ofile, "if ( %s ) {\n\n", arg1);
	      (*indent)++;
	    }
	  }
	}
      }
    }
    else {
      /* do ... while */
      (*indent)--;
      fprintf(DEC_ofile, "\n");
      DEC_Indent(*indent);
      fprintf(DEC_ofile, "} while ( %s );\n", arg1);
    }
  }
  else if (s->op == OP_GOTO) {
    if (s->a > 0) {
      /* else */
      (*indent)--;
      fprintf(DEC_ofile, "\n");
      DEC_Indent(*indent);
      fprintf(DEC_ofile, "} else {\n\n");
      (*indent)++;
    }
    else {
      /* while */
      (*indent)--;
      fprintf(DEC_ofile, "\n");
      DEC_Indent(*indent);
      fprintf(DEC_ofile, "}\n");
    }
  }
  else {
    fprintf(DEC_ofile, "\n/* ERROR: UNKNOWN COMMAND */\n");
  }

  oprintf("DEC_DecompileStatement - Current line is \"%s\"\n", line);
  if (arg1) {
    tfree(arg1);
    arg1 = 0;
  }
  if (arg2) {
    tfree(arg2);
    arg2 = 0;
  }
  if (arg3) {
    tfree(arg3);
    arg3 = 0;
  }

  return;
}

void DEC_DecompileFunction(register dfunction_t * df)
{
  dstatement_t *ds;
  int indent;

  /* Initialize */
  DEC_Immediate(df, 0, 0, NULL);

  indent = 1;

  ds = statements + df->first_statement;
  while (1) {
    DEC_DecompileStatement(df, ds, &indent);
    if (!ds->op)
      break;
    ds++;
  }

  if (indent != 1)
    fprintf(DEC_ofile, "/* ERROR : Indentiation structure corrupt */\n");
}

ddef_t *GetField(register char *name)
{
  int i;
  ddef_t *d;

  for (i = 1; i < numfielddefs; i++) {
    d = &fields[i];

    if (!__strcmp(strings + d->s_name, name))
      return d;
  }
  return NULL;
}

void DEC_Function(register char *name)
{
  int i, findex, ps;
  dstatement_t *ds, *ts;
  dfunction_t *df;
  ddef_t *par;
  char *arg2;
  unsigned short dom, tom;

  int j, start, end;
  dfunction_t *dfpred;
  ddef_t *ef;

  static char line[256];

  dstatement_t *k;
  int dum;

  for (i = 1; i < numfunctions; i++)
    if (!__strcmp(name, strings + functions[i].s_name))
      break;
  if (i == numfunctions) {
    eprintf("No function named \"%s\"", name);
    return;
  }

  df = functions + i;

  findex = i;

  /* Check ''local globals'' */
  dfpred = df - 1;

  for (j = 0, ps = 0; j < dfpred->numparms; j++)
    ps += dfpred->parm_size[j];

  start = dfpred->parm_start + dfpred->locals + ps;

  if (dfpred->first_statement < 0 && df->first_statement > 0)
    start -= 1;

  if (start == 0)
    start = 1;

  end = df->parm_start;

  for (j = start; j < end; j++) {
    par = DEC_GetParameter(j);

    if (par) {
      if (par->type & (1 << 15))
	par->type -= (1 << 15);

      if (par->type == ev_function) {
	if (__strcmp(strings + par->s_name, "IMMEDIATE"))
	  if (__strcmp(strings + par->s_name, name)) {
	    fprintf(DEC_ofile, "%s;\n", DEC_Profiles[DEC_GetFunctionIdxByName(strings + par->s_name)]);
	  }
      }
      else if (par->type != ev_pointer)
	if (__strcmp(strings + par->s_name, "IMMEDIATE")) {
	  if (par->type == ev_field) {
	    ef = GetField(strings + par->s_name);

	    if (!ef) {
	      eprintf("Could not locate a field named \"%s\"", strings + par->s_name);
	      return;
	    }

	    if (ef->type == ev_vector)
	      j += 3;
#ifndef DONT_USE_DIRTY_TRICKS
	    if ((ef->type == ev_function) && !__strcmp(strings + ef->s_name, "th_pain")) {
	      fprintf(DEC_ofile, ".void(entity attacker, float damage) th_pain;\n");
	    }
	    else
#endif
	      fprintf(DEC_ofile, ".%s %s;\n", type_names[ef->type], strings + ef->s_name);
	  }
	  else {
	    if (par->type == ev_vector)
	      j += 2;
	    if (par->type == ev_entity || par->type == ev_void) {
	      fprintf(DEC_ofile, "%s %s;\n", type_names[par->type], strings + par->s_name);
	    }
	    else {
	      line[0] = '\0';
	      sprintf(line, "%s", DEC_ValueString(par->type, &pr_globals[par->ofs]));

	      if ((__strlen(strings + par->s_name) > 1) &&
		  isupper((strings + par->s_name)[0]) &&
		  (isupper((strings + par->s_name)[1]) || (strings + par->s_name)[1] == '_')) {
		fprintf(DEC_ofile, "%s %s    = %s;\n", type_names[par->type], strings + par->s_name, line);
	      }
	      else
		fprintf(DEC_ofile, "%s %s /* = %s */;\n", type_names[par->type], strings + par->s_name, line);
	    }
	  }
	}
    }
  }
  /* Check ''local globals'' */
  if (df->first_statement <= 0) {
    fprintf(DEC_ofile, "%s", DEC_Profiles[findex]);
    fprintf(DEC_ofile, " = #%i; \n", -df->first_statement);
    return;
  }

  ds = statements + df->first_statement;

  while (1) {
    dom = (ds->op) % 100;

    if (!dom)
      break;
    else if (dom == OP_GOTO) {
      /*
       * check for i-t-e 
       */
      if (ds->a > 0) {
	ts = ds + ds->a;
	/* mark the end of a if/ite construct */
	ts->op += 100;
      }
    }
    else if (dom == OP_IFNOT) {
      /*
       * check for pure if 
       */
      ts = ds + ds->b;
      tom = (ts - 1)->op % 100;

      if (tom != OP_GOTO)
	/* mark the end of a if/ite construct */
	ts->op += 100;
      else if ((ts - 1)->a < 0) {
	/*
	 * arg2 = DEC_Global(ds->a,NULL);
	 * 
	 * if (!( ( ((ts-1)->a + ds->b) == 0)   || 
	 * ( (arg2 != NULL) && (((ts-1)->a + ds->b) == 1) ))) 
	 * (ts-1)->op += 100;
	 * 
	 * if (arg2) {
	 *   tfree(arg2);
	 *   arg2 = 0;
	 * }
	 */
	if (((ts - 1)->a + ds->b) > 1) {
	  /* pure if  */
	  /* mark the end of a if/ite construct */
	  ts->op += 100;
	}
	else {
	  dum = 1;
	  for (k = (ts - 1) + ((ts - 1)->a); k < ds; k++) {
	    tom = k->op % 100;
	    if (tom == OP_GOTO || tom == OP_IF || tom == OP_IFNOT)
	      dum = 0;
	  }
	  if (!dum) {
	    /* pure if  */
	    /* mark the end of a if/ite construct */
	    ts->op += 100;
	  }
	}
      }
    }
    else if (dom == OP_IF) {
      ts = ds + ds->b;
      /* mark the start of a do construct */
      ts->op += 10000;
    }
    ds++;
  }

  /* print the prototype */
  fprintf(DEC_ofile, "%s", DEC_Profiles[findex]);

  /* handle state functions */
  ds = statements + df->first_statement;

  if (ds->op == OP_STATE) {
    par = DEC_GetParameter(ds->a);
    if (!par) {
      eprintf("DEC_Function - Can't determine frame number.");
      return;
    }

    arg2 = DEC_Get(df, ds->b, NULL);
    if (!arg2) {
      eprintf("DEC_Function - No state parameter with offset %i.", ds->b);
      return;
    }

    fprintf(DEC_ofile, " = [ %s, %s ]", DEC_ValueString(par->type, &pr_globals[par->ofs]), arg2);
    tfree(arg2);
    arg2 = 0;
  }
  else {
    fprintf(DEC_ofile, " =");
  }
  fprintf(DEC_ofile, " {\n\n");

#ifdef	QC_PROFILE
  fprintf(DEC_profile, "%s", DEC_Profiles[findex]);
  fprintf(DEC_profile, ") %s;\n", name);
#endif

  /* calculate the parameter size */
  for (j = 0, ps = 0; j < df->numparms; j++)
    ps += df->parm_size[j];

  /* print the locals */
  if (df->locals > 0) {
    if ((df->parm_start) + df->locals - 1 >= (df->parm_start) + ps) {
      for (i = df->parm_start + ps; i < (df->parm_start) + df->locals; i++) {
	par = DEC_GetParameter(i);

	if (!par) {
	  fprintf(DEC_ofile, "   /* ERROR: No local name with offset %i */\n", i);
	}
	else {
	  if (par->type == ev_function)
	    fprintf(DEC_ofile, "   /* ERROR: Fields and functions must be global */\n");
	  else
	    fprintf(DEC_ofile, "   local %s;\n", DEC_PrintParameter(par));
	  if (par->type == ev_vector)
	    i += 2;
	}
      }

      fprintf(DEC_ofile, "\n");
    }
  }

  /* do the hard work */
  DEC_DecompileFunction(df);
  fprintf(DEC_ofile, "\n};\n");
}

bool DEC_DecompileFunctions(register char *destDir)
{
  int i;
  dfunction_t *d;
  FILE *f;
  char fname[512];

  DEC_CalcProfiles();

  fname[0] = '\0';
  sprintf(fname, "%s%s", destDir, "progs.src");
  DEC_progssrc = fopen(fname, "w");
  if (!DEC_progssrc) {
    eprintf("DEC_DecompileFunctions - Could not open \"progs.src\" for output.");
    return FALSE;
  }

  fprintf(DEC_progssrc, "./progs.dat\n\n");

#ifdef	QC_PROFILE
  DEC_profile = fopen("!profile.qc", "w");
  if (!DEC_profile)
    Error("DEC_DecompileFunctions - Could not open \"!profile.qc\" for output.");
  fprintf(DEC_progssrc, "!profile.qc\n");
#endif

  for (i = 1; i < numfunctions; i++) {
    d = &functions[i];

    fname[0] = '\0';
    sprintf(fname, "%s%s", destDir, strings + d->s_file);
    f = fopen(fname, "a+");

    if (!DEC_AlreadySeen(fname))
      fprintf(DEC_progssrc, "%s\n", fname);

    if (!f) {
      eprintf("DEC_DecompileFunctions - Could not open \"%s\" for output.", fname);
      return FALSE;
    }
    DEC_ofile = f;
    DEC_Function(strings + d->s_name);

    if (fclose(f)) {
      eprintf("DEC_DecompileFunctions - Could not close \"%s\" properly.", fname);
      return FALSE;
    }
  }

  if (fclose(DEC_progssrc)) {
    eprintf("DEC_DecompileFunctions - Could not close \"progs.src\" properly.");
    return FALSE;
  }

#ifdef	QC_PROFILE
  if (fclose(DEC_profile))
    Error("DEC_DecompileFunctions - Could not close \"!profile.qc\" properly.");
#endif
  return TRUE;
}

char *DEC_GlobalStringNoContents(register gofs_t ofs)
{
  int i;
  ddef_t *def;
  static char line[128];

  line[0] = '0';
  sprintf(line, "%i(???)", ofs);

  for (i = 0; i < numglobaldefs; i++) {
    def = &globals[i];

    if (def->ofs == ofs) {
      line[0] = '0';
      sprintf(line, "%i(%s)", def->ofs, strings + def->s_name);
      break;
    }
  }

  i = __strlen(line);
  for (; i < 16; i++)
    __strcat(line, " ");
  __strcat(line, " ");

  return line;
}

char *DEC_GlobalString(register gofs_t ofs)
{
  char *s;
  int i;
  ddef_t *def;
  static char line[128];

  line[0] = '0';
  sprintf(line, "%i(???)", ofs);

  for (i = 0; i < numglobaldefs; i++) {
    def = &globals[i];

    if (def->ofs == ofs) {

      line[0] = '0';
      if (!__strcmp(strings + def->s_name, "IMMEDIATE")) {
	s = PR_ValueString(def->type, &pr_globals[ofs]);
	sprintf(line, "%i(%s)", def->ofs, s);
      }
      else
	sprintf(line, "%i(%s)", def->ofs, strings + def->s_name);
    }
  }

  i = __strlen(line);
  for (; i < 16; i++)
    __strcat(line, " ");
  __strcat(line, " ");

  return line;
}

/*
 * fix: same as PR_PrintStatement
 *  mprintf("%4i : %4i : %s ", (int)(s - statements), statement_linenums[s - statements], pr_opcodes[s->op].opname);
 */
void DEC_PrintStatement(register dstatement_t * s)
{
  int i;

  mprintf("%4i : %s ", (int)(s - statements), pr_opcodes[s->op].opname);
  i = __strlen(pr_opcodes[s->op].opname);
  for (; i < 10; i++)
    mprintf(" ");

  if (s->op == OP_IF || s->op == OP_IFNOT)
    mprintf("%sbranch %i", DEC_GlobalString(s->a), s->b);
  else if (s->op == OP_GOTO) {
    mprintf("branch %i", s->a);
  }
  else if ((unsigned)(s->op - OP_STORE_F) < 6) {
    mprintf("%s", DEC_GlobalString(s->a));
    mprintf("%s", DEC_GlobalStringNoContents(s->b));
  }
  else {
    if (s->a)
      mprintf("%s", DEC_GlobalString(s->a));
    if (s->b)
      mprintf("%s", DEC_GlobalString(s->b));
    if (s->c)
      mprintf("%s", DEC_GlobalStringNoContents(s->c));
  }
  mprintf("\n");
}

/*
 * fix: same as PrintFunction
 */
void DEC_PrintFunction(register 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)
    eprintf("No function names \"%s\"", name);
  else {
    df = functions + i;

    mprintf("Statements for %s:\n", name);
    ds = statements + df->first_statement;
    while (1) {
      DEC_PrintStatement(ds);

      if (!ds->op)
	break;

      ds++;
    }
  }
}

/*=========================================================================== */
/*interface */
/*=========================================================================== */

bool qcc(FILE * srcFile, char *srcDir, operation procOper)
{
  char *srcData, *srcFree = 0;
  char *srcPart;
  char fileName[1024];
  void *freeIt = 0;
  bool failure = FALSE;

  if (InitData()) {
    switch (procOper) {
      case OP_ADD:
	__strcpy(sourcedir, srcDir);
	if (!(srcData = srcFree = (char *)GetVoidF(fileno(srcFile))))
	  break;

	if (!(srcData = COM_Parse(srcData))) {
	  eprintf("No destination fileName.\n");
	  tfree(srcFree);
	  srcFree = 0;
	  break;
	}

	__strcpy(destfile, com_token);
	mprintf("outputfile: %s\n", destfile);

	pr_dumpasm = FALSE;
	PR_BeginCompilation(freeIt = (void *)kmalloc(0x100000), 0x100000);

	/* compile all the files */
	do {
	  if (!(srcData = COM_Parse(srcData)))
	    break;

	  sprintf(fileName, "%s%s", sourcedir, com_token);
	  mprintf("compiling %s\n", fileName);
	  if (!(srcPart = (char *)GetPreProcessed(fileName))) {
	    eprintf(failed_fileopen, fileName);
	    failure = TRUE;
	    break;
	  }

	  if (!PR_CompileFile(srcPart, fileName)) {
	    eprintf("cannot compile %s\n", fileName);
	    tfree(srcPart);
	    srcPart = 0;
	    failure = TRUE;
	    break;
	  }
	  else {
	    tfree(srcPart);
	    srcPart = 0;
	  }
	} while (1);

	if (!failure) {
	  if (PR_FinishCompilation()) {
	    /* write progdefs.h */
	    sprintf(fileName, "%s%s", sourcedir, "progdefs.h");
	    /* write data file */
	    WriteData(PR_WriteProgdefs(fileName));
	    /* write files.dat */
	    WriteFiles();
	  }
	  else
	    eprintf("compilation errors\n");
	}
	else
	  eprintf("compilation errors\n");

	tfree(srcFree);
	srcFree = 0;
	break;
      default:
	break;
    }
    ExitData();
    return TRUE;
  }
  else
    return FALSE;
}

bool unqcc(HANDLE srcFile, char *destDir, operation procOper)
{
  bool retval = FALSE;

  switch (procOper) {
    case OP_EXTRACT:
      if (ReadData(srcFile)) {
	CreatePath(destDir);
	retval = DEC_DecompileFunctions(destDir);
      }
      else
	eprintf("decompilation error!\n");
      break;
    case OP_LIST:
    default:
      retval = ShowData(srcFile);
      break;
  }

  ExitData();
  return retval;
}
