foone,
@foone@digipres.club avatar

I should make a big sign reading "it has been X days since Foone started reverse engineering a new thing", and wire it up to my Ghidras.

Today it got up to 1 days! then I reset it again

foone,
@foone@digipres.club avatar

today it's 1997's Take No Prisoners by Raven Software

wildweasel,

@foone hell yes.

smallsees,
@smallsees@social.dropbear.xyz avatar

@foone the picture has very Duke Nukem vibes

foone,
@foone@digipres.club avatar

definitely built on the Quake engine. some error messages match exactly.

foone,
@foone@digipres.club avatar

good to know this engine supports TRANS FLAGS
(although it predates the most common pride flag, this one: 🏳️‍⚧️

foone,
@foone@digipres.club avatar

why's every game got to have a scripting engine embedded in it these days*?

stop being a coward and just write your game in a scripting language in the first place

  • about 1995-2010
glassbottommeg,
@glassbottommeg@peoplemaking.games avatar

@foone ahhhhh. The good times.

Same time window: "you can write a game entirely in C#, nobody is even going to try"

Rairii,
@Rairii@haqueers.com avatar

@foone i think pokemon gen3 has about 3 different bytecode vms?

foone,
@foone@digipres.club avatar

So the scripting components seem to be in SCRIPTS.RPC (Raven PCode?) inside TNP.VPK
which you can extract with https://aluigi.altervista.org/bms/take_no_prisoners.bms

foone,
@foone@digipres.club avatar

it starts with an "RCC-OBJ" header .
Searching that in the EXE gives the function FUN_004546a0 which seems to load these files, including "not a valid pcode object file" errors and checks the file version against "2.17".

foone,
@foone@digipres.club avatar

the VPK code apparently lives in "C:\source\outrage\Res_man.c".

Outrage is an interesting directory name. A codename or an earlier name?

foone,
@foone@digipres.club avatar

ahh, mobygames says it has the working titles of "Outrage" and "RIOT"

foone,
@foone@digipres.club avatar

so it seems to solve the memory problem of loading a bunch of datafiles at start by having only one buffer, into which it loads everything? interesting idea.

foone,
@foone@digipres.club avatar

so there appear to be a bunch of console commands that no one documented? fun.

foone,
@foone@digipres.club avatar

so the function at 00450e00 registers some pcode debugging commands:
pc_stats
pc_listServices
pc_top
pc_dump
pc_runService
pc_spawn
pc_lookupip
pc_posttoall
pc_postto

foone,
@foone@digipres.club avatar

so pc_stats, loaded into level 1, says there are 345 services loaded, 1242 routines, 235 primaries, 51 nats, 143 globs, and 2670 strings

foone,
@foone@digipres.club avatar

it uses a tagged malloc system? interesting. you don't just do malloc(1600), you do tagmalloc(1600,"concmd")

presumably so if it runs out of memory it can better debug what's using up all the ram

foone,
@foone@digipres.club avatar

the game supports up to 15 joysticks.
16 would be ridiculous

RnDanger,

@foone
... a bit too much?

TheColourOfFear,

@foone this implies that at some point they needed to support 8, right?

foone,
@foone@digipres.club avatar

correction: 15 joystick buttons

foone,
@foone@digipres.club avatar

ahh, seems the tagged malloc is also in quake, but under a different name

foone,
@foone@digipres.club avatar

apparently FUN_00454c50 loads pcode data per-level, so there apparently is per-level code as well

foone,
@foone@digipres.club avatar

textures seem to be 8 bits per pixel but I think they've got some per-line metadata (like Doom's sprites) and also it's one of those formats that includes mipmaps in the texture file

foone,
@foone@digipres.club avatar

so it's weirder: images like this one do not have more complex metadata, but other images do.
So only some files do.

foone,
@foone@digipres.club avatar

and this per-file level stuff I was looking at turns out to be savegame related. Savegames can have code associated with them. Very weird.

foone,
@foone@digipres.club avatar

probably something like "we save the state of all scripting engine globals by writing them out to a bytecode file that just sets them again"

foone,
@foone@digipres.club avatar

ugh. I think their bytecode has a "DPMI" instruction. that better not mean what it sounds like

foone,
@foone@digipres.club avatar

so at 00515328 there's a char*[137] for all the opcodes.

foone,
@foone@digipres.club avatar

DAT_0051c210 = DAT_0051c210 * 214013 + 2531011;

magic numbers! that's the MSVC LCG for rand()

foone,
@foone@digipres.club avatar

once again I can't seem to force ghidra to handle switching on an enum properly

JonBright,

@foone I recently had a similar issue (although on ARM). Ghidra 10.3.1 had a fix which, if memory serves, was not ARM-specific.

foone,
@foone@digipres.club avatar

anyway FUN_00455a20 seems to be the inner loop of the interpreter. it's a big ol' switch on the opcodes

foone,
@foone@digipres.club avatar

you've been reverse engineering too much when you see these sorts of numbers and instantly know what they are.

chrisisgr8,

@foone ..... what are they :3

foone,
@foone@digipres.club avatar

@chrisisgr8 floating point numbres.
0x3f800000 in particular is 1.0

stupidhoroscope,

@foone I did the whole "Oh god, those numbers look familiar, where the hell do I know them from?" thing, before plugging it into a hex -> binary converter and immediately knowing what it was.

foone,
@foone@digipres.club avatar

there's a string "VCR Connection not available" which I assume stands for something boring and video game related and not the coolest hidden feature of all time

foone,
@foone@digipres.club avatar

found an apparent exact match for Z_Free from the quake source, except they renamed it MM_Free

foone,
@foone@digipres.club avatar

they didn't even change the zoneid, it's still 0x1d4a11

foone,
@foone@digipres.club avatar

which might be a hex pun.
id for all?

foone,
@foone@digipres.club avatar

apparently the class called Naturals has a linked class called Unnaturals.

Personally I'm just waiting for the Big Naturals to show up

foone,
@foone@digipres.club avatar

I think Visual C++ 5 or 6 has a very annoying optimization: if you have a loop that increments a number and then at the end you only ever use that number plus one, it instead counts DOWN and NOTs the result

combinatorial_explosion,

@foone Visual C++ 5 was the source of so much pain at work when I would port stuff from SGIs to PCs. At a later job I suggested throwing in a free copy of a newer C++ compiler with our (insanely expensive) hardware so I didn't have to jump through ver. 5's asinine hoops. I am still convinced we would have saved money.

foone,
@foone@digipres.club avatar

this is a sort of thing that happens surprisingly often, thanks to null-terminated strings and the need to malloc space for the NUL terminator. and I guess starting the integer at 0xFFFFFFFF and then subtracting one for each loop and then doing a bitwise not is cheaper?

phenidone,
@phenidone@mstdn.social avatar

@foone but surely it's cheaper to start at 1 and count up? Don't need the NOT at all then.

foone,
@foone@digipres.club avatar

an example.
uVar3 here is counting up how many chars are in PcVar7, except MSVC inverted it into counting how many chars are not in it, or something

foone,
@foone@digipres.club avatar

huh, I didn't realize even WinQuake had some x86 assembly. I thought by then they'd fully transitioned to C/C++. Maybe that was Quake II?

foone,
@foone@digipres.club avatar

found a vaguely undocumented subcommand of a command:
the game will tell you that there's a tr_dump command (for dumping the "tractors") but it won't tell you that there's a hidden flag: "pos0", which gives different data than "position", the documented flag

foone,
@foone@digipres.club avatar

the results are the same for my sample object, though

foone,
@foone@digipres.club avatar

hey there's another.
"all" is undocumented but will give you all the flags at once

foone,
@foone@digipres.club avatar

so the game apparently always has networked support enabled at some level, and when you ask it how many players are in the game (by using the SV_INFO console command), it counts the players then subtracts one before telling you how many are there.

Otherwise, singleplayer games would say there are two players in the world right now. Wait, what?

foone,
@foone@digipres.club avatar

yeah this is definitely visual C++.
I should have recognized its foul stench when I opened the EXE

foone,
@foone@digipres.club avatar

So say you're given this structure, with a compiler set to pack it with no padding:

struct foo{
int a; // offset 0
byte b,c,d; // offsets 4,5,6
int e; //offset 7
byte f; //offset 8
int g; //offset 12
int h; //offset 16;
byte[56] i;
};

foone,
@foone@digipres.club avatar

and there's an array defined:
struct foo foos[100];

and there's some code that's gonna loop through the foos and needs to check three members of the foo structure: a, e, and h. Like so:

for(int i=0;i<100;i++){
if(foos[i].a>0 && foos[i].e==7){
printf("%d\n", foos[i].h);
}
}

foone,
@foone@digipres.club avatar

now the compiler is gonna want to optimize this. The unoptimal way to do it is to just get the address of foos and then calculate &foos + sizeof(foos) * i + offset_to_member each time it needs to read foos[i].a or foos[i].e

foone,
@foone@digipres.club avatar

so the obvious way to do this is to turn it into a pointer.
The pointer can just be increased by sizeof(foo) each iteration, and we don't need to do any calculations, other than doing current pointer + offset to member. That's easy and fast on x86!

foone,
@foone@digipres.club avatar

but this pointer has to, well, point somewhere.
Logically it "should" point to the start of each struct foo (which also happens to be foo.a), but there's no reason it couldn't point elsewhere.

foone,
@foone@digipres.club avatar

Like if the loop just needed to check the foo.e member, we could initialize the pointer at the start to &foo[0].e, then increase it by sizeof(foo) each time. Then we don't need to do any pointer math to look up the address of foo.e!

foone,
@foone@digipres.club avatar

so fun fact, MSVC (at least back in the mid-90s days) does do this but not in any way you'd expect.

foone,
@foone@digipres.club avatar

given we need to read a at offset 0, e at offset 7, and h at offset 16, you'd think it wouldn't bother using an offset pointer. just pointing it at a (aka +0) should be fine. each time we calculate e as ptr+7 and h as ptr+16, right?

foone,
@foone@digipres.club avatar

SURPRISE it points the pointer at e

foone,
@foone@digipres.club avatar

so each iteration it reads ptr-7, ptr, and ptr+9

foone,
@foone@digipres.club avatar

why? I have no fucking idea.

my guess is that it's trying to make smaller code by using encoding variants that can only encode small offsets? and so it tries to vaguely aim the pointer in the middle of the structure, just snapping it to the nearest needed member for optimization's sake

foone,
@foone@digipres.club avatar

anyway @pjf pointed out I messed up my math in the struct to get the offsets so this probably makes a lot less sense than I intended

foone,
@foone@digipres.club avatar

god I love finding a function that is something like DebugAssert()
and half the functions in the game are defined as:

void FUN_23854972(char* param1){
if(param1==NULL){
DebugAssert("SetupLevel: filename is NULL!");
}
}

then I just search references to DebugAssert() and WHOOPS a bunch of function names

foone,
@foone@digipres.club avatar

this is why all evil game engines include shit like:

void SetPlayerPosition(Player* p, Vertex3 pos){
if(p==null){
DebugAssert("CopyScriptFilenames: Script Count is 0!");
}
}

foone,
@foone@digipres.club avatar

excited to realize that by combining the res_idlist and res_idstat commands I can

  1. get a list of filenames in the VPK
  2. get their size + offset

allowing me to dump the VPK files!

then I remembered: I've already dumped the VPK files

pistonminer,

@foone But consider: have you dumped what isn't a VPK file? :p
https://pistonminer.github.io/blog/2022/03/25/vpk-undelete

(mostly kidding since I'm guessing this variety of VPKs is much earlier/unrelated in heritage to the Source engine variety and the packing tool used presumably isn't that sophisticated in this case)

foone,
@foone@digipres.club avatar

@pistonminer very neat, hadn't heard of that!
(and yeah, completely unrelated format)

chainlinq,

@foone ugh, some of my worst conversations with a bad boss would have him insisting that I was not very good because I never implemented tricks like this and me explaining to him that they now had these things called optimizers and if you tried to do your own tricks you would confuse it and God knows what it would think you were trying to do and then try to optimize.

theproski,

@foone extension .bbw?

ToddVierling,

@foone
interactive analog horror

DropTableFoxes,
@DropTableFoxes@stackunderflow.com avatar

@foone Could be related to this https://www.quakewiki.net/console/console-commands/quake-console-commands/#p--record which creates a file "quake.vcr" with debug parameters in it.

I don't know what kind of "connection" it'd be referring to though.

hourofoblivion,

@foone Demo recording to VHS tape? Hell yeah!

brouhaha,
@brouhaha@mastodon.social avatar

@foone What, no HCF? For shame!

foone,
@foone@digipres.club avatar

@brouhaha I know, right? what if you need to self-destruct the game to deny it from the enemy?

Ephraim_Bane,
@Ephraim_Bane@wetdry.world avatar

@foone Dingle Pingle Mingle Ingle

freemin7,
@freemin7@mast.hpc.social avatar

@foone all my ram is used by tags 😭😭😭😭

moira,
@moira@mastodon.murkworks.net avatar

@foone that's... really neat, actually. i like that.

chainlinq,

@foone might be for memory reaping. Roll your own memory management systems were a thing in the mid 90s

hourofoblivion,

@foone Someone on the forum had an issue where they couldn't open the pdd because the keybinding is fixed to TILDE and there's no option in the menu to change it. Binding sb_togglepdd in the usercfg fixes it but it's strange why they don't show it

hurgusburgus,

@foone tcrf edit time eh?

cmarqu,
@cmarqu@dresden.network avatar

@foone A good directory name to store all your technical debt in.

hourofoblivion,

@foone I wonder if RCC is Ravens take on QuakeC, given the raw hex data of something like progs.dat has a bunch of function names at the start whereas scripts and default.rpc have them in the middle

brocolie,

@foone I... this is literally what we did at my last workplace

don't do it, that way lies madness

lilstevie,

@foone and now I want to see a game written in BASH script 😂

static,
@static@aus.social avatar

@foone I don't think I am saying anything you don't know, but I would think a lot of games would benefit from their own scripting language to direct and control interactions. A story-oriented game like GTA IV would have most of the game-play logic in it's own scripting language.

Randomfrequency,

@foone you don't remember QuakeC?

foone,
@foone@digipres.club avatar

@Randomfrequency this engine is basically using a variant of quakeC!

Randomfrequency,

@foone modern day all my game dev friends talk about having to use Lua to do the same

G33KatWork,

@foone Check out the XBox game "Pirates of the Carribean"! cough archive.org cough
When I first unpacked it years ago, I thought they delivered its source code with the game on the disc, but it turns out they are using C as something like a scripting language that they interpret during runtime.

nitrofurano,
@nitrofurano@ciberlandia.pt avatar

@foone are there polyamory and aromantic flags as well? im struggling tofind them...

hourofoblivion,

@foone When I wrote up my steam guide for the game I mistakenly believed it was modified Quake II because it came out in 1997 and had the same console font 🤦

bison,
@bison@mastodon.social avatar

@foone a bot that only tweets.... toots 0?

  • All
  • Subscribed
  • Moderated
  • Favorites
  • random
  • ethstaker
  • DreamBathrooms
  • InstantRegret
  • tacticalgear
  • magazineikmin
  • Youngstown
  • thenastyranch
  • mdbf
  • slotface
  • rosin
  • modclub
  • kavyap
  • cubers
  • osvaldo12
  • megavids
  • khanakhh
  • cisconetworking
  • Durango
  • everett
  • ngwrru68w68
  • Leos
  • normalnudes
  • GTA5RPClips
  • tester
  • provamag3
  • anitta
  • JUstTest
  • lostlight
  • All magazines