← @dazabani Twitter archive

@delan@kolektiva.social + cohost.org/delan

@dazabani

Press „… to throw bananas to the chimps.

31/10/2021, 2:23:56 am

Favs: 8

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

ok, binja was alright, but let’s try ghidra this time.

31/10/2021, 3:57:44 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

i think i wanna figure out how the parts placement works, because it’s mandatory for full game world record. not even the puzzle rng fluke is enough, and i can no longer reproduce that anyway (except perhaps on my old windows install).

puzzle rng thread: https://twitter.com/dazabani/status/1335564050785189888

31/10/2021, 4:17:19 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

first i established that all parts are placed when you enter the workshop for a vehicle for the first time, that is, when the blueprint opens automatically.

then with cheat engine and unknown + unchanged + changed, i found some bytes that change when picking up this test part.

31/10/2021, 5:17:39 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

ohh! it was a pointer to some very nearby game state, which was getting nulled out when picking up the part.

and if i change the pointer back, then go through the open door and come back out, the part returns! though trying to pick it up again hangs.

but for now, time for bed!

31/10/2021, 5:35:56 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

aaaaand we’re back.

starting at 4657F0h, because the memory before that changes continuously while the game is in focus, here’s what changes when picking up those two parts.

hmm, 22F87D0h and 22F8850h look like this. part definitions perhaps?

31/10/2021, 2:38:29 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

ok so my ghidra is cooked, and i have no idea how to fix it. for one, the debugger keeps opening with no breakpoints panel and no way to get it back, unless i delete and import default tools each time. and worse, i can no longer patch the running process.

https://twitter.com/dazabani/status/682981235384188928

01/11/2021, 2:03:44 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

recreating my project didn’t help, nor did most of the things i tried, but wiping all of my global state (~/.ghidra + %LOCALAPPDATA%\Ghidra) fixed the first problem.

as for live patching… well…

01/11/2021, 2:25:47 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

new approach. there are a bunch of things that ask for random numbers, and parts placement is one of them. see if “random” is all zero, the game still works, but the parts are always placed like this.

let’s ret out each of the callers until we find something interesting.

01/11/2021, 3:17:37 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

the first two had no noticeable effect on parts or even puzzles. but with 413004h, starting a vehicle gives us this spooky empty workshop, and the game is no longer *entirely* responsive.

01/11/2021, 3:28:54 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

while it hangs, let’s try patching just the random numbers in this function to zero. changing the three sites as follows…

call 454254h
cdq
idiv ecx → xor eax,eax
jmp .out → xor edx,edx
xor edx,edx
.out:

…and undoing the ret, we get different places, different parts. bummer.

01/11/2021, 4:16:53 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

if we ret out 417532h, which i thought was parts-related yesterday, it has no effect on its own. but if we *also* ret out our first caller (43D8A5h), then we have different places, but picking them up in any order goes like this:

1. fat envelope
2. complete hang

hmm. *hmm*.

01/11/2021, 4:45:46 pm

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

when i said first caller, i lied. 43D8A5h is actually the first caller’s only caller. so if we instead ret out the first caller and the fourth caller… no parts! anywhere!

01/11/2021, 5:00:40 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

on closer look, there appear to be two parts, now in fixed places on the reverse (big) side. there’s one in value town, and one near the bottom right of the map, and guess what? fat envelope and crash.

so without these functions, we’ve achieved fixed places and broken parts \o/

01/11/2021, 5:11:25 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

that fourth function is actually a generic utility that’s called by said first caller’s only caller.

not loving how ghidra’s decompiler keeps giving me three bogus arguments (borlanddelphi __register), nor the missed opportunities to simplify if-do-while to while, but no biggie.

01/11/2021, 5:56:06 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

if we make this function always fill the given array with [0,count) without shuffling anything, we get two parts in the obverse (small) side, then a bunch of parts in each of the four reverse (big) quadrants.

notably, they are sorted by the kind of part, then by worst to best.

01/11/2021, 7:03:46 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

ok, so 43D8A5h is responsible for generating a given number of part ids in random order. if that number is more than the number of available parts for the current vehicle, the remaining ids are bananas (1000).

not yet sure how duplicate parts or paints/decals are handled here.

01/11/2021, 7:24:01 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

i’ve been seeing a lot of expressions of the form “(uint)x & 0xffff0000 | (uint)y” in the ghidra decompiler, and i thought they might be some kind of special world-related packed integer format, but now i think it’s just due to how x86 does 16-bit writes in 32-bit code.

01/11/2021, 8:14:35 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

i strongly suspect that the borlanddelphi setting chosen by ghidra has made it assume everything’s in borland register convention (a-d-c-stack ltr), whereas a lot of these functions seem to be stdcall (stack rtl) or maybe pascal (stack ltr), so let’s reanalyse as borlandcpp.

01/11/2021, 9:03:44 pm

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

instances of memcpy encountered so far: 2

01/11/2021, 10:30:02 pm

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

no wait, that was memset. anyway i’ve made some progress. i’ve identified quite a few functions and types, including some i’m fairly confident about:

• zCheckRectIntersects
• zWorldGetNextAvailableObject
• zWorldGetNextPartId
• zWorldRecyclePart

02/11/2021, 2:51:01 am

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

fun fact while i’m slogging through the massive “world place or recycle part” function… there’s a debug menu in the resources! not sure how to get to it yet.

02/11/2021, 2:52:52 am

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

day 3. i’m not enjoying how ghidra struggles to show me fresh memory while debugging, making the dynamic stack listing and memory watches useless.

on my other monitor, i’ve resorted to using cheat engine memory windows to keep an eye on everything. note the different memory.

02/11/2021, 11:55:40 pm

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

finally making progress in this gnarly mystery function. for one, j (eax) gets movsx’d to edx, then edx is reused for x, which strains the local renaming support.

but figuring out the world structure?

1. ignore the decompiler
2. realise current/next werer four(!) types not one

03/11/2021, 12:59:09 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

there’s gotta be a better way to watch a stack frame than manually highlighting [esp,ebp) in cheat engine. anyway good news, i reached the end of that function, and now i’m on zWorldPlaceOrRecyclePart!

03/11/2021, 1:35:00 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

ok, i think i’ve figured out how parts are placed in gizmos & gadgets! some background…

for any given vehicle/level, the world is divided into areas: up to three obverse areas [1,4), and up to twelve reverse areas [4,16), but i’m not 100% sure about the order in sets of four.

03/11/2021, 3:41:14 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

the first phase is to generate a list of part ids. each level has a predefined number of parts to place in each area. some of these parts will be vehicle parts, but any excess parts will be bananas (for sedating chimps).

03/11/2021, 3:50:29 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

to generate a list of part ids, we take [0,vpc) where vpc is the number of vehicle parts, pad it with bananas (part id 1000) up to the world’s total required parts count across all areas, then shuffle the list by swapping two random members ten times.

03/11/2021, 3:56:58 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

after generating that list of part ids (and loading the details about five different kinds of world objects), the second phase is to place those parts in the world.

the locations are random, but the ids are taken sequentially from that list. this will make things easier for us!

03/11/2021, 4:04:35 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

for each area, we call a function to take the next apc part ids, where apc is the area’s part count, and place them randomly in the world.

to place them randomly in the world, we generate a shuffled list of up to 90 possible locations, numbered in row-major order.

03/11/2021, 4:08:04 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

like part ids, locations are taken sequentially, one for each part that needs to be placed. but not all locations are actually desirable for parts, so we may need to discard some locations and try more!

locations are undesirable for parts if they fail any one of three tests.

03/11/2021, 4:12:41 am

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

each of the three tests is for whether the location intersects with any of three kinds of world objects. these are probably:

1. not sure, but probably irrelevant to world gen

2. gaps in the level, through which the player can fall

3. vents, paddles, and trampolines

03/11/2021, 4:15:41 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

object kinds 2 and 3 are part of the level design, so if we can figure out the exact rng state at any point in this process, then we know exactly how the whole parts process will play out (both start→point and point→end).

sadly we don’t… but we can guess!

03/11/2021, 4:23:32 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

at the start of every level, there are always two parts visible in area 1. their locations effectively reveals some bits of rng state, greatly narrowing the possibilities.

this forms the basis of how @OddtomSR planned the route for his world record!

https://www.twitch.tv/videos/141015187

03/11/2021, 4:28:07 am

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

not sure i’ve ever seen this hang prompt before?

05/11/2021, 7:11:57 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

oops! all parts (and one door frame)

05/11/2021, 7:33:29 am

Favs: 1

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

i cannot stress this enough:

parts

05/11/2021, 7:36:10 am

Favs: 2

Retweets: 0

@delan@kolektiva.social + cohost.org/delan

@dazabani

oh exploitable for a death generator @Foone

05/11/2021, 8:20:40 am

Favs: 2

Retweets: 0