mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-25 13:21:36 +00:00
ed63b7435e
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6322 fc73d0e0-1445-4013-8a0c-d673dee63da5
3391 lines
88 KiB
C
3391 lines
88 KiB
C
|
|
#include "qcc.h"
|
|
//#include "decomp.h"
|
|
|
|
//This file is derived from frikdec, but has some extra tweaks to make it more friendly with fte's compiler/opcodes (its not perfect though)
|
|
//FIXME: there's still a load of mallocs, so we don't allow this more than once per process.
|
|
//convert vector terms into floats when its revealed that they're using vector ops and not floats
|
|
//FIXME: fteqcc converts do {} while(1); into a goto -1; which is a really weeeird construct.
|
|
|
|
#if defined(_WIN32) || defined(__DJGPP__)
|
|
#include <malloc.h>
|
|
#else
|
|
#include <alloca.h>
|
|
#endif
|
|
|
|
#undef printf
|
|
#define printf GUIprintf
|
|
|
|
#define OP_MARK_END_DO 0x00010000 //do{
|
|
#define OP_MARK_END_ELSE 0x00000400 //}
|
|
|
|
#define MAX_REGS 65536
|
|
#define dstatement_t QCC_dstatement32_t
|
|
#define statements destatements
|
|
#define functions defunctions
|
|
#define strings destrings
|
|
static dstatement_t *statements;
|
|
static float *pr_globals;
|
|
static char *strings;
|
|
static QCC_ddef32_t *globals;
|
|
static dfunction_t *functions;
|
|
|
|
static int ofs_return;
|
|
static int ofs_parms[MAX_PARMS];
|
|
static int ofs_size = 3;
|
|
|
|
static QCC_ddef_t *globalofsdef[MAX_REGS];
|
|
|
|
|
|
//forward declarations.
|
|
QCC_ddef_t *GetField(const char *name);
|
|
|
|
#include <stdio.h>
|
|
|
|
/*int QC_snprintfz(char *buffer, size_t maxlen, const char *format, ...)
|
|
{
|
|
int p;
|
|
va_list argptr;
|
|
|
|
if (!maxlen)
|
|
return -1;
|
|
|
|
va_start (argptr, format);
|
|
p = _vsnprintf (buffer, maxlen, format,argptr);
|
|
va_end (argptr);
|
|
buffer[maxlen-1] = 0;
|
|
|
|
return p;
|
|
}*/
|
|
|
|
const char *GetString(dstring_t str)
|
|
{
|
|
if (str >= strofs)
|
|
return "INVALIDSTRING";
|
|
else
|
|
return strings+str;
|
|
}
|
|
|
|
extern QCC_opcode_t pr_opcodes [];
|
|
|
|
int endofsystemfields;
|
|
int debug_offs = 0;
|
|
int assumeglobals = 0; //unknown globals are assumed to be actual globals and NOT unlocked temps
|
|
int assumelocals = 0; //unknown locals are assumed to be actual locals and NOT locked temps
|
|
|
|
vfile_t *Decompileofile;
|
|
vfile_t *Decompileprogssrc;
|
|
vfile_t *Decompileprofile;
|
|
char **DecompileProfiles;//[MAX_FUNCTIONS];
|
|
static char **rettypes;//[MAX_FUNCTIONS];
|
|
|
|
extern int quakeforgeremap[];
|
|
|
|
char *type_names[] =
|
|
{
|
|
"void",
|
|
"string",
|
|
"float",
|
|
"vector",
|
|
"entity",
|
|
"ev_field",
|
|
"void()",
|
|
"ev_pointer",
|
|
"int",
|
|
"__variant",
|
|
"ev_struct",
|
|
"ev_union",
|
|
"ev_accessor",
|
|
"ev_quat",
|
|
"ev_uinteger"
|
|
};
|
|
const char *typetoname(QCC_type_t *type)
|
|
{
|
|
return type->name;
|
|
}
|
|
|
|
const char *temp_type (int temp, dstatement_t *start, dfunction_t *df)
|
|
{
|
|
int i;
|
|
dstatement_t *stat;
|
|
stat = start - 1;
|
|
// determine the type of a temp
|
|
|
|
while(stat > statements)
|
|
{
|
|
if (temp == stat->a)
|
|
return typetoname(*pr_opcodes[stat->op].type_a);
|
|
else if (temp == stat->b)
|
|
return typetoname(*pr_opcodes[stat->op].type_b);
|
|
else if (temp == stat->c)
|
|
return typetoname(*pr_opcodes[stat->op].type_c);
|
|
stat--;
|
|
}
|
|
|
|
// method 2
|
|
// find a call to this function
|
|
for (i = 0; i < numstatements; i++)
|
|
{
|
|
stat = &statements[i];
|
|
|
|
if (stat->op >= OP_CALL0 && stat->op <= OP_CALL8 && ((eval_t *)&pr_globals[stat->a])->function == df - functions)
|
|
{
|
|
for(i++; i < numstatements; i++)
|
|
{
|
|
stat = &statements[i];
|
|
if (ofs_return == stat->a && (*pr_opcodes[stat->op].type_a)->type != ev_void)
|
|
return type_names[(*pr_opcodes[stat->op].type_a)->type];
|
|
else if (ofs_return == stat->b && (*pr_opcodes[stat->op].type_b)->type != ev_void)
|
|
return type_names[(*pr_opcodes[stat->op].type_b)->type];
|
|
else if (stat->op == OP_DONE)
|
|
break;
|
|
else if (stat->op >= OP_CALL0 && stat->op <= OP_CALL8 && stat->a != df - functions)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("warning: Could not determine return type for %s\n", GetString(df->s_name));
|
|
|
|
return "float";
|
|
|
|
}
|
|
|
|
pbool IsConstant(QCC_ddef_t *def)
|
|
{
|
|
|
|
int i;
|
|
dstatement_t *d;
|
|
|
|
if (def->type & DEF_SAVEGLOBAL)
|
|
return false;
|
|
|
|
if (pr_globals[def->ofs] == 0)
|
|
return false;
|
|
|
|
for (i = 1; i < numstatements; i++)
|
|
{
|
|
d = &statements[i];
|
|
if (d->b == def->ofs)
|
|
{
|
|
if (pr_opcodes[d->op].associative == ASSOC_RIGHT)
|
|
{
|
|
if (d->op - OP_STORE_F < 6)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
char *type_name (QCC_ddef_t *def)
|
|
{
|
|
QCC_ddef_t *j;
|
|
|
|
switch(def->type&~DEF_SAVEGLOBAL)
|
|
{
|
|
case ev_field:
|
|
case ev_pointer:
|
|
j = GetField(GetString(def->s_name));
|
|
if (j)
|
|
return qcva(".%s",type_names[j->type]);
|
|
else
|
|
return type_names[def->type&~DEF_SAVEGLOBAL];
|
|
case ev_void:
|
|
case ev_string:
|
|
case ev_entity:
|
|
case ev_vector:
|
|
case ev_float:
|
|
return type_names[def->type&~DEF_SAVEGLOBAL];
|
|
case ev_function:
|
|
return "void()";
|
|
case ev_integer:
|
|
return "int";
|
|
// case ev_uinteger:
|
|
// return "unsigned";
|
|
// case ev_quat:
|
|
// return "quat";
|
|
default:
|
|
return "float";
|
|
}
|
|
};
|
|
|
|
extern int numstatements;
|
|
|
|
extern int numfunctions;
|
|
|
|
#define FILELISTSIZE 62
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
PR_String
|
|
|
|
Returns a string suitable for printing (no newlines, max 60 chars length)
|
|
===============
|
|
*/
|
|
const char *PR_String (const 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;
|
|
}
|
|
/*
|
|
============
|
|
PR_ValueString
|
|
|
|
Returns a string describing *data in a type specific manner
|
|
=============
|
|
*/
|
|
|
|
|
|
static char *PR_ValueString (etype_t type, void *val)
|
|
{
|
|
static char line[8192];
|
|
|
|
dfunction_t *f;
|
|
|
|
switch (type)
|
|
{
|
|
case ev_string:
|
|
QC_snprintfz(line, sizeof(line), "%s", PR_String(GetString(*(int *)val)));
|
|
break;
|
|
case ev_entity:
|
|
QC_snprintfz(line, sizeof(line), "entity %i", *(int *)val);
|
|
break;
|
|
case ev_function:
|
|
f = functions + *(int *)val;
|
|
if (!f)
|
|
QC_snprintfz(line, sizeof(line), "undefined function");
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "%s()", GetString(f->s_name));
|
|
break;
|
|
/*
|
|
case ev_field:
|
|
def = PR_DefForFieldOfs ( *(int *)val );
|
|
sprintf (line, ".%s", def->name);
|
|
break;
|
|
*/
|
|
case ev_void:
|
|
QC_snprintfz(line, sizeof(line), "void");
|
|
break;
|
|
case ev_float:
|
|
{
|
|
unsigned int high = *(unsigned int*)val & 0xff000000;
|
|
if (high == 0xff000000 || !high)
|
|
//FIXME this is probably a string or something, but we don't really know what type it is.
|
|
QC_snprintfz(line, sizeof(line), "(float)(__variant)%ii", *(int*)val);
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "%5.1f", *(float *)val);
|
|
}
|
|
break;
|
|
case ev_vector:
|
|
QC_snprintfz(line, sizeof(line), "'%5.1f %5.1f %5.1f'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]);
|
|
break;
|
|
case ev_pointer:
|
|
QC_snprintfz(line, sizeof(line), "pointer");
|
|
break;
|
|
case ev_field:
|
|
QC_snprintfz(line, sizeof(line), "<FIELD@%i>", *(int*)val);
|
|
break;
|
|
default:
|
|
QC_snprintfz(line, sizeof(line), "bad type %i", type);
|
|
break;
|
|
}
|
|
|
|
return line;
|
|
}
|
|
|
|
|
|
static char *filenames[] =
|
|
{
|
|
"makevectors", "defs.qc",
|
|
"button_wait", "buttons.qc",
|
|
"anglemod", "ai.qc",
|
|
"boss_face", "boss.qc",
|
|
"info_intermission", "client.qc",
|
|
"CanDamage", "combat.qc",
|
|
"demon1_stand1", "demon.qc",
|
|
"dog_bite", "dog.qc",
|
|
"door_blocked", "doors.qc",
|
|
"Laser_Touch", "enforcer.qc",
|
|
"knight_attack", "fight.qc",
|
|
"f_stand1", "fish.qc",
|
|
"hknight_shot", "hknight.qc",
|
|
"SUB_regen", "items.qc",
|
|
"knight_stand1", "knight.qc",
|
|
"info_null", "misc.qc",
|
|
"monster_use", "monsters.qc",
|
|
"OgreGrenadeExplode", "ogre.qc",
|
|
"old_idle1", "oldone.qc",
|
|
"plat_spawn_inside_trigger", "plats.qc",
|
|
"player_stand1", "player.qc",
|
|
"shal_stand", "shalrath.qc",
|
|
"sham_stand1", "shambler.qc",
|
|
"army_stand1", "soldier.qc",
|
|
"SUB_Null", "subs.qc",
|
|
"tbaby_stand1", "tarbaby.qc",
|
|
"trigger_reactivate", "triggers.qc",
|
|
"W_Precache", "weapons.qc",
|
|
"LaunchMissile", "wizard.qc",
|
|
"main", "world.qc",
|
|
"zombie_stand1", "zombie.qc"
|
|
};
|
|
|
|
//FIXME: parse fteextensions.qc instead, or something.
|
|
#define QW(x) //x,
|
|
static struct {
|
|
int num;
|
|
char *name; //purly for readability.
|
|
QCC_type_t **returns;
|
|
QCC_type_t **params[8];
|
|
char *text;
|
|
} builtins[]=
|
|
{
|
|
{0, NULL, NULL, {NULL}, NULL},
|
|
{1, "makevectors", NULL, {&type_vector}, "void (vector ang)"},
|
|
{2, "setorigin", NULL, {&type_entity, &type_vector}, "void (entity e, vector o)"},
|
|
{3, "setmodel", NULL, {&type_entity, &type_string}, "void (entity e, string m)"},
|
|
{4, "setsize", NULL, {&type_entity, &type_vector, &type_vector}, "void (entity e, vector min, vector max)"},
|
|
{5, NULL, NULL, {NULL}, NULL},
|
|
{6, NULL, NULL, {NULL}, "void ()"},
|
|
{7, "random", NULL, {NULL}, "float ()"},
|
|
{8, "sound", NULL, {&type_entity, &type_float, &type_string, &type_float, &type_float}, "void (entity e, float chan, string samp, float vol, float atten)"},
|
|
{9, "normalize", &type_vector, {&type_vector}, "vector (vector v)"},
|
|
{10, "error", NULL, {&type_string}, "void (string e)"},
|
|
{11, "objerror", NULL, {&type_string}, "void (string e)"},
|
|
{12, "vlen", &type_float, {&type_vector}, "float (vector v)"},
|
|
{13, "vectoyaw", &type_float, {&type_vector}, "float (vector v)"},
|
|
{14, "spawn", &type_entity, {NULL}, "entity ()"},
|
|
{15, "remove", NULL, {&type_entity}, "void (entity e)"},
|
|
{16, "traceline", NULL, {&type_vector, &type_vector, &type_float, &type_entity}, "void (vector v1, vector v2, float nomonsters, entity forent)"},
|
|
{17, NULL, NULL, {NULL}, "entity ()"},
|
|
{18, "find", &type_entity, {&type_entity, &type_field, &type_string}, "entity (entity start, .string fld, string match)"},
|
|
{19, "precache_sound", NULL, {&type_string}, "string (string s)"},
|
|
{20, "precache_model", NULL, {&type_string}, "string (string s)"},
|
|
{21, "stuffcmd", NULL, {&type_entity, &type_string}, "void (entity client, string s)"},
|
|
{22, "findradius", NULL, {&type_vector, &type_float}, "entity (vector org, float rad)"},
|
|
{23, "bprint", NULL, {QW(&type_float) &type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, "void (...)"},
|
|
{24, "sprint", NULL, {&type_entity, QW(&type_float) &type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, "void (...)"},
|
|
{25, "dprint", NULL, {&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, "void (...)"},
|
|
{26, "ftos", &type_string, {&type_float}, "string (float f)"},
|
|
{27, "vtos", &type_string, {&type_vector}, "string (vector v)"},
|
|
{28, "coredump", NULL, {NULL}, "void ()"},
|
|
{29, "traceon", NULL, {NULL}, "void ()"},
|
|
{30, "traceoff", NULL, {NULL}, "void ()"},
|
|
{31, "eprint", NULL, {&type_entity}, "void (entity e)"},
|
|
{32, "walkmove", &type_float, {&type_float, &type_float}, "float (float yaw, float dist)"},
|
|
{33, NULL, NULL, {NULL}, NULL},
|
|
{34, "droptofloor", NULL, {&type_float, &type_float}, "float ()"},
|
|
{35, "lightstyle", NULL, {&type_float, &type_string}, "void (float style, string value)"},
|
|
{36, "rint", &type_float, {&type_vector}, "float (float v)"},
|
|
{37, "floor", &type_float, {&type_vector}, "float (float v)"},
|
|
{38, "ceil", &type_float, {&type_vector}, "float (float v)"},
|
|
{39, NULL, NULL, {NULL}, NULL},
|
|
{40, "checkbottom", &type_float, {&type_entity}, "float (entity e)"},
|
|
{41, "pointcontents", &type_float, {&type_vector}, "float (vector v)"},
|
|
{42, NULL, NULL, {NULL}, NULL},
|
|
{43, "fabs", &type_float, {&type_float}, "float (float f)"},
|
|
{44, "aim", NULL, {&type_entity, &type_float}, "vector (entity e, float speed)"},
|
|
{45, "cvar", &type_string, {&type_string}, "float (string s)"},
|
|
{46, "localcmd", NULL, {&type_string}, "void (string s)"},
|
|
{47, "nextent", &type_entity, {&type_entity}, "entity (entity e)"},
|
|
{48, "", NULL, {&type_vector, &type_vector, &type_float, &type_float}, "void (vector o, vector d, float color, float count)"},
|
|
{49, "changeyaw", NULL, {NULL}, "void ()"},
|
|
{50, NULL, NULL, {NULL}, NULL},
|
|
{51, "vectoangles", &type_vector, {&type_vector}, "vector (vector v)"},
|
|
{52, "WriteByte", NULL, {&type_float, &type_float}, "void (float to, float f)"},
|
|
{53, "WriteChar", NULL, {&type_float, &type_float}, "void (float to, float f)"},
|
|
{54, "WriteShort", NULL, {&type_float, &type_float}, "void (float to, float f)"},
|
|
{55, "WriteLong", NULL, {&type_float, &type_float}, "void (float to, float f)"},
|
|
{56, "WriteCoord", NULL, {&type_float, &type_float}, "void (float to, float f)"},
|
|
{57, "WriteAngle", NULL, {&type_float, &type_float}, "void (float to, float f)"},
|
|
{58, "WriteString", NULL, {&type_float, &type_string}, "void (float to, string s)"},
|
|
{59, "WriteEntity", NULL, {&type_float, &type_entity}, "void (float to, entity s)"},
|
|
{60, NULL, NULL, {NULL}, NULL},
|
|
{61, NULL, NULL, {NULL}, NULL},
|
|
{62, NULL, NULL, {NULL}, NULL},
|
|
{63, NULL, NULL, {NULL}, NULL},
|
|
{64, NULL, NULL, {NULL}, NULL},
|
|
{65, NULL, NULL, {NULL}, NULL},
|
|
{66, NULL, NULL, {NULL}, NULL},
|
|
{67, "movetogoal", NULL, {&type_float}, "void (float step)"},
|
|
{68, "precache_file", NULL, {&type_string}, "string (string s)"},
|
|
{69, "makestatic", NULL, {&type_entity}, "void (entity e)"},
|
|
{70, "changelevel", NULL, {&type_string}, "void (string s)"},
|
|
{71, NULL, NULL, {NULL}, NULL},
|
|
{72, "cvar_set", NULL, {&type_string, &type_string}, "void (string var, string val)"},
|
|
{73, "centerprint", NULL, {&type_entity,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, "void (entity client, string s, ...)"},
|
|
{74, "ambientsound", NULL, {&type_vector, &type_string, &type_float, &type_float}, "void (vector pos, string samp, float vol, float atten)"},
|
|
{75, "precache_model2", NULL, {&type_string}, "string (string s)"},
|
|
{76, "precache_sound2", NULL, {&type_string}, "string (string s)"},
|
|
{77, "precache_file2", NULL, {&type_string}, "string (string s)"},
|
|
{78, "setspawnparms", NULL, {&type_entity}, "void (entity e)"},
|
|
|
|
//quakeworld specific
|
|
{79, "logfrag", NULL, {&type_entity, &type_entity}, "void(entity killer, entity killee)"},
|
|
{80, "infokey", &type_string, {&type_entity, &type_string}, "string(entity e, string key)"},
|
|
{81, "stof", &type_float, {&type_string}, "float(string s)"},
|
|
{82, "multicast", NULL, {&type_vector, &type_float}, "void(vector where, float set)"},
|
|
/*
|
|
//these are mvdsv specific
|
|
{83, "executecmd", NULL, {NULL}, NULL},
|
|
{84, "tokenize", NULL, {&type_string}, NULL},
|
|
{85, "argc", &type_float, {NULL}, "float()"},
|
|
{86, "argv", &type_string, {&type_float}, "string(float f)"},
|
|
{87, "teamfield", NULL, {NULL}, "void(.string fs)"},
|
|
{88, "substr", &type_string, {&type_string, &type_float, &type_float}, "string(string, float, float)"},
|
|
{89, "strcat", &type_string, {&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, "string (...)"},
|
|
{90, "strlen", &type_float, {&type_string}, "float(string s)"},
|
|
{91, "str2byte", &type_float, {&type_string}, "float(string s)"},
|
|
{92, NULL, NULL, {NULL}, NULL},
|
|
{93, "newstr", &type_string, {&type_string}, "string(...)"},
|
|
{94, "freestr", NULL, {&type_string}, "void(string s)"},
|
|
{95, "conprint", NULL, {NULL}, NULL},
|
|
{96, "readcmd", &type_string, {&type_string}, "string(string cmd)"},
|
|
{97, "strcpy", NULL, {NULL}, NULL},
|
|
{98, "strstr", &type_string, {&type_string, &type_string}, "string(string str, string sub)"},
|
|
{99, "strncpy", NULL, {NULL}, NULL},
|
|
{100, "log", NULL, {NULL}, NULL},
|
|
{101, "redirectcmd", NULL, {NULL}, NULL},
|
|
{102, "calltimeofday", NULL, {NULL}, NULL},
|
|
{103, "forcedemoframe", NULL, {NULL}, NULL},
|
|
*/
|
|
|
|
//some QSG extensions
|
|
|
|
{83, NULL, NULL, {NULL}, NULL},
|
|
{84, NULL, NULL, {NULL}, NULL},
|
|
{85, NULL, NULL, {NULL}, NULL},
|
|
{86, NULL, NULL, {NULL}, NULL},
|
|
{87, NULL, NULL, {NULL}, NULL},
|
|
{88, NULL, NULL, {NULL}, NULL},
|
|
{89, NULL, NULL, {NULL}, NULL},
|
|
{90, "tracebox", NULL, {&type_vector, &type_vector, &type_vector, &type_vector, &type_float, &type_entity}, "void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent)"},
|
|
{91, "randomvec", &type_vector, {NULL}, "vector()"},
|
|
{92, "getlight", &type_vector, {&type_vector}, "vector(vector org)"},
|
|
{93, "registercvar", &type_float, {&type_string, &type_string}, "float(string cvarname, string defaultvalue)"},
|
|
{94, "min", &type_float, {&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float},"float(float a, float b, ...)"},
|
|
{95, "max", &type_float, {&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float},"float(float a, float b, ...)"},
|
|
{96, "bound", &type_float, {&type_float,&type_float,&type_float}, "float(float minimum, float val, float maximum)"},
|
|
{97, "pow", &type_float, {&type_float,&type_float}, "float(float value, float exp)"},
|
|
{98, "findfloat", &type_entity, {&type_entity,&type_field,&type_float}, "entity(entity start, .__variant fld, __variant match)"},
|
|
|
|
{99, "checkextension",&type_float, {&type_string}, "float(string extname)"},
|
|
{100, "builtin_find", &type_float, {&type_string}, "float(string builtinname)"},
|
|
{101, "redirectcmd", NULL, {&type_entity,&type_string}, "void(entity to, string str)"},
|
|
{102, "anglemod", &type_float, {&type_float}, "float(float value)"},
|
|
{103, "cvar_string", &type_string, {&type_string}, "string(string cvarname)"},
|
|
|
|
{104, "showpic", NULL, {NULL}, "void(string slot, string picname, float x, float y, float zone, optional entity player)"},
|
|
{105, "hidepic", NULL, {NULL}, "void(string slot, optional entity player)"},
|
|
{106, "movepic", NULL, {NULL}, "void(string slot, float x, float y, float zone, optional entity player)"},
|
|
{107, "changepic", NULL, {NULL}, "void(string slot, string picname, optional entity player)"},
|
|
{108, "showpicent", NULL, {NULL}, "void(string slot, entity player)"},
|
|
{109, "hidepicent", NULL, {NULL}, "void(string slot, entity player)"},
|
|
|
|
{110, "fopen", &type_float, {&type_string,&type_float}, "float(string filename, float mode, optional float mmapminsize)"},
|
|
{111, "fclose", NULL, {&type_float}, "void(float fhandle)"},
|
|
{112, "fgets", &type_string, {&type_float,&type_string}, "string(float fhandle)"},
|
|
{113, "fputs", NULL, {&type_float,&type_string}, "void(float fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)"},
|
|
{114, "strlen", &type_float, {&type_string}, "float(string s)"},
|
|
{115, "strcat", &type_string, {&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string},"string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)"},
|
|
{116, "substring", &type_string, {&type_string,&type_float,&type_float}, "string(string s, float start, float length)"},
|
|
{117, "stov", &type_vector, {&type_string}, "vector(string s)"},
|
|
{118, "strzone", &type_string, {&type_string}, "string(string s, ...)"},
|
|
{119, "strunzone", NULL, {&type_string}, "void(string s)"},
|
|
};
|
|
|
|
char *DecompileValueString(etype_t type, void *val);
|
|
QCC_ddef_t *DecompileGetParameter(gofs_t ofs);
|
|
QCC_ddef_t *DecompileFindGlobal(const char *name);
|
|
char *DecompilePrintParameter(QCC_ddef_t * def);
|
|
QCC_ddef_t *DecompileFunctionGlobal(int funcnum);
|
|
|
|
char *ReadProgsCopyright(char *buf, size_t bufsize)
|
|
{
|
|
char *copyright, *e;
|
|
dprograms_t *progs = (dprograms_t*)buf;
|
|
int lowest = progs->ofs_statements;
|
|
lowest = min(lowest, progs->ofs_globaldefs);
|
|
lowest = min(lowest, progs->ofs_fielddefs);
|
|
lowest = min(lowest, progs->ofs_functions);
|
|
lowest = min(lowest, progs->ofs_strings);
|
|
lowest = min(lowest, progs->ofs_globals);
|
|
lowest = min(lowest, progs->ofs_fielddefs);
|
|
|
|
copyright = (char*)(progs+1);
|
|
if (!strncmp("\r\n\r\n", copyright, 4))
|
|
{
|
|
copyright += 4;
|
|
e = copyright+strlen(copyright)+1;
|
|
if (e && !strncmp(e, "\r\n\r\n", 4))
|
|
{
|
|
if (e+4 <= buf+lowest)
|
|
{
|
|
return copyright;
|
|
}
|
|
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int DecompileReadData(char *srcfilename, char *buf, size_t bufsize)
|
|
{
|
|
dprograms_t progs;
|
|
int i, j;
|
|
void *p;
|
|
char name[1024];
|
|
QCC_ddef_t *fd;
|
|
|
|
int stsz = 16, defsz=16;
|
|
// int quakeforge = false;
|
|
|
|
memcpy(&progs, buf, sizeof(progs));
|
|
|
|
if (progs.version == PROG_QTESTVERSION)
|
|
{
|
|
defsz = -32; //ddefs_t is 32bit
|
|
stsz = -16; //statements is mostly 16bit. there's some line numbers in there too.
|
|
}
|
|
else if (progs.version == PROG_VERSION)
|
|
stsz = defsz = 16;
|
|
else if (progs.version == 7)
|
|
{
|
|
if (progs.secondaryversion == PROG_SECONDARYVERSION16)
|
|
{
|
|
//regular 16bit progs, just an extended instruction set probably.
|
|
stsz = defsz = 16;
|
|
}
|
|
else if (progs.secondaryversion == PROG_SECONDARYVERSION32)
|
|
{
|
|
//32bit fte progs. everything is 32bit.
|
|
stsz = defsz = 32;
|
|
}
|
|
else
|
|
{
|
|
//progs is kk7 (certain QW TF mods). defs are 16bit but statements are 32bit. so this is unusable for saved games.
|
|
stsz = 32;
|
|
defsz = 16; //gah! fucked!
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stsz = defsz = 16;
|
|
// quakeforge = true;
|
|
}
|
|
|
|
strings = buf + progs.ofs_strings;
|
|
strofs = progs.numstrings;
|
|
|
|
numstatements = progs.numstatements;
|
|
|
|
// if (numstatements > MAX_STATEMENTS)
|
|
// Sys_Error("Too many statements");
|
|
if (stsz == 32)
|
|
statements = (dstatement32_t*)(buf+progs.ofs_statements);
|
|
else if (stsz == 16)
|
|
{ //need to expand the statements to 32bit.
|
|
const dstatement16_t *statements6 = (const dstatement16_t*)(buf+progs.ofs_statements);
|
|
statements = malloc(numstatements * sizeof(*statements));
|
|
for (i = 0; i < numstatements; i++)
|
|
{
|
|
statements[i].op = statements6[i].op;
|
|
|
|
if (statements[i].op == OP_GOTO)
|
|
statements[i].a = (signed short)statements6[i].a;
|
|
else
|
|
statements[i].a = (unsigned short)statements6[i].a;
|
|
|
|
if (statements[i].op == OP_IF_I || statements[i].op == OP_IFNOT_I ||
|
|
statements[i].op == OP_IF_F || statements[i].op == OP_IFNOT_F ||
|
|
statements[i].op == OP_IF_S || statements[i].op == OP_IFNOT_S)
|
|
statements[i].b = (signed short)statements6[i].b;
|
|
else
|
|
statements[i].b = (unsigned short)statements6[i].b;
|
|
|
|
statements[i].c = (unsigned short)statements6[i].c;
|
|
}
|
|
}
|
|
else if (stsz == -16)
|
|
{
|
|
const qtest_statement_t *statements3 = (const qtest_statement_t*)(buf+progs.ofs_statements);
|
|
statements = malloc(numstatements * sizeof(*statements));
|
|
for (i = 0; i < numstatements; i++)
|
|
{
|
|
statements[i].op = statements3[i].op;
|
|
|
|
if (statements[i].op == OP_GOTO)
|
|
statements[i].a = (signed short)statements3[i].a;
|
|
else
|
|
statements[i].a = (unsigned short)statements3[i].a;
|
|
|
|
if (statements[i].op == OP_IF_I || statements[i].op == OP_IFNOT_I ||
|
|
statements[i].op == OP_IF_F || statements[i].op == OP_IFNOT_F ||
|
|
statements[i].op == OP_IF_S || statements[i].op == OP_IFNOT_S)
|
|
statements[i].b = (signed short)statements3[i].b;
|
|
else
|
|
statements[i].b = (unsigned short)statements3[i].b;
|
|
|
|
statements[i].c = (unsigned short)statements3[i].c;
|
|
}
|
|
}
|
|
else
|
|
externs->Sys_Error("Unrecognised progs version");
|
|
|
|
numfunctions = progs.numfunctions;
|
|
functions = (dfunction_t*)(buf+progs.ofs_functions);
|
|
DecompileProfiles = calloc(numfunctions, sizeof(*DecompileProfiles));
|
|
rettypes = calloc(numfunctions, sizeof(*rettypes));
|
|
|
|
numglobaldefs = progs.numglobaldefs;
|
|
numfielddefs = progs.numfielddefs;
|
|
if (defsz == 16)
|
|
{
|
|
const QCC_ddef16_t *gd16 = (const QCC_ddef16_t*)(buf+progs.ofs_globaldefs);
|
|
globals = malloc(numglobaldefs * sizeof(*globals));
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
globals[i].ofs = gd16[i].ofs;
|
|
globals[i].s_name = gd16[i].s_name;
|
|
globals[i].type = gd16[i].type;
|
|
}
|
|
|
|
|
|
gd16 = (const QCC_ddef16_t*)(buf+progs.ofs_fielddefs);
|
|
fields = malloc(numfielddefs * sizeof(*fields));
|
|
for (i = 0; i < numfielddefs; i++)
|
|
{
|
|
fields[i].ofs = gd16[i].ofs;
|
|
fields[i].s_name = gd16[i].s_name;
|
|
fields[i].type = gd16[i].type;
|
|
}
|
|
}
|
|
else if (defsz == -32)
|
|
{
|
|
const qtest_function_t *funcin = (const qtest_function_t*)(buf+progs.ofs_functions);
|
|
const qtest_def_t *gdqt = (const qtest_def_t*)(buf+progs.ofs_globaldefs);
|
|
globals = malloc(numglobaldefs * sizeof(*globals));
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
globals[i].ofs = gdqt[i].ofs;
|
|
globals[i].s_name = gdqt[i].s_name;
|
|
globals[i].type = gdqt[i].type;
|
|
}
|
|
|
|
|
|
gdqt = (const qtest_def_t*)(buf+progs.ofs_fielddefs);
|
|
fields = malloc(numfielddefs * sizeof(*fields));
|
|
for (i = 0; i < numfielddefs; i++)
|
|
{
|
|
fields[i].ofs = gdqt[i].ofs;
|
|
fields[i].s_name = gdqt[i].s_name;
|
|
fields[i].type = gdqt[i].type;
|
|
}
|
|
|
|
functions = malloc(numfunctions * sizeof(*functions));
|
|
for (i = 0; i < numfunctions; i++)
|
|
{
|
|
functions[i].first_statement = funcin[i].first_statement; // negative numbers are builtins
|
|
functions[i].parm_start = funcin[i].parm_start;
|
|
functions[i].locals = funcin[i].locals; // total ints of parms + locals
|
|
|
|
functions[i].profile = funcin[i].profile; // runtime
|
|
|
|
functions[i].s_name = funcin[i].s_name;
|
|
functions[i].s_file = funcin[i].s_file; // source file defined in
|
|
|
|
functions[i].numparms = funcin[i].numparms;
|
|
for (j = 0; j < MAX_PARMS; j++)
|
|
functions[i].parm_size[j] = funcin[i].parm_size[j];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
globals = (QCC_ddef32_t*)(buf+progs.ofs_globaldefs);
|
|
fields = (QCC_ddef32_t*)(buf+progs.ofs_fielddefs);
|
|
}
|
|
|
|
pr_globals = (float*)(buf+progs.ofs_globals);
|
|
numpr_globals = progs.numglobals;
|
|
|
|
printf("Decompiling...\n");
|
|
printf("Read Data from %s:\n", srcfilename);
|
|
printf("Total Size is %6i\n", bufsize);
|
|
printf("Version Code is %i\n", progs.version);
|
|
printf("CRC is %i\n", 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");
|
|
|
|
if (numpr_globals > MAX_REGS)
|
|
{
|
|
printf("fatal error: progs exceeds a limit\n");
|
|
exit(1);
|
|
}
|
|
|
|
ofs_return = OFS_RETURN;
|
|
for (i = 0; i < 8; i++)
|
|
ofs_parms[i] = OFS_PARM0 + i * 3;
|
|
ofs_size = 3;
|
|
|
|
/*
|
|
if (quakeforge)
|
|
{
|
|
int typeremap[] = {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_quat, ev_integer, ev_uinteger};
|
|
for (i = 1; i < numglobaldefs; i++)
|
|
{
|
|
globals[i].type = (globals[i].type & DEF_SAVEGLOBGAL) | typeremap[globals[i].type&~DEF_SAVEGLOBGAL];
|
|
}
|
|
for (i = 1; i < numfielddefs; i++)
|
|
{
|
|
fields[i].type = (fields[i].type & DEF_SAVEGLOBGAL) | typeremap[fields[i].type&~DEF_SAVEGLOBGAL];
|
|
}
|
|
|
|
for (i = 1; i < numstatements; i++)
|
|
{
|
|
if (statements[i].op >= OP_H2_FIRST)// && statements[i].op <= OP_H2_FIRST+sizeof(quakeforgeremap)/sizeof(quakeforgeremap[0]))
|
|
statements[i].op = quakeforgeremap[statements[i].op-OP_H2_FIRST];
|
|
}
|
|
|
|
fd = DecompileFindGlobal(".zero");
|
|
if (fd)
|
|
fd->ofs = -1;
|
|
|
|
fd = DecompileFindGlobal(".return");
|
|
if (fd)
|
|
{
|
|
ofs_return = fd->ofs;
|
|
fd->ofs = -1;
|
|
}
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
QC_snprintfz(name, sizeof(name), ".param_%i", i);
|
|
fd = DecompileFindGlobal(name);
|
|
if (fd)
|
|
{
|
|
ofs_parms[i] = fd->ofs;
|
|
fd->ofs = -1;
|
|
}
|
|
}
|
|
|
|
fd = DecompileFindGlobal(".param_size");
|
|
if (fd)
|
|
ofs_size = ((int*)pr_globals)[fd->ofs];
|
|
}
|
|
*/
|
|
|
|
//fix up the globaldefs
|
|
for (i = 1; i < numglobaldefs; i++)
|
|
{
|
|
if (globals[i].ofs < RESERVED_OFS)
|
|
globals[i].ofs += numpr_globals;
|
|
}
|
|
// fix up the functions
|
|
for (i = 1; i < numfunctions; i++)
|
|
{
|
|
if ((unsigned)functions[i].s_name >= (unsigned)strofs || strlen(GetString(functions[i].s_name)) <= 0)
|
|
{
|
|
fd = DecompileFunctionGlobal(i);
|
|
if (fd)
|
|
{
|
|
functions[i].s_name = fd->s_name;
|
|
continue;
|
|
}
|
|
QC_snprintfz(name, sizeof(name), "function%i", i);
|
|
name[strlen(name)] = 0;
|
|
p = malloc(strlen(name + 1));
|
|
strcpy(p, name);
|
|
functions[i].s_name = (char *)p - strings;
|
|
}
|
|
if (functions[i].first_statement > 0 && !functions[i].locals && functions[i].numparms)
|
|
{ //vanilla qcc apparently had a bug
|
|
for (j = 0; j < functions[i].numparms; j++)
|
|
functions[i].locals += functions[i].parm_size[j];
|
|
}
|
|
}
|
|
|
|
return progs.version;
|
|
}
|
|
|
|
int
|
|
DecompileGetFunctionIdxByName(const char *name)
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 1; i < numfunctions; i++)
|
|
if (!strcmp(name, GetString(functions[i].s_name)))
|
|
{
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const etype_t DecompileGetFieldTypeByDef(QCC_ddef_t *def)
|
|
{
|
|
int i;
|
|
int ofs = ((int*)pr_globals)[def->ofs];
|
|
|
|
for (i = 1; i < numfielddefs; i++)
|
|
if (fields[i].ofs == ofs)
|
|
{
|
|
if (!strcmp(GetString(def->s_name), GetString(fields[i].s_name)))
|
|
return fields[i].type;
|
|
}
|
|
return ev_void;
|
|
}
|
|
const char *DecompileGetFieldNameIdxByFinalOffset(int ofs)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i < numfielddefs; i++)
|
|
if (fields[i].ofs == ofs)
|
|
{
|
|
return GetString(fields[i].s_name);
|
|
}
|
|
return "UNKNOWN FIELD";
|
|
}
|
|
void DecompileGetFieldNameIdxByFinalOffset2(char *out, size_t outsize, int ofs)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i < numfielddefs; i++)
|
|
{
|
|
if (fields[i].ofs == ofs)
|
|
{
|
|
QC_snprintfz(out, outsize, "%s", GetString(fields[i].s_name));
|
|
return;
|
|
}
|
|
else if (fields[i].type == ev_vector && fields[i].ofs+1 == ofs)
|
|
{
|
|
QC_snprintfz(out, outsize, "%s_y", GetString(fields[i].s_name));
|
|
return;
|
|
}
|
|
else if (fields[i].type == ev_vector && fields[i].ofs+2 == ofs)
|
|
{
|
|
QC_snprintfz(out, outsize, "%s_z", GetString(fields[i].s_name));
|
|
return;
|
|
}
|
|
}
|
|
QC_snprintfz(out, outsize, "<FIELD@%i>", ofs);
|
|
}
|
|
|
|
int
|
|
DecompileAlreadySeen(char *fname, vfile_t **rfile)
|
|
{
|
|
int ret = 1;
|
|
|
|
vfile_t *file;
|
|
|
|
file = QCC_FindVFile(fname);
|
|
if (!file)
|
|
{
|
|
ret = 0;
|
|
if (rfile)
|
|
{
|
|
char *header = "//Decompiled code. Please respect the original copyright.\n";
|
|
*rfile = QCC_AddVFile(fname, header, strlen(header));
|
|
AddSourceFile("progs.src", fname);
|
|
}
|
|
}
|
|
else if (rfile)
|
|
*rfile = file;
|
|
|
|
return ret;
|
|
}
|
|
|
|
char *DecompileReturnType(dfunction_t *df);
|
|
|
|
char *DecompileAgressiveType(dfunction_t *df, dstatement_t *last, gofs_t ofs)
|
|
{
|
|
QCC_ddef_t *par;
|
|
par = DecompileGetParameter(ofs);
|
|
if (par) //single = intended
|
|
{
|
|
return type_name(par);
|
|
}
|
|
|
|
if (ofs == ofs_return && ((last->op >= OP_CALL0 && last->op <= OP_CALL8) || (last->op >= OP_CALL1H && last->op <= OP_CALL8H)))
|
|
{ //offset is a return value, go look at the called function's return type.
|
|
return DecompileReturnType(functions + ((int*)pr_globals)[last->a]);
|
|
}
|
|
|
|
while(last >= &statements[df->first_statement])
|
|
{
|
|
if (last->c == ofs &&
|
|
pr_opcodes[last->op].associative == ASSOC_LEFT &&
|
|
pr_opcodes[last->op].priorityclass)
|
|
{
|
|
//previous was an operation into the temp
|
|
return type_names[(*pr_opcodes[last->op].type_c)->type];
|
|
|
|
|
|
// sprintf(fname, "%s ", temp_type6(rds->a, rds, df));
|
|
}
|
|
last--;
|
|
}
|
|
|
|
return NULL; //got to start of function... shouldn't really happen.
|
|
}
|
|
|
|
static unsigned int DecompileBuiltin(dfunction_t *df)
|
|
{
|
|
unsigned int bi, i;
|
|
if (df->first_statement > 0)
|
|
return 0; //not a builtin.
|
|
bi = -df->first_statement;
|
|
//okay, so this is kinda screwy, different mods have different sets of builtins, and a load of fte's are #0 too
|
|
//so just try to match by name first... lots of scanning. :(
|
|
if (df->s_name>0 && df->s_name < strofs)
|
|
{
|
|
const char *biname = GetString(df->s_name);
|
|
for (i = 0; i < (sizeof(builtins)/sizeof(builtins[0])); i++)
|
|
{
|
|
if (!builtins[i].name)
|
|
continue;
|
|
if (!strcmp(builtins[i].name, biname))
|
|
{ //okay, this one matched.
|
|
bi = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bi >= (sizeof(builtins)/sizeof(builtins[0])))
|
|
return 0; //unknown.
|
|
return bi;
|
|
}
|
|
|
|
char *DecompileReturnType(dfunction_t *df)
|
|
{
|
|
dstatement_t *ds;
|
|
unsigned short dom;
|
|
pbool foundret = false;
|
|
static int recursion;
|
|
char *ret = NULL; //return null if we don't know.
|
|
int couldbeastring = true;
|
|
|
|
if (df->first_statement <= 0)
|
|
{
|
|
unsigned int bi = DecompileBuiltin(df);
|
|
if (bi)
|
|
if (builtins[bi].returns)
|
|
return type_names[(*builtins[bi].returns)->type];
|
|
|
|
return "void"; //no returns statements found
|
|
}
|
|
|
|
if (rettypes[df - functions])
|
|
return rettypes[df - functions];
|
|
|
|
recursion++;
|
|
|
|
ds = statements + df->first_statement;
|
|
|
|
/*
|
|
* find a return statement, to determine the result type
|
|
*/
|
|
|
|
while (1)
|
|
{
|
|
dom = (ds->op) % OP_MARK_END_ELSE;
|
|
if (!dom)
|
|
break;
|
|
// if (dom == OPQF_RETURN_V)
|
|
// break;
|
|
if (dom == OP_RETURN)
|
|
{
|
|
if (ds->a != 0) //some code is buggy.
|
|
{
|
|
foundret = true;
|
|
|
|
if (recursion < 10)
|
|
{
|
|
ret = DecompileAgressiveType(df, ds-1, ds->a);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
if (((int*)pr_globals)[ds->a] < 0 && ((int*)pr_globals)[ds->a] >= strofs)
|
|
couldbeastring = false; //definatly not
|
|
else
|
|
{
|
|
char buf[64];
|
|
QC_snprintfz(buf, sizeof(buf), "%f", pr_globals[ds->a]);
|
|
if (strcmp(buf, "0.000000"))
|
|
couldbeastring = false; //doesn't fit the profile
|
|
}
|
|
}
|
|
|
|
}
|
|
ds++;
|
|
}
|
|
recursion--;
|
|
|
|
if (foundret)
|
|
{
|
|
if (!ret)
|
|
{
|
|
if (couldbeastring)
|
|
ret = "string /*WARNING: could not determine return type*/";
|
|
else
|
|
ret = "float /*WARNING: could not determine return type*/";
|
|
}
|
|
}
|
|
else
|
|
ret = "void"; //no returns statements found
|
|
|
|
rettypes[df - functions] = ret;
|
|
return ret;
|
|
}
|
|
|
|
void DecompileCalcProfiles(void)
|
|
{
|
|
|
|
int i, ps;
|
|
gofs_t j;
|
|
char *knew;
|
|
static char fname[512];
|
|
static char line[512];
|
|
dfunction_t *df;
|
|
QCC_ddef_t *par;
|
|
|
|
for (i = 1; i < numfunctions; i++)
|
|
{
|
|
|
|
df = functions + i;
|
|
fname[0] = '\0';
|
|
line[0] = '\0';
|
|
DecompileProfiles[i] = NULL;
|
|
|
|
if (df->first_statement <= 0)
|
|
{
|
|
unsigned int bi = DecompileBuiltin(df);
|
|
if (bi && builtins[bi].text)
|
|
QC_snprintfz(fname, sizeof(fname), "%s %s", builtins[bi].text, GetString(functions[i].s_name));
|
|
else
|
|
{
|
|
QC_snprintfz(fname, sizeof(fname), "__variant(...) %s", GetString(functions[i].s_name));
|
|
printf("warning: unknown builtin %s\n", GetString(functions[i].s_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char *rettype;
|
|
|
|
rettype = DecompileReturnType(df);
|
|
if (!rettype)
|
|
{ //but we do know that it's not void
|
|
rettype = "float /*WARNING: could not determine return type*/";
|
|
}
|
|
strcpy(fname, rettype);
|
|
strcat(fname, "(");
|
|
|
|
/*
|
|
* determine overall parameter size
|
|
*/
|
|
|
|
for (j = 0, ps = 0; j < df->numparms; j++)
|
|
ps += df->parm_size[j];
|
|
|
|
if (ps > 0)
|
|
{
|
|
int p;
|
|
for (p = 0, j = df->parm_start; j < (df->parm_start) + ps; p++)
|
|
{
|
|
line[0] = '\0';
|
|
par = DecompileGetParameter(j);
|
|
if (!par)
|
|
par = DecompileGetParameter((short)j);
|
|
|
|
if (!par)
|
|
{
|
|
//Error("Error - No parameter names with offset %i.", j);
|
|
// printf("No parameter names with offset %i\n", j);
|
|
if (p<8)
|
|
j += df->parm_size[p];
|
|
else
|
|
j += 1;
|
|
if (p<8&&df->parm_size[p] == 3)
|
|
{
|
|
if (j < (df->parm_start) + ps)
|
|
QC_snprintfz(line, sizeof(line), "vector par%i, ", p);
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "vector par%i", p);
|
|
}
|
|
else
|
|
{
|
|
if (j < (df->parm_start) + ps)
|
|
QC_snprintfz(line, sizeof(line), "__variant par%i, ", p);
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "__variant par%i", p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (par->type == ev_vector)
|
|
j += 2;
|
|
j++;
|
|
if (j < (df->parm_start) + ps)
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%s, ", DecompilePrintParameter(par));
|
|
}
|
|
else
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%s", DecompilePrintParameter(par));
|
|
}
|
|
}
|
|
strcat(fname, line);
|
|
}
|
|
}
|
|
strcat(fname, ") ");
|
|
line[0] = '\0';
|
|
QC_snprintfz(line, sizeof(line), "%s", GetString(functions[i].s_name));
|
|
strcat(fname, line);
|
|
|
|
}
|
|
|
|
knew = (char *)malloc(strlen(fname) + 1);
|
|
strcpy(knew, fname);
|
|
DecompileProfiles[i] = knew;
|
|
}
|
|
|
|
}
|
|
|
|
QCC_ddef_t *GlobalAtOffset(dfunction_t *df, gofs_t ofs)
|
|
{
|
|
QCC_ddef_t *def;
|
|
int i, j;
|
|
|
|
def = globalofsdef[ofs];
|
|
if (def)
|
|
return def;
|
|
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
def = &globals[i];
|
|
|
|
if (def->ofs == ofs)
|
|
{
|
|
|
|
/*if (!GetString(def->s_name))
|
|
{
|
|
char line[16];
|
|
char *buf;
|
|
|
|
sprintf(line, "_s_%i", def->ofs); //globals, which are defined after the locals of the function they are first used in...
|
|
buf = malloc(strlen(line)+1); //must be static variables, but we can't handle them very well
|
|
strcpy(buf, line);
|
|
def->s_name = buf - strings;
|
|
}*/
|
|
globalofsdef[ofs] = def;
|
|
return def;
|
|
}
|
|
}
|
|
|
|
if (ofs >= df->parm_start && ofs < df->parm_start + df->locals)
|
|
{
|
|
static QCC_ddef_t parm[8];
|
|
static char *parmnames[] = {"par0","par1","par2","par3","par4","par5","par6","par7"};
|
|
int parmofs = ofs - df->parm_start;
|
|
for (i = 0; i < df->numparms && i < 8; i++)
|
|
{
|
|
if (parmofs < df->parm_size[i])
|
|
{
|
|
parm[i].ofs = ofs - parmofs;
|
|
parm[i].s_name = parmnames[i]-strings;
|
|
parm[i].type = ev_void;
|
|
|
|
ofs = parm[i].ofs;
|
|
for (j = 0; j < numglobaldefs; j++)
|
|
{
|
|
def = &globals[j];
|
|
if (def->ofs == ofs)
|
|
{
|
|
char line[256], *buf;
|
|
sprintf(line, "%s_%c", GetString(def->s_name), 'x'+parmofs); //globals, which are defined after the locals of the function they are first used in...
|
|
def = malloc(sizeof(*def)+strlen(line)+1); //must be static variables, but we can't handle them very well
|
|
buf = (char*)(def+1);
|
|
strcpy(buf, line);
|
|
def->s_name = buf - strings;
|
|
def->type = ev_float;
|
|
return def;
|
|
}
|
|
}
|
|
|
|
return &parm[i];
|
|
}
|
|
parmofs -= df->parm_size[i];
|
|
}
|
|
//moo
|
|
}
|
|
//FIXME: if its within the current function's bounds, its:
|
|
// within param list: argument
|
|
// never written: immediate
|
|
// optimised: a local / locked temp.
|
|
// vanilla qcc: always a temp (other locals will be named)
|
|
//elsewhere:
|
|
// if its assigned to somewhere, then its a temp
|
|
// otherwise its a const.
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *DecompileGlobal(dfunction_t *df, gofs_t ofs, QCC_type_t * req_t)
|
|
{
|
|
int i;
|
|
QCC_ddef_t *def;
|
|
static char line[8192];
|
|
char *res;
|
|
|
|
line[0] = '\0';
|
|
|
|
/*if (req_t == &def_short)
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%ii", ofs);
|
|
res = (char *)malloc(strlen(line) + 1);
|
|
strcpy(res, line);
|
|
return res;
|
|
}*/
|
|
|
|
def = GlobalAtOffset(df, ofs);
|
|
|
|
if (def)
|
|
{
|
|
const char *defname = GetString(def->s_name);
|
|
|
|
if (!strcmp(defname, "IMMEDIATE") || !strcmp(defname, ".imm") || !def->s_name)
|
|
{
|
|
etype_t ty;
|
|
if (!req_t)
|
|
ty = def->type;
|
|
else
|
|
{
|
|
ty = (etype_t)(req_t->type);
|
|
if (!ty)
|
|
ty = def->type;
|
|
}
|
|
QC_snprintfz(line, sizeof(line), "%s", DecompileValueString(ty, &pr_globals[def->ofs]));
|
|
}
|
|
else
|
|
{
|
|
if (!*defname)
|
|
{
|
|
char line[16];
|
|
char *buf;
|
|
QCC_ddef_t *parent;
|
|
if (ofs >= df->parm_start && ofs < df->parm_start + df->locals)
|
|
goto lookslikealocal;
|
|
else if ((parent = GlobalAtOffset(df, ofs-1)) && parent->type == ev_vector)
|
|
{ // _y
|
|
QC_snprintfz(line, sizeof(line), "%s_y", GetString(parent->s_name)); //globals, which are defined after the locals of the function they are first used in...
|
|
buf = malloc(strlen(line)+1); //must be static variables, but we can't handle them very well
|
|
strcpy(buf, line);
|
|
def->s_name = buf - strings;
|
|
}
|
|
else if ((parent = GlobalAtOffset(df, ofs-2)) && parent->type == ev_vector)
|
|
{ // _z
|
|
QC_snprintfz(line, sizeof(line), "%s_z", GetString(parent->s_name)); //globals, which are defined after the locals of the function they are first used in...
|
|
buf = malloc(strlen(line)+1); //must be static variables, but we can't handle them very well
|
|
strcpy(buf, line);
|
|
def->s_name = buf - strings;
|
|
}
|
|
else
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "_sloc_%i", def->ofs); //globals, which are defined after the locals of the function they are first used in...
|
|
buf = malloc(strlen(line)+1); //must be static variables, but we can't handle them very well
|
|
strcpy(buf, line);
|
|
def->s_name = buf - strings;
|
|
}
|
|
}
|
|
|
|
QC_snprintfz(line, sizeof(line), "%s", GetString(def->s_name));
|
|
if (def->type == ev_field && req_t == type_field && req_t->aux_type == type_float && DecompileGetFieldTypeByDef(def) == ev_vector)
|
|
strcat(line, "_x");
|
|
else if (def->type == ev_vector && req_t == type_float)
|
|
strcat(line, "_x");
|
|
|
|
}
|
|
res = (char *)malloc(strlen(line) + 1);
|
|
strcpy(res, line);
|
|
|
|
return res;
|
|
}
|
|
|
|
if (ofs >= df->parm_start && ofs < df->parm_start + df->locals)
|
|
{
|
|
int parmofs;
|
|
lookslikealocal:
|
|
QC_snprintfz(line, sizeof(line), "local_%i", ofs);
|
|
for (i = 0, parmofs = ofs - df->parm_start; i < df->numparms && i < 8; i++)
|
|
{
|
|
if (parmofs < df->parm_size[i])
|
|
{
|
|
if (parmofs)
|
|
QC_snprintfz(line, sizeof(line), "par%i_%c", i, 'x'+parmofs);
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "par%i", i);
|
|
break;
|
|
}
|
|
parmofs -= df->parm_size[i];
|
|
}
|
|
if (!assumelocals && i == df->numparms)
|
|
return NULL; //we don't know what this is. assume its a temp
|
|
|
|
res = (char *)malloc(strlen(line) + 1);
|
|
strcpy(res, line);
|
|
return res;
|
|
}
|
|
|
|
if (assumeglobals)
|
|
{ //unknown globals are normally assumed to be temps
|
|
if (ofs >= ofs_parms[7]+ofs_size)
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "tmp_%i", ofs);
|
|
res = (char *)malloc(strlen(line) + 1);
|
|
strcpy(res, line);
|
|
|
|
return res;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct
|
|
{
|
|
char *text;
|
|
QCC_type_t *type;
|
|
} IMMEDIATES[MAX_REGS];
|
|
gofs_t DecompileScaleIndex(dfunction_t *df, gofs_t ofs)
|
|
{
|
|
gofs_t nofs = 0;
|
|
|
|
/*if (ofs > ofs_parms[7]+ofs_size)
|
|
nofs = ofs - df->parm_start + ofs_parms[7]+ofs_size;
|
|
else*/
|
|
nofs = ofs;
|
|
|
|
if ((nofs < 0) || (nofs > MAX_REGS - 1))
|
|
{
|
|
printf("Fatal Error - Index (%i) out of bounds.\n", nofs);
|
|
return 0;
|
|
exit(1);
|
|
}
|
|
|
|
return nofs;
|
|
}
|
|
|
|
void DecompileImmediate_Free(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_REGS; i++)
|
|
{
|
|
if (IMMEDIATES[i].text)
|
|
{
|
|
free(IMMEDIATES[i].text);
|
|
IMMEDIATES[i].text = NULL;
|
|
}
|
|
}
|
|
}
|
|
void DecompileImmediate_Insert(dfunction_t *df, gofs_t ofs, char *knew, QCC_type_t *type)
|
|
{
|
|
QCC_ddef_t *d;
|
|
int nofs;
|
|
|
|
nofs = DecompileScaleIndex(df, ofs);
|
|
|
|
if (IMMEDIATES[nofs].text)
|
|
{
|
|
// fprintf(Decompileofile, "/*WARNING: Discarding \"%s\"/", IMMEDIATES[nofs]);
|
|
free(IMMEDIATES[nofs].text);
|
|
IMMEDIATES[nofs].text = NULL;
|
|
}
|
|
|
|
|
|
d = GlobalAtOffset(df, ofs);
|
|
if (d && d->s_name)// && strcmp(GetString(d->s_name), "IMMEDIATE"))
|
|
{ //every operator has a src (or two) and a dest.
|
|
//many compilers optimise by using the dest of a maths/logic operator to store to a local/global
|
|
//they then skip off the storeopcode.
|
|
//without this, we would never see these stores.
|
|
IMMEDIATES[nofs].text = NULL;
|
|
IMMEDIATES[nofs].type = NULL;
|
|
|
|
QCC_CatVFile(Decompileofile, "%s = %s;\n", GetString(d->s_name), knew);
|
|
}
|
|
else
|
|
{
|
|
IMMEDIATES[nofs].text = (char *)malloc(strlen(knew) + 1);
|
|
strcpy(IMMEDIATES[nofs].text, knew);
|
|
IMMEDIATES[nofs].type = type;
|
|
}
|
|
}
|
|
|
|
void FloatToString(char *out, size_t outsize, float f)
|
|
{
|
|
char *e;
|
|
QC_snprintfz(out, outsize, "%f", f);
|
|
|
|
//trim any trailing decimals
|
|
e = strchr(out, '.');
|
|
if (e)
|
|
{
|
|
e = e+strlen(e);
|
|
while (e > out && e[-1] == '0')
|
|
e--;
|
|
if (e > out && e[-1] == '.')
|
|
e--;
|
|
*e = 0;
|
|
}
|
|
}
|
|
|
|
char *DecompileImmediate_Get(dfunction_t *df, gofs_t ofs, QCC_type_t *req_t)
|
|
{
|
|
char *res;
|
|
|
|
gofs_t nofs;
|
|
|
|
nofs = DecompileScaleIndex(df, ofs);
|
|
// printf("DecompileImmediate - Index scale: %i -> %i.\n", ofs, nofs);
|
|
|
|
// insert at nofs
|
|
if (IMMEDIATES[nofs].text)
|
|
{
|
|
|
|
// printf("DecompileImmediate - Reading \"%s\" at index %i.\n", IMMEDIATES[nofs], nofs);
|
|
|
|
if (IMMEDIATES[nofs].type == type_vector && req_t == type_float)
|
|
{
|
|
res = (char *)malloc(strlen(IMMEDIATES[nofs].text) + 4);
|
|
if (strchr(IMMEDIATES[nofs].text, '('))
|
|
sprintf(res, "%s[0]", IMMEDIATES[nofs].text);
|
|
else
|
|
sprintf(res, "%s_x", IMMEDIATES[nofs].text);
|
|
}
|
|
else
|
|
{
|
|
res = (char *)malloc(strlen(IMMEDIATES[nofs].text) + 1);
|
|
strcpy(res, IMMEDIATES[nofs].text);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
else
|
|
{ //you are now entering the hack zone.
|
|
char temp[8192];
|
|
|
|
switch(req_t?req_t->type:-1)
|
|
{
|
|
case ev_void: //for lack of any better ideas.
|
|
case ev_float:
|
|
//denormalised floats need special handling.
|
|
if ((0x7fffffff&*(int*)&pr_globals[ofs]) >= 1 && (0x7fffffff&*(int*)&pr_globals[ofs]) < 0x00800000)
|
|
{
|
|
QC_snprintfz(temp, sizeof(temp), "((float)(__variant)%ii)", *(int*)&pr_globals[ofs]);
|
|
|
|
// if (req_t && *(int*)&pr_globals[ofs] >= 1 && *(int*)&pr_globals[ofs] < strofs)
|
|
// ; //failure to break means we'll print out a trailing /*string*/
|
|
// else
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
FloatToString(temp, sizeof(temp), pr_globals[ofs]);
|
|
break;
|
|
}
|
|
case ev_string:
|
|
{
|
|
const char *in;
|
|
char *out;
|
|
if (((int*)pr_globals)[ofs] < 0 || ((int*)pr_globals)[ofs] > strofs)
|
|
{
|
|
printf("Hey! That's not a string! error in %s\n", GetString(df->s_name));
|
|
QC_snprintfz(temp, sizeof(temp), "%f", pr_globals[ofs]);
|
|
break;
|
|
}
|
|
in = GetString(((int*)pr_globals)[ofs]);
|
|
out = temp;
|
|
if (req_t->type != ev_string)
|
|
{
|
|
QC_snprintfz(temp, sizeof(temp), "/*%i*/", ((int*)pr_globals)[ofs]);
|
|
out += strlen(out);
|
|
}
|
|
|
|
*out++ = '\"';
|
|
while (*in)
|
|
{
|
|
if (*in == '\"')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = '\"';
|
|
in++;
|
|
}
|
|
else if (*in == '\n')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 'n';
|
|
in++;
|
|
}
|
|
else if (*in == '\\')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = '\\';
|
|
in++;
|
|
}
|
|
else if (*in == '\r')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 'r';
|
|
in++;
|
|
}
|
|
else if (*in == '\a')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 'a';
|
|
in++;
|
|
}
|
|
else if (*in == '\b')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 'b';
|
|
in++;
|
|
}
|
|
else if (*in == '\f')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 'f';
|
|
in++;
|
|
}
|
|
else if (*in == '\t')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 't';
|
|
in++;
|
|
}
|
|
else if (*in == '\v')
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 'v';
|
|
in++;
|
|
}
|
|
else
|
|
*out++ = *in++;
|
|
}
|
|
*out++ = '\"';
|
|
*out++ = '\0';
|
|
}
|
|
break;
|
|
case ev_vector:
|
|
{
|
|
char x[64], y[64], z[64];
|
|
FloatToString(x, sizeof(x), pr_globals[ofs+0]);
|
|
FloatToString(y, sizeof(y), pr_globals[ofs+1]);
|
|
FloatToString(z, sizeof(z), pr_globals[ofs+2]);
|
|
QC_snprintfz(temp, sizeof(temp), "\'%s %s %s\'", x, y, z);
|
|
}
|
|
break;
|
|
// case ev_quat:
|
|
// QC_snprintfz(temp, sizeof(temp), "\'%f %f %f %f\'", pr_globals[ofs],pr_globals[ofs+1],pr_globals[ofs+2],pr_globals[ofs+3]);
|
|
// break;
|
|
case ev_integer:
|
|
QC_snprintfz(temp, sizeof(temp), "%ii", ((int*)pr_globals)[ofs]);
|
|
break;
|
|
// case ev_uinteger:
|
|
// QC_snprintfz(temp, sizeof(temp), "%uu", ((int*)pr_globals)[ofs]);
|
|
// break;
|
|
case ev_pointer:
|
|
QC_snprintfz(temp, sizeof(temp), "(__variant*)0x%xi", ((int*)pr_globals)[ofs]);
|
|
break;
|
|
case ev_function:
|
|
if (!((int*)pr_globals)[ofs])
|
|
QC_snprintfz(temp, sizeof(temp), "__NULL__/*func*/");
|
|
else if (((int*)pr_globals)[ofs] > 0 && ((int*)pr_globals)[ofs] < numfunctions && functions[((int*)pr_globals)[ofs]].s_name>0)
|
|
QC_snprintfz(temp, sizeof(temp), "%s/*immediate*/", GetString(functions[((int*)pr_globals)[ofs]].s_name));
|
|
else
|
|
QC_snprintfz(temp, sizeof(temp), "((__variant(...))%i)", ((int*)pr_globals)[ofs]);
|
|
break;
|
|
case ev_entity:
|
|
if (!pr_globals[ofs])
|
|
QC_snprintfz(temp, sizeof(temp), "((entity)__NULL__)");
|
|
else
|
|
QC_snprintfz(temp, sizeof(temp), "(entity)%i", ((int*)pr_globals)[ofs]);
|
|
break;
|
|
case ev_field:
|
|
if (!pr_globals[ofs])
|
|
QC_snprintfz(temp, sizeof(temp), "((.void)__NULL__)");
|
|
else
|
|
QC_snprintfz(temp, sizeof(temp), "/*field %s*/%i", DecompileGetFieldNameIdxByFinalOffset(((int*)pr_globals)[ofs]), ((int*)pr_globals)[ofs]);
|
|
break;
|
|
default:
|
|
QC_snprintfz(temp, sizeof(temp), "FIXME");
|
|
break;
|
|
}
|
|
|
|
res = (char *)malloc(strlen(temp) + 1);
|
|
strcpy(res, temp);
|
|
|
|
return res;
|
|
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *DecompileGet(dfunction_t *df, gofs_t ofs, QCC_type_t *req_t)
|
|
{
|
|
char *farg1;
|
|
/*if (req_t == &def_short)
|
|
{
|
|
char temp[16];
|
|
QC_snprintfz(temp, sizeof(temp), "%i", ofs);
|
|
return strdup(temp);
|
|
}*/
|
|
farg1 = NULL;
|
|
|
|
farg1 = DecompileGlobal(df, ofs, req_t);
|
|
|
|
if (farg1 == NULL)
|
|
farg1 = DecompileImmediate_Get(df, ofs, req_t);
|
|
|
|
return farg1;
|
|
}
|
|
|
|
void DecompilePrintStatement(dstatement_t *s);
|
|
|
|
void DecompileIndent(int c)
|
|
{
|
|
int i;
|
|
|
|
if (c < 0)
|
|
c = 0;
|
|
|
|
for (i = 0; i < c; i++)
|
|
{
|
|
QCC_CatVFile(Decompileofile, "\t");
|
|
}
|
|
}
|
|
|
|
void DecompileOpcode(dfunction_t *df, int a, int b, int c, char *opcode, QCC_type_t *typ1, QCC_type_t *typ2, QCC_type_t *typ3, int usebrackets, int *indent)
|
|
{
|
|
static char line[8192];
|
|
char *arg1, *arg2, *arg3;
|
|
arg1 = DecompileGet(df, a, typ1);
|
|
arg2 = DecompileGet(df, b, typ2);
|
|
arg3 = DecompileGlobal(df, c, typ3);
|
|
|
|
if (arg3)
|
|
{
|
|
DecompileIndent(*indent);
|
|
if (usebrackets)
|
|
QCC_CatVFile(Decompileofile, "%s = %s %s %s;\n", arg3, arg1, opcode, arg2);
|
|
else
|
|
QCC_CatVFile(Decompileofile, "%s = %s%s%s;\n", arg3, arg1, opcode, arg2);
|
|
}
|
|
else
|
|
{
|
|
if (usebrackets)
|
|
QC_snprintfz(line, sizeof(line), "(%s %s %s)", arg1, opcode, arg2);
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "%s%s%s", arg1, opcode, arg2);
|
|
DecompileImmediate_Insert(df, c, line, typ3);
|
|
}
|
|
}
|
|
|
|
void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent)
|
|
{
|
|
static char line[8192];
|
|
static char fnam[512];
|
|
char *arg1, *arg2, *arg3;
|
|
int nargs, i, j;
|
|
dstatement_t *t;
|
|
unsigned int dom, doc, ifc, tom;
|
|
QCC_type_t *typ1, *typ2, *typ3;
|
|
QCC_ddef_t *par;
|
|
dstatement_t *k;
|
|
int dum;
|
|
|
|
|
|
arg1 = arg2 = arg3 = NULL;
|
|
|
|
line[0] = '\0';
|
|
fnam[0] = '\0';
|
|
|
|
dom = s->op;
|
|
|
|
doc = dom / OP_MARK_END_DO;
|
|
ifc = (dom % OP_MARK_END_DO) / OP_MARK_END_ELSE;
|
|
|
|
// use program flow information
|
|
|
|
for (i = 0; i < ifc; i++)
|
|
{
|
|
(*indent)--;
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "}\n");//FrikaC style modification
|
|
}
|
|
for (i = 0; i < doc; i++)
|
|
{
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "do\n");
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
(*indent)++;
|
|
}
|
|
|
|
/*
|
|
* remove all program flow information
|
|
*/
|
|
s->op %= OP_MARK_END_ELSE;
|
|
typ1 = pr_opcodes[s->op].type_a?*pr_opcodes[s->op].type_a:NULL;
|
|
typ2 = pr_opcodes[s->op].type_b?*pr_opcodes[s->op].type_b:NULL;
|
|
typ3 = pr_opcodes[s->op].type_c?*pr_opcodes[s->op].type_c:NULL;
|
|
|
|
/*
|
|
* printf("DecompileDecompileStatement - decompiling %i (%i):\n",(int)(s - statements),dom);
|
|
* DecompilePrintStatement (s);
|
|
*/
|
|
/*
|
|
* states are handled at top level
|
|
*/
|
|
if (s->op == OP_DONE)
|
|
{
|
|
|
|
}
|
|
else if (s->op == OP_BOUNDCHECK)
|
|
{
|
|
/*these are auto-generated as a sideeffect. currently there is no syntax to explicitly use one (other than asm), but we don't want to polute the code when they're autogenerated, so ditch them all*/
|
|
}
|
|
else if (s->op == OP_STATE)
|
|
{
|
|
|
|
par = DecompileGetParameter(s->a);
|
|
if (!par)
|
|
{
|
|
printf("Error - Can't determine frame number.\n");
|
|
exit(1);
|
|
}
|
|
arg2 = DecompileGet(df, s->b, NULL);
|
|
if (!arg2)
|
|
{
|
|
printf("Error - No state parameter with offset %i.\n", s->b);
|
|
exit(1);
|
|
}
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "state [ %s, %s ];\n", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs]), arg2);
|
|
|
|
// free(arg2);
|
|
}
|
|
else if (s->op == OP_RETURN/* || s->op == OPQF_RETURN_V*/)
|
|
{
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "return");
|
|
|
|
if (s->a)
|
|
{
|
|
QCC_CatVFile(Decompileofile, " ");
|
|
arg1 = DecompileGet(df, s->a, type_void); //FIXME: we should know the proper type better than this.
|
|
QCC_CatVFile(Decompileofile, "(%s)", arg1);
|
|
}
|
|
QCC_CatVFile(Decompileofile, ";\n");
|
|
|
|
}
|
|
else if (pr_opcodes[s->op].flags & OPF_STD)
|
|
{
|
|
DecompileOpcode(df, s->a, s->b, s->c, pr_opcodes[s->op].name, typ1, typ2, typ3, true, indent);
|
|
}
|
|
else if ((pr_opcodes[s->op].flags & OPF_LOADPTR) || OP_GLOBALADDRESS == s->op)
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
arg2 = DecompileGet(df, s->b, typ2);
|
|
arg3 = DecompileGlobal(df, s->c, typ3);
|
|
|
|
if (arg3)
|
|
{
|
|
DecompileIndent(*indent);
|
|
if (s->b)
|
|
QCC_CatVFile(Decompileofile, "%s = &%s[%s];\n", arg3, arg1, arg2);
|
|
else
|
|
QCC_CatVFile(Decompileofile, "%s = &%s;\n", arg3, arg1);
|
|
}
|
|
else
|
|
{
|
|
if (s->b)
|
|
QC_snprintfz(line, sizeof(line), "%s[%s]", arg1, arg2);
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "%s", arg1);
|
|
DecompileImmediate_Insert(df, s->c, line, typ3);
|
|
}
|
|
}
|
|
else if ((OP_LOAD_F <= s->op && s->op <= OP_ADDRESS) || s->op == OP_LOAD_P || s->op == OP_LOAD_I)
|
|
{
|
|
if (s->op == OP_ADDRESS)
|
|
{
|
|
QCC_ddef_t *def = GlobalAtOffset(df, s->b);
|
|
if (def && DecompileGetFieldTypeByDef(def) == ev_vector)
|
|
typ3 = type_vector;
|
|
}
|
|
|
|
type_field->aux_type = typ3;
|
|
DecompileOpcode(df, s->a, s->b, s->c, ".", typ1, typ2, typ3, false, indent);
|
|
type_field->aux_type = NULL;
|
|
}
|
|
else if ((OP_LOADA_F <= s->op && s->op <= OP_LOADA_I))// || (OPQF_LOADBI_F <= s->op && s->op <= OPQF_LOADBI_P))
|
|
{
|
|
static char line[512];
|
|
char *arg1, *arg2, *arg3;
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
arg2 = DecompileGet(df, s->b, typ2);
|
|
arg3 = DecompileGlobal(df, s->c, typ3);
|
|
|
|
if (arg3)
|
|
{
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "%s = %s[%s];\n", arg3, arg1, arg2);
|
|
}
|
|
else
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%s[%s]", arg1, arg2);
|
|
DecompileImmediate_Insert(df, s->c, line, typ3);
|
|
}
|
|
}
|
|
else if (pr_opcodes[s->op].flags & OPF_STORE)
|
|
{
|
|
QCC_type_t *parmtype=NULL;
|
|
if (s->b >= ofs_parms[0] && s->b < ofs_parms[7]+ofs_size)
|
|
{ //okay, so typ1 might not be what the store type says it should be.
|
|
k = s+1;
|
|
while(k->op%OP_MARK_END_ELSE)
|
|
{
|
|
if ((k->op >= OP_CALL0 && k->op <= OP_CALL8) || (k->op >= OP_CALL1H && k->op <= OP_CALL8H))
|
|
{
|
|
//well, this is it.
|
|
int fn = ((int*)pr_globals)[k->a];
|
|
dfunction_t *cf = &functions[fn];
|
|
int bi = DecompileBuiltin(cf);
|
|
int pn;
|
|
QCC_ddef_t *def;
|
|
if (bi)
|
|
{ //builtins don't have valid parm_start values
|
|
QCC_type_t **p = builtins[bi].params[(s->b-ofs_parms[0])/ofs_size];
|
|
parmtype = p?*p:NULL;
|
|
}
|
|
else
|
|
{ //qc functions do, though.
|
|
fn = cf->parm_start;
|
|
for (pn = 0; pn < (s->b-ofs_parms[0])/ofs_size; pn++)
|
|
fn += cf->parm_size[pn];
|
|
|
|
def = DecompileGetParameter(fn);
|
|
if (def)
|
|
{
|
|
switch(def->type)
|
|
{
|
|
case ev_float:
|
|
parmtype = type_float;
|
|
break;
|
|
case ev_string:
|
|
parmtype = type_string;
|
|
break;
|
|
case ev_vector:
|
|
parmtype = type_vector;
|
|
break;
|
|
case ev_entity:
|
|
parmtype = type_entity;
|
|
break;
|
|
case ev_function:
|
|
parmtype = type_function;
|
|
break;
|
|
case ev_integer:
|
|
parmtype = type_integer;
|
|
break;
|
|
// case ev_uinteger:
|
|
// parmtype = type_uinteger;
|
|
// break;
|
|
// case ev_quat:
|
|
// parmtype = type_quat;
|
|
// break;
|
|
default:
|
|
// parmtype = type_float;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (OP_STORE_F <= s->op && s->op <= OP_STORE_FNC)
|
|
{
|
|
if (k->b < s->b) //whoops... older QCCs can nest things awkwardly.
|
|
break;
|
|
}
|
|
k++;
|
|
}
|
|
}
|
|
|
|
if (parmtype)
|
|
arg1 = DecompileGet(df, s->a, parmtype);
|
|
else
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
arg3 = DecompileGlobal(df, s->b, typ2);
|
|
|
|
if (arg3)
|
|
{
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "%s %s %s;\n", arg3, pr_opcodes[s->op].name, arg1);
|
|
}
|
|
else
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%s", arg1);
|
|
DecompileImmediate_Insert(df, s->b, line, NULL);
|
|
}
|
|
|
|
}
|
|
else if (pr_opcodes[s->op].flags & (OPF_STOREPTR|OPF_STOREPTROFS))
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ2);
|
|
//FIXME: we need to deal with ref types and other crazyness, so we know whether we need to add * or *& or if we can skip that completely
|
|
arg2 = DecompileGet(df, s->b, typ2);
|
|
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "%s %s %s;\n", arg2, pr_opcodes[s->op].name, arg1);
|
|
}
|
|
else if (pr_opcodes[s->op].flags & OPF_STOREFLD)
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
//FIXME: we need to deal with ref types and other crazyness, so we know whether we need to add * or *& or if we can skip that completely
|
|
arg2 = DecompileGet(df, s->b, typ2);
|
|
arg3 = DecompileGet(df, s->c, typ3);
|
|
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "%s.%s %s %s;\n", arg1, arg2, pr_opcodes[s->op].name, arg3);
|
|
}
|
|
else if (OP_CONV_FTOI == s->op)
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
QC_snprintfz(line, sizeof(line), "(int)%s", arg1);
|
|
DecompileImmediate_Insert(df, s->c, line, type_integer);
|
|
}
|
|
else if (OP_CONV_ITOF == s->op)
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
QC_snprintfz(line, sizeof(line), "(float)%s", arg1);
|
|
DecompileImmediate_Insert(df, s->c, line, type_float);
|
|
}
|
|
else if (OP_RAND0 == s->op)
|
|
{
|
|
DecompileImmediate_Insert(df, ofs_return, "random()", type_float);
|
|
}
|
|
else if (OP_RAND1 == s->op)
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
QC_snprintfz(line, sizeof(line), "random(%s)", arg1);
|
|
DecompileImmediate_Insert(df, ofs_return, line, type_float);
|
|
}
|
|
else if (OP_RAND2 == s->op)
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
arg2 = DecompileGet(df, s->b, typ2);
|
|
QC_snprintfz(line, sizeof(line), "random(%s, %s)", arg1, arg2);
|
|
DecompileImmediate_Insert(df, ofs_return, line, type_float);
|
|
}
|
|
else if (pr_opcodes[s->op].flags & OPF_STDUNARY)
|
|
{
|
|
arg1 = DecompileGet(df, s->a, typ1);
|
|
QC_snprintfz(line, sizeof(line), "%s%s", pr_opcodes[s->op].name, arg1);
|
|
DecompileImmediate_Insert(df, s->c, line, type_float);
|
|
}
|
|
else if ((OP_CALL0 <= s->op && s->op <= OP_CALL8) || (OP_CALL1H <= s->op && s->op <= OP_CALL8H))
|
|
{
|
|
if (OP_CALL1H <= s->op && s->op <= OP_CALL8H)
|
|
nargs = (s->op - OP_CALL1H) + 1;
|
|
else
|
|
nargs = s->op - OP_CALL0;
|
|
|
|
arg1 = DecompileGet(df, s->a, type_function);
|
|
QC_snprintfz(line, sizeof(line), "%s(", arg1);
|
|
QC_snprintfz(fnam, sizeof(fnam), "%s", arg1);
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
|
|
typ1 = NULL;
|
|
|
|
if (i == 0 && OP_CALL1H <= s->op && s->op <= OP_CALL8H)
|
|
j = s->b;
|
|
else if (i == 1 && OP_CALL1H <= s->op && s->op <= OP_CALL8H)
|
|
j = s->c;
|
|
else
|
|
j = ofs_parms[i];
|
|
|
|
if (arg1)
|
|
free(arg1);
|
|
|
|
arg1 = DecompileGet(df, (gofs_t)j, typ1);
|
|
strcat(line, arg1);
|
|
|
|
if (i < nargs - 1)
|
|
strcat(line, ", ");//frikqcc modified
|
|
}
|
|
|
|
strcat(line, ")");
|
|
DecompileImmediate_Insert(df, ofs_return, line, NULL);
|
|
|
|
/*
|
|
* if ( ( ( (s+1)->a != 1) && ( (s+1)->b != 1) &&
|
|
* ( (s+2)->a != 1) && ( (s+2)->b != 1) ) ||
|
|
* ( ((s+1)->op) % OP_MARK_END_ELSE == OP_CALL0 ) ) {
|
|
* DecompileIndent(*indent);
|
|
* fprintf(Decompileofile,"%s;\n",line);
|
|
* }
|
|
*/
|
|
|
|
if ((((s + 1)->a != ofs_return) && ((s + 1)->b != ofs_return) &&
|
|
((s + 2)->a != ofs_return) && ((s + 2)->b != ofs_return)) ||
|
|
((((s + 1)->op) % OP_MARK_END_ELSE == OP_CALL0) && ((((s + 2)->a != ofs_return)) || ((s + 2)->b != ofs_return))))
|
|
{
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "%s;\n", line);
|
|
}
|
|
}
|
|
else if (s->op == OP_IF_I || s->op == OP_IFNOT_I ||
|
|
s->op == OP_IF_F || s->op == OP_IFNOT_F ||
|
|
s->op == OP_IF_S || s->op == OP_IFNOT_S/* || s->op == OPQF_IFA || s->op == OPQF_IFB || s->op == OPQF_IFAE || s->op == OPQF_IFBE*/)
|
|
{
|
|
|
|
arg1 = DecompileGet(df, s->a, type_float); //FIXME: this isn't quite accurate...
|
|
arg2 = DecompileGlobal(df, s->a, NULL);
|
|
|
|
if (s->op == OP_IFNOT_I || s->op == OP_IFNOT_F || s->op == OP_IFNOT_S)
|
|
{
|
|
lameifnot:
|
|
if ((signed int)s->b < 1)
|
|
{
|
|
// if (arg1)
|
|
// free(arg1);
|
|
// if (arg2)
|
|
// free(arg2);
|
|
// if (arg3)
|
|
// free(arg3);
|
|
|
|
return;
|
|
|
|
|
|
printf("Found a negative IFNOT jump.\n");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* get instruction right before the target
|
|
*/
|
|
t = s + (signed int)s->b - 1;
|
|
tom = t->op % OP_MARK_END_ELSE;
|
|
|
|
if (tom != OP_GOTO)
|
|
{
|
|
|
|
/*
|
|
* pure if
|
|
*/
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "if (%s)\n", arg1);//FrikaC modified
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
|
|
(*indent)++;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
if ((signed int)t->a > 0)
|
|
{
|
|
/*
|
|
* ite
|
|
*/
|
|
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "if (%s)\n", arg1);//FrikaC modified
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
|
|
(*indent)++;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
if (((signed int)t->a + (signed int)s->b) > 1)
|
|
{
|
|
/*
|
|
* pure if
|
|
*/
|
|
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "if (%s)\n", arg1);//FrikaC modified
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
(*indent)++;
|
|
}
|
|
else
|
|
{
|
|
|
|
dum = 1;
|
|
for (k = t + (signed int)(t->a); k < s; k++)
|
|
{
|
|
tom = k->op % OP_MARK_END_ELSE;
|
|
if (tom == OP_GOTO ||
|
|
tom == OP_IF_I || tom == OP_IFNOT_I ||
|
|
tom == OP_IF_F || tom == OP_IFNOT_F ||
|
|
tom == OP_IF_S || tom == OP_IFNOT_S)
|
|
dum = 0;
|
|
}
|
|
if (dum)
|
|
{
|
|
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "while (%s)\n", arg1);
|
|
DecompileIndent(*indent); //FrikaC
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
(*indent)++;
|
|
}
|
|
else
|
|
{
|
|
|
|
DecompileIndent(*indent);
|
|
|
|
|
|
QCC_CatVFile(Decompileofile, "if (%s)\n", arg1);//FrikaC modified
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
(*indent)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*else if (s->op == OPQF_IFA)
|
|
{
|
|
char *t = arg1;
|
|
arg1 = malloc(strlen(arg1)+8);
|
|
sprintf(arg1, "(%s) <= 0", t);
|
|
free(t);
|
|
goto lameifnot;
|
|
}
|
|
else if (s->op == OPQF_IFAE)
|
|
{
|
|
char *t = arg1;
|
|
arg1 = malloc(strlen(arg1)+7);
|
|
sprintf(arg1, "(%s) < 0", t);
|
|
free(t);
|
|
goto lameifnot;
|
|
}
|
|
else if (s->op == OPQF_IFB)
|
|
{
|
|
char *t = arg1;
|
|
arg1 = malloc(strlen(arg1)+8);
|
|
sprintf(arg1, "(%s) >= 0", t);
|
|
free(t);
|
|
goto lameifnot;
|
|
}
|
|
else if (s->op == OPQF_IFBE)
|
|
{
|
|
char *t = arg1;
|
|
arg1 = malloc(strlen(arg1)+7);
|
|
sprintf(arg1, "(%s) > 0", t);
|
|
free(t);
|
|
goto lameifnot;
|
|
}*/
|
|
else
|
|
{
|
|
if ((signed int)s->b>0)
|
|
{
|
|
char *t = arg1;
|
|
//if (!...)
|
|
|
|
arg1 = malloc(strlen(arg1)+2);
|
|
sprintf(arg1, "!%s", t);
|
|
free(t);
|
|
goto lameifnot;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* do ... while
|
|
*/
|
|
|
|
(*indent)--;
|
|
QCC_CatVFile(Decompileofile, "\n");
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "} while (%s);\n", arg1);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
else if (s->op == OPD_GOTO_FORSTART)
|
|
{
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "do_tail\n", (s-statements) + (signed int)s->a);
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
(*indent)++;
|
|
}
|
|
else if (s->op == OPD_GOTO_WHILE1)
|
|
{
|
|
(*indent)--;
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "} while(1);\n");
|
|
}
|
|
else if (s->op == OP_GOTO)
|
|
{
|
|
|
|
if ((signed int)s->a > 0)
|
|
{
|
|
/*
|
|
* else
|
|
*/
|
|
(*indent)--;
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "}\n");
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "else\n");
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "{\n");
|
|
(*indent)++;
|
|
|
|
//FIXME: look for the next control statement (ignoring writes to temps, maybe allow functions too if the following statement reads OFS_RETURN)
|
|
//if its an IFNOT (optionally with its own else) that ends at our else then output this as "}\nelse " instead
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* while
|
|
*/
|
|
(*indent)--;
|
|
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "}\n");
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
int op = s->op%OP_MARK_END_ELSE;
|
|
if (op <= OP_BITOR_F && pr_opcodes[s->op].opname)
|
|
printf("warning: Unknown usage of OP_%s", pr_opcodes[s->op].opname);
|
|
else
|
|
{
|
|
DecompileIndent(*indent);
|
|
QCC_CatVFile(Decompileofile, "[OP_%s", pr_opcodes[op].opname);
|
|
if (s->a)
|
|
QCC_CatVFile(Decompileofile, ", %s", DecompileGet(df, s->a, typ1));
|
|
if (s->b)
|
|
QCC_CatVFile(Decompileofile, ", %s", DecompileGet(df, s->b, typ1));
|
|
if (s->c)
|
|
QCC_CatVFile(Decompileofile, ", %s", DecompileGet(df, s->c, typ1));
|
|
QCC_CatVFile(Decompileofile, "]\n");
|
|
printf("warning: Unknown opcode %i in %s\n", op, GetString(df->s_name));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// printf("DecompileDecompileStatement - Current line is \"%s\"\n", line);
|
|
|
|
|
|
if (arg1)
|
|
free(arg1);
|
|
if (arg2)
|
|
free(arg2);
|
|
if (arg3)
|
|
free(arg3);
|
|
|
|
return;
|
|
}
|
|
|
|
pbool DecompileDecompileFunction(dfunction_t * df, dstatement_t *altdone)
|
|
{
|
|
dstatement_t *ds;
|
|
int indent;
|
|
|
|
|
|
// Initialize
|
|
|
|
DecompileImmediate_Free();
|
|
|
|
indent = 1;
|
|
|
|
ds = statements + df->first_statement;
|
|
if(ds->op == OP_STATE)
|
|
ds++;
|
|
while (1)
|
|
{
|
|
if (ds == altdone)
|
|
{
|
|
//decompile the dummy done, cos we can
|
|
DecompileDecompileStatement(df, statements, &indent);
|
|
break;
|
|
}
|
|
DecompileDecompileStatement(df, ds, &indent);
|
|
if (!ds->op)
|
|
break;
|
|
ds++;
|
|
}
|
|
|
|
if (indent != 1)
|
|
{
|
|
printf("warning: Indentation structure corrupt (in func %s)\n", GetString(df->s_name));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
char *DecompileString(int qcstring)
|
|
{
|
|
static char buf[8192];
|
|
char *s;
|
|
int c = 1;
|
|
const char *string = GetString(qcstring);
|
|
if (qcstring < 0 || qcstring >= strofs)
|
|
return "Invalid String";
|
|
|
|
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 > (int)(sizeof(buf) - 10))
|
|
{
|
|
*s++ = '.';
|
|
*s++ = '.';
|
|
*s++ = '.';
|
|
c += 3;
|
|
break;
|
|
}
|
|
}
|
|
*s++ = '"';
|
|
*s++ = 0;
|
|
return buf;
|
|
}
|
|
|
|
char *DecompileValueString(etype_t type, void *val)
|
|
{
|
|
static char line[8192];
|
|
|
|
line[0] = '\0';
|
|
|
|
switch (type)
|
|
{
|
|
case ev_string:
|
|
QC_snprintfz(line, sizeof(line), "%s", DecompileString(*(int *)val));
|
|
break;
|
|
case ev_void:
|
|
QC_snprintfz(line, sizeof(line), "void");
|
|
break;
|
|
case ev_float:
|
|
if (*(float *)val > 999999 || *(float *)val < -999999) // ugh
|
|
QC_snprintfz(line, sizeof(line), "%.f", *(float *)val);
|
|
else if ((!(*(int*)val & 0x7f800000) || (*(int*)val & 0x7f800000)==0x7f800000) && (*(int*)val & 0x7fffffff))
|
|
//QC_snprintfz(line, sizeof(line), "%%%i", *(int*)val);
|
|
QC_snprintfz(line, sizeof(line), "/*%di*/%s", *(int*)val, DecompileString(*(int *)val));
|
|
else if ((*(float *)val < 0.001) && (*(float *)val > 0))
|
|
QC_snprintfz(line, sizeof(line), "%.6f", *(float *)val);
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "%g", *(float *)val);
|
|
break;
|
|
case ev_vector:
|
|
QC_snprintfz(line, sizeof(line), "'%g %g %g'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]);
|
|
break;
|
|
// case ev_quat:
|
|
// QC_snprintfz(line, sizeof(line), "'%g %g %g %g'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2], ((float *)val)[3]);
|
|
// break;
|
|
case ev_field:
|
|
DecompileGetFieldNameIdxByFinalOffset2(line, sizeof(line), *(int *)val);
|
|
break;
|
|
case ev_entity:
|
|
QC_snprintfz(line, sizeof(line), "(entity)%ii", *(int *)val);
|
|
break;
|
|
case ev_integer:
|
|
QC_snprintfz(line, sizeof(line), "%ii", *(int *)val);
|
|
break;
|
|
// case ev_uinteger:
|
|
// QC_snprintfz(line, sizeof(line), "%uu", *(int *)val);
|
|
// break;
|
|
case ev_pointer:
|
|
QC_snprintfz(line, sizeof(line), "(__variant*)0x%xi", *(int *)val);
|
|
break;
|
|
case ev_function:
|
|
if (*(int *)val>0 && *(int *)val<numfunctions)
|
|
QC_snprintfz(line, sizeof(line), "(/*func 0x%x*/%s)", *(int *)val, GetString(functions[*(int *)val].s_name));
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "((void())0x%xi)", *(int *)val);
|
|
break;
|
|
default:
|
|
QC_snprintfz(line, sizeof(line), "bad type %i", type);
|
|
break;
|
|
}
|
|
|
|
return line;
|
|
}
|
|
|
|
char *DecompilePrintParameter(QCC_ddef_t * def)
|
|
{
|
|
static char line[256];
|
|
static char debug[128];
|
|
|
|
line[0] = '0';
|
|
|
|
if (debug_offs)
|
|
{
|
|
QC_snprintfz(debug, sizeof(debug), " /*@%i*/", def->ofs);
|
|
}
|
|
else
|
|
*debug = 0;
|
|
|
|
if (!strings[def->s_name]) //null string...
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%s _p_%i%s", type_name(def), def->ofs, debug);
|
|
}
|
|
else if (!strcmp(GetString(def->s_name), "IMMEDIATE") || !strcmp(GetString(def->s_name), ".imm"))
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%s%s", DecompileValueString((etype_t)(def->type), &pr_globals[def->ofs]), debug);
|
|
}
|
|
else
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "%s %s%s", type_name(def), GetString(def->s_name), debug);
|
|
}
|
|
return line;
|
|
}
|
|
|
|
//we only work with prior fields.
|
|
const char *GetMatchingField(QCC_ddef_t *field)
|
|
{
|
|
int i;
|
|
QCC_ddef_t *def;
|
|
int ld, lf;
|
|
const char *ret = NULL;
|
|
|
|
def = NULL;
|
|
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
def = &globals[i];
|
|
|
|
if ((def->type&~DEF_SAVEGLOBAL) == ev_field)
|
|
{
|
|
if (((int*)pr_globals)[def->ofs] == field->ofs)
|
|
{
|
|
if (!strcmp(GetString(def->s_name), GetString(field->s_name)))
|
|
return NULL; //found ourself, give up.
|
|
lf = strlen(GetString(field->s_name));
|
|
ld = strlen(GetString(def->s_name));
|
|
if (lf - 2 == ld)
|
|
{
|
|
if ((GetString(field->s_name)[lf-2]) == '_' && (GetString(field->s_name)[lf-1]) == 'x')
|
|
if (!strncmp(GetString(field->s_name), GetString(def->s_name), ld))
|
|
return NULL; //vector found foo_x
|
|
}
|
|
if (!ret)
|
|
ret = GetString(def->s_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QCC_ddef_t *GetField(const char *name)
|
|
{
|
|
int i;
|
|
QCC_ddef_t *d;
|
|
if (!*name)
|
|
return NULL;
|
|
|
|
if (*name == '.')
|
|
name++; //some idiot _intentionally_ decided to fuck shit up. go them!
|
|
|
|
for (i = 0; i < numfielddefs; i++)
|
|
{
|
|
d = &fields[i];
|
|
|
|
if (!strcmp(GetString(d->s_name), name))
|
|
return d;
|
|
}
|
|
return NULL;
|
|
}
|
|
QCC_ddef_t *DecompileGetParameter(gofs_t ofs)
|
|
{
|
|
int i;
|
|
QCC_ddef_t *def;
|
|
|
|
def = NULL;
|
|
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
def = &globals[i];
|
|
|
|
if (def->ofs == ofs)
|
|
{
|
|
return def;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
QCC_ddef_t *DecompileFindGlobal(const char *findname)
|
|
{
|
|
int i;
|
|
QCC_ddef_t *def;
|
|
const char *defname;
|
|
|
|
def = NULL;
|
|
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
def = &globals[i];
|
|
defname = GetString(def->s_name);
|
|
|
|
if (!strcmp(findname, defname))
|
|
{
|
|
return def;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
QCC_ddef_t *DecompileFunctionGlobal(int funcnum)
|
|
{
|
|
int i;
|
|
QCC_ddef_t *def;
|
|
|
|
def = NULL;
|
|
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
def = &globals[i];
|
|
|
|
if (def->type == ev_function)
|
|
{
|
|
if (((int*)pr_globals)[def->ofs] == funcnum)
|
|
{
|
|
return def;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void DecompilePreceedingGlobals(int start, int end, const char *name)
|
|
{
|
|
QCC_ddef_t *par;
|
|
int j;
|
|
QCC_ddef_t *ef;
|
|
static char line[8192];
|
|
|
|
const char *matchingfield;
|
|
|
|
//print globals leading up to the function.
|
|
for (j = start; j < end; j++)
|
|
{
|
|
|
|
par = DecompileGetParameter((gofs_t)j);
|
|
|
|
if (par)
|
|
{
|
|
if (par->type & DEF_SAVEGLOBAL)
|
|
par->type -= DEF_SAVEGLOBAL;
|
|
|
|
if (par->type == ev_function)
|
|
{
|
|
if (strcmp(GetString(par->s_name), "IMMEDIATE") && strcmp(GetString(par->s_name), ".imm"))
|
|
{
|
|
if (strcmp(GetString(par->s_name), name))
|
|
{
|
|
int f = ((int*)pr_globals)[par->ofs];
|
|
//DecompileGetFunctionIdxByName(strings + par->s_name);
|
|
if (f && strcmp(GetString(functions[f].s_name), GetString(par->s_name)))
|
|
{
|
|
char *s = strrchr(DecompileProfiles[f], ' ');
|
|
//happens with void() func = otherfunc;
|
|
//such functions thus don't have their own type+body
|
|
*s = 0;
|
|
QCC_CatVFile(Decompileofile, "var %s %s = %s;\n", DecompileProfiles[f], GetString(par->s_name), s+1);
|
|
*s = ' ';
|
|
}
|
|
else
|
|
QCC_CatVFile(Decompileofile, "%s;\n", DecompileProfiles[f]);
|
|
}
|
|
}
|
|
}
|
|
else if (par->type != ev_pointer)
|
|
{
|
|
if (strcmp(GetString(par->s_name), "IMMEDIATE") && strcmp(GetString(par->s_name), ".imm") && par->s_name)
|
|
{
|
|
|
|
if (par->type == ev_field)
|
|
{
|
|
|
|
ef = GetField(GetString(par->s_name));
|
|
|
|
if (!ef)
|
|
{
|
|
QCC_CatVFile(Decompileofile, "var .unknowntype %s;\n", GetString(par->s_name));
|
|
printf("Fatal Error: Could not locate a field named \"%s\"\n", GetString(par->s_name));
|
|
}
|
|
else
|
|
{
|
|
//if (ef->type == ev_vector)
|
|
// j += 2;
|
|
|
|
matchingfield = GetMatchingField(ef);
|
|
|
|
#ifndef DONT_USE_DIRTY_TRICKS //could try scanning for an op_address+op_storep_fnc pair
|
|
if ((ef->type == ev_function) && !strcmp(GetString(ef->s_name), "th_pain"))
|
|
{
|
|
QCC_CatVFile(Decompileofile, ".void(entity attacker, float damage) th_pain;\n");
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (matchingfield)
|
|
QCC_CatVFile(Decompileofile, "var .%s %s = %s;\n", type_name(ef), GetString(ef->s_name), matchingfield);
|
|
else
|
|
QCC_CatVFile(Decompileofile, ".%s %s;\n", type_name(ef), GetString(ef->s_name));
|
|
|
|
// fprintf(Decompileofile, "//%i %i %i %i\n", ef->ofs, ((int*)pr_globals)[ef->ofs], par->ofs, ((int*)pr_globals)[par->ofs]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
if (par->type == ev_vector)
|
|
j += 2;
|
|
|
|
if (par->type == ev_entity || par->type == ev_void)
|
|
{
|
|
|
|
QCC_CatVFile(Decompileofile, "%s %s;\n", type_name(par), GetString(par->s_name));
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
line[0] = '\0';
|
|
QC_snprintfz(line, sizeof(line), "%s", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs]));
|
|
|
|
if (IsConstant(par))
|
|
{
|
|
QCC_CatVFile(Decompileofile, "%s %s = %s;\n", type_name(par), GetString(par->s_name), line);
|
|
}
|
|
else
|
|
{
|
|
if (pr_globals[par->ofs] != 0)
|
|
QCC_CatVFile(Decompileofile, "%s %s /* = %s */;\n", type_name(par), GetString(par->s_name), line);
|
|
else
|
|
QCC_CatVFile(Decompileofile, "%s %s;\n", type_name(par), GetString(par->s_name), line);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void DecompileFunction(const char *name, int *lastglobal)
|
|
{
|
|
int i, findex, ps;
|
|
dstatement_t *ds, *ts, *altdone;
|
|
dfunction_t *df;
|
|
QCC_ddef_t *par;
|
|
char *arg2;
|
|
unsigned short dom, tom;
|
|
int j, start, end;
|
|
static char line[8192];
|
|
dstatement_t *k;
|
|
int dum;
|
|
size_t startpos;
|
|
|
|
|
|
|
|
for (i = 1; i < numfunctions; i++)
|
|
if (!strcmp(name, GetString(functions[i].s_name)))
|
|
break;
|
|
if (i == numfunctions)
|
|
{
|
|
printf("Fatal Error: No function named \"%s\"\n", name);
|
|
exit(1);
|
|
}
|
|
df = functions + i;
|
|
altdone = statements + numstatements;
|
|
for (j = i+1; j < numfunctions; j++)
|
|
{
|
|
if (functions[j].first_statement <= 0)
|
|
continue;
|
|
altdone = statements + functions[j].first_statement;
|
|
break;
|
|
}
|
|
|
|
findex = i;
|
|
|
|
start = *lastglobal;
|
|
// if (dfpred->first_statement <= 0 && df->first_statement > 0)
|
|
// start -= 1;
|
|
end = df->parm_start;
|
|
if (!end)
|
|
{
|
|
par = DecompileFindGlobal(name);
|
|
if (par)
|
|
end = par - globals;
|
|
}
|
|
*lastglobal = max(*lastglobal, end + df->locals);
|
|
DecompilePreceedingGlobals(start, end, name);
|
|
|
|
/*
|
|
* Check ''local globals''
|
|
*/
|
|
|
|
if (df->first_statement <= 0)
|
|
{
|
|
|
|
QCC_CatVFile(Decompileofile, "%s", DecompileProfiles[findex]);
|
|
QCC_CatVFile(Decompileofile, " = #%i; \n", -df->first_statement);
|
|
|
|
return;
|
|
}
|
|
ds = statements + df->first_statement;
|
|
|
|
while (1)
|
|
{
|
|
|
|
dom = (ds->op) % OP_MARK_END_ELSE;
|
|
|
|
if (!dom || ds == altdone)
|
|
break;
|
|
else if (dom == OP_GOTO)
|
|
{
|
|
// check for i-t-e
|
|
if ((signed int)ds->a > 0)
|
|
{
|
|
ts = ds + (signed int)ds->a;
|
|
ts->op += OP_MARK_END_ELSE; // mark the end of a if/ite construct
|
|
}
|
|
else
|
|
{
|
|
ts = ds + (signed int)ds->a;
|
|
//if its a negative goto then it should normally be the end of a while{} loop. if we can't find the while statement itself, then its an infinite loop
|
|
for (k = (ts); k < ds; k++)
|
|
{
|
|
tom = k->op % OP_MARK_END_ELSE;
|
|
if (tom == OP_IF_I || tom == OP_IFNOT_I ||
|
|
tom == OP_IF_F || tom == OP_IFNOT_F ||
|
|
tom == OP_IF_S || tom == OP_IFNOT_S)
|
|
{
|
|
if (k + (signed int)k->b == ds+1)
|
|
break;
|
|
}
|
|
}
|
|
if (k == ds)
|
|
{
|
|
ds->op += OPD_GOTO_WHILE1-OP_GOTO;
|
|
ts->op += OP_MARK_END_DO;
|
|
}
|
|
}
|
|
}
|
|
else if (dom == OP_IFNOT_I || dom == OP_IFNOT_F || dom == OP_IFNOT_S)
|
|
{
|
|
// check for pure if
|
|
|
|
ts = ds + (signed int)ds->b;
|
|
tom = (ts - 1)->op % OP_MARK_END_ELSE;
|
|
|
|
if (tom != OP_GOTO)
|
|
ts->op += OP_MARK_END_ELSE; // mark the end of a if construct
|
|
else if ((signed int)(ts - 1)->a < 0)
|
|
{
|
|
if (((signed int)(ts - 1)->a + (signed int)ds->b) > 1)
|
|
{
|
|
// pure if
|
|
ts->op += OP_MARK_END_ELSE; // mark the end of a if/ite construct
|
|
}
|
|
else
|
|
{
|
|
|
|
dum = 1;
|
|
for (k = (ts - 1) + (signed int)((ts - 1)->a); k < ds; k++)
|
|
{
|
|
tom = k->op % OP_MARK_END_ELSE;
|
|
if (tom == OP_GOTO ||
|
|
tom == OP_IF_I || tom == OP_IFNOT_I ||
|
|
tom == OP_IF_F || tom == OP_IFNOT_F ||
|
|
tom == OP_IF_S || tom == OP_IFNOT_S)
|
|
dum = 0;
|
|
}
|
|
if (!dum)
|
|
{
|
|
// pure if
|
|
ts->op += OP_MARK_END_ELSE; // mark the end of a if/ite construct
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (dom == OP_IF_I || dom == OP_IF_F || dom == OP_IF_S)
|
|
{
|
|
if ((signed int)ds->b<1)
|
|
{
|
|
ts = ds + (signed int)ds->b;
|
|
//this is some kind of loop, either a while or for.
|
|
|
|
//if the statement before the 'do' is a forwards goto, and it jumps to within the loop (instead of after), then we have to assume that it is a for loop and not a loop inside an else block.
|
|
if ((ts-1)->op%OP_MARK_END_ELSE == OP_GOTO && (signed int)(ts-1)->a > 0 && (ts-1)+(signed int)(ts-1)->a <= ds)
|
|
{
|
|
(--ts)->op += OPD_GOTO_FORSTART - OP_GOTO;
|
|
//because it was earlier, we need to unmark that goto's target as an end_else
|
|
ts = ts + (signed int)ts->a;
|
|
ts->op -= OP_MARK_END_ELSE;
|
|
}
|
|
else
|
|
ts->op += OP_MARK_END_DO; // mark the start of a do construct
|
|
}
|
|
else
|
|
{
|
|
ts = ds + ds->b;
|
|
if ((ts-1)->op%OP_MARK_END_ELSE != OP_GOTO)
|
|
ts->op += OP_MARK_END_ELSE; // mark the end of an if construct
|
|
else if ((signed int)(ts - 1)->a < 0)
|
|
{
|
|
if (((signed int)(ts - 1)->a + (signed int)ds->b) > 1)
|
|
{
|
|
// pure if
|
|
ts->op += OP_MARK_END_ELSE; // mark the end of a if/ite construct
|
|
}
|
|
else
|
|
{
|
|
|
|
dum = 1;
|
|
for (k = (ts - 1) + (signed int)((ts - 1)->a); k < ds; k++)
|
|
{
|
|
tom = k->op % OP_MARK_END_ELSE;
|
|
if (tom == OP_GOTO ||
|
|
tom == OP_IF_I || tom == OP_IFNOT_I ||
|
|
tom == OP_IF_F || tom == OP_IFNOT_F ||
|
|
tom == OP_IF_S || tom == OP_IFNOT_S)
|
|
dum = 0;
|
|
}
|
|
if (!dum)
|
|
{
|
|
// pure if
|
|
ts->op += OP_MARK_END_ELSE; // mark the end of a if/ite construct
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ds++;
|
|
}
|
|
|
|
/*
|
|
* print the prototype
|
|
*/
|
|
QCC_CatVFile(Decompileofile, "\n%s", DecompileProfiles[findex]);
|
|
|
|
// handle state functions
|
|
|
|
ds = statements + df->first_statement;
|
|
|
|
if (ds->op == OP_STATE)
|
|
{
|
|
|
|
par = DecompileGetParameter(ds->a);
|
|
if (!par)
|
|
{
|
|
static QCC_ddef_t pars;
|
|
//must be a global (gotta be a float), create the def as needed
|
|
pars.ofs = ds->a;
|
|
pars.s_name = "IMMEDIATE"-strings;
|
|
pars.type = ev_float;
|
|
par = &pars;
|
|
// printf("Fatal Error - Can't determine frame number.");
|
|
// exit(1);
|
|
}
|
|
|
|
arg2 = DecompileGet(df, ds->b, NULL);
|
|
if (!arg2)
|
|
{
|
|
printf("Fatal Error - No state parameter with offset %i.", ds->b);
|
|
exit(1);
|
|
}
|
|
|
|
QCC_CatVFile(Decompileofile, " = [ %s, %s ]", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs]), arg2);
|
|
|
|
free(arg2);
|
|
|
|
}
|
|
else
|
|
{
|
|
QCC_CatVFile(Decompileofile, " =");
|
|
}
|
|
QCC_CatVFile(Decompileofile, "\n{\n");
|
|
|
|
startpos = Decompileofile->size;
|
|
|
|
/*
|
|
fprintf(Decompileprofile, "%s", DecompileProfiles[findex]);
|
|
fprintf(Decompileprofile, ") %s;\n", name);
|
|
*/
|
|
|
|
/*
|
|
* calculate the parameter size
|
|
*/
|
|
|
|
for (j = 0, ps = 0; j < df->numparms; j++)
|
|
{
|
|
par = DecompileGetParameter((gofs_t)(df->parm_start + ps));
|
|
|
|
if (par)
|
|
{
|
|
if (!strings[par->s_name])
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "_p_%i", par->ofs);
|
|
arg2 = malloc(strlen(line)+1);
|
|
strcpy(arg2, line);
|
|
par->s_name = arg2 - strings;
|
|
}
|
|
}
|
|
|
|
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 = DecompileGetParameter((gofs_t)i);
|
|
|
|
if (!par)
|
|
{
|
|
// temps, or stripped...
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (!strcmp(GetString(par->s_name), "IMMEDIATE") || !strcmp(GetString(par->s_name), ".imm"))
|
|
continue; // immediates don't belong
|
|
|
|
if (!GetString(par->s_name))
|
|
{
|
|
QC_snprintfz(line, sizeof(line), "_l_%i", par->ofs);
|
|
arg2 = malloc(strlen(line)+1);
|
|
strcpy(arg2, line);
|
|
par->s_name = arg2 - strings;
|
|
}
|
|
|
|
if (par->type == ev_function)
|
|
{
|
|
printf("Warning Fields and functions must be global\n");
|
|
}
|
|
else
|
|
{
|
|
if (((int*)pr_globals)[par->ofs])
|
|
QCC_CatVFile(Decompileofile, "\tlocal %s = %s;\n", DecompilePrintParameter(par), DecompileValueString(par->type, &pr_globals[par->ofs]));
|
|
else
|
|
QCC_CatVFile(Decompileofile, "\tlocal %s;\n", DecompilePrintParameter(par));
|
|
}
|
|
if (par->type == ev_vector)
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
QCC_CatVFile(Decompileofile, "\n");
|
|
|
|
}
|
|
}
|
|
/*
|
|
* do the hard work
|
|
*/
|
|
|
|
if (!DecompileDecompileFunction(df, altdone))
|
|
{
|
|
QCC_InsertVFile(Decompileofile, startpos, "#error Corrupt Function: %s\n#if 0\n", GetString(df->s_name));
|
|
QCC_CatVFile(Decompileofile, "#endif\n");
|
|
}
|
|
|
|
QCC_CatVFile(Decompileofile, "};\n");
|
|
}
|
|
|
|
extern pbool safedecomp;
|
|
int fake_name;
|
|
char synth_name[1024]; // fake name part2
|
|
|
|
pbool TrySynthName(const char *first)
|
|
{
|
|
int i;
|
|
|
|
// try to figure out the filename
|
|
// based on the first function in the file
|
|
for (i=0; i < FILELISTSIZE; i+=2)
|
|
{
|
|
if (!strcmp(filenames[i], first))
|
|
{
|
|
QC_snprintfz(synth_name, sizeof(synth_name), "%s", filenames[i + 1]);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DecompileDecompileFunctions(const char *origcopyright)
|
|
{
|
|
int i;
|
|
unsigned int o;
|
|
dfunction_t *d;
|
|
pbool bogusname;
|
|
vfile_t *f = NULL;
|
|
char fname[1024];
|
|
int lastglob = 1;
|
|
int lastfileofs = 0;
|
|
QCC_ddef_t *def;
|
|
|
|
DecompileCalcProfiles();
|
|
|
|
AddSourceFile(NULL, "progs.src");
|
|
Decompileprogssrc = QCC_AddVFile("progs.src", NULL, 0);
|
|
if (!Decompileprogssrc)
|
|
{
|
|
printf("Fatal Error - Could not open \"progs.src\" for output.\n");
|
|
exit(1);
|
|
}
|
|
|
|
QCC_CatVFile(Decompileprogssrc, "./progs.dat\n\n");
|
|
|
|
QCC_CatVFile(Decompileprogssrc, "#pragma flag enable lax //remove this line once you've fixed up any decompiler bugs...\n");
|
|
if (origcopyright)
|
|
QCC_CatVFile(Decompileprogssrc, "//#pragma copyright \"%s\"\n", origcopyright);
|
|
QCC_CatVFile(Decompileprogssrc, "\n", origcopyright);
|
|
|
|
def = DecompileFindGlobal("end_sys_fields");
|
|
lastglob = def?def->ofs+1:1;
|
|
if (lastglob != 1)
|
|
{
|
|
QC_snprintfz(synth_name, sizeof(synth_name), "sysdefs.qc");
|
|
QC_snprintfz(fname, sizeof(fname), "%s", synth_name);
|
|
if (!DecompileAlreadySeen(fname, &f))
|
|
{
|
|
printf("decompiling %s\n", fname);
|
|
compilecb();
|
|
QCC_CatVFile(Decompileprogssrc, "%s\n", fname);
|
|
}
|
|
if (!f)
|
|
{
|
|
printf("Fatal Error - Could not open \"%s\" for output.\n", fname);
|
|
exit(1);
|
|
}
|
|
Decompileofile = f;
|
|
|
|
DecompilePreceedingGlobals(1, lastglob, "");
|
|
}
|
|
|
|
for (i = 1; i < numfunctions; i++)
|
|
{
|
|
d = &functions[i];
|
|
|
|
if (d->s_file != lastfileofs || f == NULL)
|
|
{
|
|
lastfileofs = d->s_file;
|
|
fname[0] = '\0';
|
|
if (d->s_file <= strofs && d->s_file >= 0)
|
|
sprintf(fname, "%s", GetString(d->s_file));
|
|
// FrikaC -- not sure if this is cool or what?
|
|
bogusname = false;
|
|
if (strlen(fname) <= 0)
|
|
bogusname = true;
|
|
else for (o = 0; o < strlen(fname); o++)
|
|
{
|
|
if ((fname[o] < 'a' || fname[o] > 'z') &&
|
|
(fname[o] < '0' || fname[o] > '9') &&
|
|
(fname[o] <'A' || fname[o] > 'Z') &&
|
|
(fname[o] != '.' && fname[o] != '!' && fname[o] != '_'))
|
|
{
|
|
if (fname[o] == '/')
|
|
fname[o] = '.';
|
|
else if (fname[o] == '\\')
|
|
fname[o] = '.';
|
|
else
|
|
{
|
|
bogusname = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bogusname)
|
|
{
|
|
if (*fname && !DecompileAlreadySeen(fname, NULL))
|
|
{
|
|
synth_name[0] = 0;
|
|
}
|
|
if(!TrySynthName(qcva("%s", GetString(d->s_name))) && !synth_name[0])
|
|
QC_snprintfz(synth_name, sizeof(synth_name), "frik%i.qc", fake_name++);
|
|
|
|
QC_snprintfz(fname, sizeof(fname), "%s", synth_name);
|
|
}
|
|
else
|
|
synth_name[0] = 0;
|
|
|
|
|
|
|
|
if (!DecompileAlreadySeen(fname, &f))
|
|
{
|
|
printf("decompiling %s\n", fname);
|
|
compilecb();
|
|
QCC_CatVFile(Decompileprogssrc, "%s\n", fname);
|
|
}
|
|
if (!f)
|
|
{
|
|
printf("Fatal Error - Could not open \"%s\" for output.\n", fname);
|
|
exit(1);
|
|
}
|
|
}
|
|
Decompileofile = f;
|
|
DecompileFunction(GetString(d->s_name), &lastglob);
|
|
}
|
|
}
|
|
|
|
void DecompileProgsDat(char *name, void *buf, size_t bufsize)
|
|
{
|
|
char *c = ReadProgsCopyright(buf, bufsize);
|
|
if (c)
|
|
printf("Copyright: %s\n", c);
|
|
|
|
PreCompile();
|
|
pHash_Get = &Hash_Get;
|
|
pHash_GetNext = &Hash_GetNext;
|
|
pHash_Add = &Hash_Add;
|
|
pHash_RemoveData = &Hash_RemoveData;
|
|
Hash_InitTable(&typedeftable, 1024, qccHunkAlloc(Hash_BytesForBuckets(1024)));
|
|
|
|
maxtypeinfos = 64;
|
|
qcc_typeinfo = (void *)malloc(sizeof(QCC_type_t)*maxtypeinfos);
|
|
numtypeinfos = 0;
|
|
|
|
type_void = QCC_PR_NewType("void", ev_void, true);
|
|
type_string = QCC_PR_NewType("string", ev_string, true);
|
|
type_float = QCC_PR_NewType("float", ev_float, true);
|
|
type_bfloat = type_float;//QCC_PR_NewType("float", ev_float, true);
|
|
type_vector = QCC_PR_NewType("vector", ev_vector, true);
|
|
type_entity = QCC_PR_NewType("entity", ev_entity, true);
|
|
type_field = QCC_PR_NewType("__field", ev_field, false);
|
|
type_function = QCC_PR_NewType("__function", ev_function, false);
|
|
type_function->aux_type = type_void;
|
|
type_pointer = QCC_PR_NewType("__pointer", ev_pointer, false);
|
|
type_integer = QCC_PR_NewType("__integer", ev_integer, true);
|
|
type_bint = type_integer;
|
|
type_variant = QCC_PR_NewType("variant", ev_variant, true);
|
|
type_variant = QCC_PR_NewType("__variant", ev_variant, true);
|
|
type_invalid = QCC_PR_NewType("invalid", ev_void, false);
|
|
|
|
DecompileReadData(name, buf, bufsize);
|
|
DecompileDecompileFunctions(c);
|
|
|
|
printf("Done.\n");
|
|
}
|
|
|
|
char *DecompileGlobalStringNoContents(gofs_t ofs)
|
|
{
|
|
int i;
|
|
QCC_ddef_t *def;
|
|
static char line[128];
|
|
|
|
line[0] = '0';
|
|
QC_snprintfz(line, sizeof(line), "%i(??""?)", ofs);
|
|
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
def = &globals[i];
|
|
|
|
if (def->ofs == ofs)
|
|
{
|
|
line[0] = '0';
|
|
QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, GetString(def->s_name));
|
|
break;
|
|
}
|
|
}
|
|
|
|
i = strlen(line);
|
|
for (; i < 16; i++)
|
|
strcat(line, " ");
|
|
strcat(line, " ");
|
|
|
|
return line;
|
|
}
|
|
|
|
char *DecompileGlobalString(gofs_t ofs)
|
|
{
|
|
char *s;
|
|
int i;
|
|
QCC_ddef_t *def;
|
|
static char line[128];
|
|
|
|
line[0] = '0';
|
|
QC_snprintfz(line, sizeof(line), "%i(??""?)", ofs);
|
|
|
|
for (i = 0; i < numglobaldefs; i++)
|
|
{
|
|
def = &globals[i];
|
|
|
|
if (def->ofs == ofs)
|
|
{
|
|
|
|
line[0] = '0';
|
|
if (!strcmp(GetString(def->s_name), "IMMEDIATE") || !strcmp(GetString(def->s_name), ".imm"))
|
|
{
|
|
s = PR_ValueString((etype_t)(def->type), &pr_globals[ofs]);
|
|
QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, s);
|
|
}
|
|
else
|
|
QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, GetString(def->s_name));
|
|
}
|
|
}
|
|
|
|
i = strlen(line);
|
|
for (; i < 16; i++)
|
|
strcat(line, " ");
|
|
strcat(line, " ");
|
|
|
|
return line;
|
|
}
|
|
|
|
void DecompilePrintStatement(dstatement_t * s)
|
|
{
|
|
int i;
|
|
|
|
printf("%4i : %s ", (int)(s - statements), pr_opcodes[s->op].opname);
|
|
i = strlen(pr_opcodes[s->op].opname);
|
|
for (; i < 10; i++)
|
|
printf(" ");
|
|
|
|
if (s->op == OP_IF_I || s->op == OP_IFNOT_I ||
|
|
s->op == OP_IF_F || s->op == OP_IFNOT_F ||
|
|
s->op == OP_IF_S || s->op == OP_IFNOT_S)
|
|
printf("%sbranch %i", DecompileGlobalString(s->a), s->b);
|
|
else if (s->op == OP_GOTO)
|
|
{
|
|
printf("branch %i", s->a);
|
|
}
|
|
else if ((unsigned)(s->op - OP_STORE_F) < 6)
|
|
{
|
|
printf("%s", DecompileGlobalString(s->a));
|
|
printf("%s", DecompileGlobalStringNoContents(s->b));
|
|
}
|
|
else
|
|
{
|
|
if (s->a)
|
|
printf("%s", DecompileGlobalString(s->a));
|
|
if (s->b)
|
|
printf("%s", DecompileGlobalString(s->b));
|
|
if (s->c)
|
|
printf("%s", DecompileGlobalStringNoContents(s->c));
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
void DecompilePrintFunction(char *name)
|
|
{
|
|
int i;
|
|
dstatement_t *ds;
|
|
dfunction_t *df;
|
|
|
|
for (i = 0; i < numfunctions; i++)
|
|
if (!strcmp(name, GetString(functions[i].s_name)))
|
|
break;
|
|
if (i == numfunctions)
|
|
{
|
|
printf("Fatal Error: No function names \"%s\"\n", name);
|
|
exit(1);
|
|
}
|
|
df = functions + i;
|
|
|
|
printf("Statements for %s:\n", name);
|
|
ds = statements + df->first_statement;
|
|
while (1)
|
|
{
|
|
DecompilePrintStatement(ds);
|
|
|
|
if (!ds->op)
|
|
break;
|
|
ds++;
|
|
}
|
|
}
|
|
|
|
|