An addon that hooks itself in
Code: Select all
#praga progs_dat "someaddon.dat"
#pragma target FTE
#define MAINPROGS 0 //sometimes a constant just makes stuff easier.
#define ANYPROGS -1 //checks every progs for whatever simple (uses first symbol found)
extern void() test123; //function auto-imported from another progs.
var void(float vwidth, float vheight, float notmenu) origupdateview;
void(float vwidth, float vheight, float notmenu) wrapperupdateview
{
print("prewrapper\n");
origupdateview();
print("postwrapper\n");
};
float(float arg) calledfrommainprogs =
{
print("calledfrommainprogs was called\n");
return arg+1;
};
void() init =
{
print("Addon init called\n");
void(float vwidth, float vheight, float notmenu) *ptr = externvalue(MAINPROGS, "&CSQC_UpdateView");
origupdateview = *ptr;
*ptr = wrapperupdateview; //zomg the csprogs probably defined it (implicitly) as const.
test123();
};
Code: Select all
#pragma progs_dat "csprogs.dat"
float somehandle;
void(float apilevel, string enginename, float engineversion) CSQC_Init =
{
somehandle = addprogs("someaddon.dat");
if (somehandle < 0)
print("it failed, dude\n");
};
var void CSQC_UpdateView(float vwidth, float vheight, float notmenu) =
{ //put what you want in here.
print("regular updateview\n");
if (somehandle < 0)
return;
if (2 != (float)externcall(somehandle, "calledfrommainprogs", 1)) error("gah"); //a lazy call
var float(float) foo = externvalue(somehandle, "calledfrommainprogs"); //yay for caching something
if (2 != foo(1)) error("gah"); //no slower than any qc function call, also supports up to 8 args rather than 6.
};
void() test123 = //some test function for the addon to call
{
print("testing 1... 2... 3...\n");
};
for lack of a better name, a 'friked' progs is one that has been compiled with string constants stripped from the progs.
with fteqcc, you should never use -O3 for addons. -O2 is the highest optimisation level supported for addons.
so yeah, strings...
in qc, string immediates are stored as an offset from the string table, the first byte of which is always empty, thus the null string is always empty.
side note: in 32bit processes, this string offset thing works well enough to refer to any string in the engine's memory space, and this is why you can't just recompile vanilla quake as 64bit (because pointer->string->pointer offsets get truncated and the pointers end up pointing to the wrong place with a crash when its executed).
anyway, this string table thing is the text blob that you can normally read inside the progs file. all qc strings are expressed relative to that (unless the engine uses some of the high bits to denote special string types from eg strzone or tempstrings or whatever).
this of course is a problem when there are logically multiple string tables, which would make strings in one progs unreadable gibberish while running code in another, so to fix that fteqw loops through some 'defs' table within the progs and biases all the strings that it finds to be relative to the string table within the main progs, allowing entity fields to be read as actual strings, with no special conversions needed for string args between addons, etc.
obviously this only works when the defs table actually still includes all the string immediates - if that data was stripped out then those embedded strings will be gibberish. and that's what fteqw is noticing and complaining about in an attempt to avoid total malfunction. obviously you can still end up with false positives if you don't have any string immediates, but that generally only happens in small test mods...
note that stripped string immediates is fine within the main progs, as there's no need to rebias when they're already set correctly anyway.