Brutkey

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

I love reverse engineering by breaking the code.

I confirmed which function is drawing the background stars by replacing the first byte of it with CB (RETF)


Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

ghidra, if you know this code is at 231e:0bbe and it's MOV BYTE PTR CS:[0x44], 0

WHY THE FUCK do you think that points to 1fb9:40b4? it's CS! CS:44! and CS is 231e. LERN TO MATH

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

and if I double click the 44, I end up at 00:44

WRONG AGAIN, GHIDRA

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

231e:0002? that's not... No!

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

the game stores the activation status for 39 missions.

the game only has 17 missions

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

got damn ghidra. I have a disassembly that shows
CMP BYTE PTR [0x0], 0x1
and a decompilation that says:
if(*(char*)0x0 == 1){

I have told ghidra that DS for this segment is 1019. if I click on the 0x0 in the decompilation, I end up at 0000:0000. WRONG
if I click on on the [0x0] in the disassembly, I end up at 231e:0. ALSO WRONG

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

also yeah, fun segmented memory thing. NULL is a valid memory address and here it's used to determine if the mouse is enabled.

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

*puVar4 = *puVar4;

NO

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

the matching disassembly:

MOV AH, byte ptr [DI]
MOV byte ptr ES:[DI], AH

DO YOU KNOW WHAT SEGMENT PREFIXES MEAN, GHIDRA?

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

found the cheat code (it's well known (it's in the MANUAL), but I hadn't seen the (machine/decompiled) code until now.

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

found a copy-paste bug!

the code that draws these three red dots miscolors the center of the rightmost one

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

the code looks like:

MOV AL, 0x34
MOV byte ptr ES:[button_offset + 0xa07 ],AL
MOV byte ptr ES:[button_offset + 0xb46 ],AL
MOV byte ptr ES:[button_offset + 0xb48 ],AL
MOV byte ptr ES:[button_offset + 0xc87 ],AL
MOV AL, 0x32
MOV byte ptr ES:[button_offset + 0xb47 ],AL

but for the 3rd one, that second MOV AL,0x32 is instead MOV AL, 0x34.

that's a copy-paste mistake, that is.

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

so if your gun is on 1-laser mode, it fires every 13-somethings (frames?)
with it on 2-laser mode, it's every 19 somethings
on 3-laser mode, it's every 30 sometimes.

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

yeah it's frames. at least logic frames, I don't think this game unhooks logic from framerate

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

huh! this game doesn't implement highlighted text by the usual way of just drawing the text in a different color.

it instead iterates over the pixels in framebuffer and increases (or decreases, in the case of de-highlighting) the palette index

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

POP AH
PUSH AH
POP AH

IN OR OUT, MAKE UP YOUR MIND!

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

so I went and beat the game while having a breakpoint on a function that tells me conversation IDs:

1: Emer Kane
2: Titus scientist
3. Gimlak
4: Titus (planet)
5: Jelina (planet)
6: Death robots
7: Government
8: Modian
9: Devon Manta
10: Kima
11: Rigelian Supply Depot
12: Titus robot
13: Titus hyperdrive ship
14: Zookeeper alien
15: Rigelian with stolen face
16: Galaen (planet)

So... something is missing!

EDIT: Found 'em

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

why write

bool is_joystick_button_down(int button)

when you can instead write two nigh identical functions:

bool is_joystick_button_1_down()
bool is_joystick_button_2_down()

?

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

I know programmers who don't use copy paste and they're all cowards

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

the only difference between the code is if it ANDs the result against 0x10 (button 1) or 0x20 (button 2).

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

years ago I asked for the source for this game in the hopes of building a modernized version. I think I'm starting to see why I never got the source: it's slightly crap

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

this code does the following:
sets joy_left to false
sets joy_right to false
reads the state of the joystick
if this fails, it sets joy_left to false, joy_right to false, then returns.

YOU DID THAT TWICE. IT'S ALWAYS FALSE

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

this is in the function read_joystick_analog_x, which is the same as read_joystick_analog_y with one byte changed

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

it keeps track of how many enemies/planets are on screen by adding 2 to a global variable in the render_enemies_and_missiles function.

but why two? suspicious.

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

on 16-bit system, 2 is a very suspicious number

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

yep there's an array of pointers!

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

this file loading code is broken. it tries to load the file in 64kb chunks but it only saves the size as a 16bit variable, so a 64kb file will be recorded as 0, and any bigger file will break.

but fortunately the file is only 11kb so it works, as the read-second-chunk behavior never triggers

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

at least this is a compression algorithm that isn't too complicated: it's simple RLE.

0xFF is a marker, and is followed by a byte of repeat count(-1) and a byte of value.

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

although technically this is the second time I hacked this compression, I did figure this out already back in, like, 2013?

Foone🏳️‍⚧️🏳️‍⚧️
@foone@digipres.club

this game uses a ton of hardcoded offsets into data files. I wonder if this was done with linker nonsense or if they had to be manually hardcoded in.

the latter is frighteningly possible

Kevin Karhan :verified:
@kkarhan@infosec.space

@foone@digipres.club that's good, I guess...