Press „… to throw bananas to the chimps.
31/10/2021, 2:23:56 am 🔗
Favs: 8
Retweets: 0
← @dazabani Twitter archive
Press „… to throw bananas to the chimps.
31/10/2021, 2:23:56 am 🔗
Favs: 8
Retweets: 0
ok, binja was alright, but let’s try ghidra this time.
31/10/2021, 3:57:44 am 🔗
Favs: 1
Retweets: 0
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
instances of memcpy encountered so far: 2
01/11/2021, 10:30:02 pm 🔗
Favs: 2
Retweets: 0
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
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
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
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
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
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
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
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
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
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
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
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
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
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
progress! given the initial seed (two dwords), plus dumped map data to know where obstacles are, we can now simulate where gizmos & gadgets will spawn parts, and what parts they will be.
here we have bananas and a racing bee decal.
https://bitbucket.org/delan/ssgesus/src/e1b65d505154c3047d6113610bbac04ac9b2bc64
05/11/2021, 3:51:01 am 🔗
Favs: 2
Retweets: 0
not sure i’ve ever seen this hang prompt before?
05/11/2021, 7:11:57 am 🔗
Favs: 1
Retweets: 0
oops! all parts (and one door frame)
05/11/2021, 7:33:29 am 🔗
Favs: 1
Retweets: 0
i cannot stress this enough:
parts
05/11/2021, 7:36:10 am 🔗
Favs: 2
Retweets: 0
oh exploitable for a death generator @Foone
05/11/2021, 8:20:40 am 🔗
Favs: 2
Retweets: 0