fun fact:
This game I'm trying to reverse engineer defines one thousand, one hundred, and fifteen v-tables!

second fun fact:
I'M ABOUT TO PULL OUT ALL MY HAIR

on the positive side, I think I've discovered built-in modding capability that's gone unnoticed for 13 years

I've moved on to ADVANCED reverse engineering techniques.

I emailed the company that made the game asking them for the specs/SDK for their engine.

oh sweet lord this code uses TEMPLATES.
like C++ reverse engineering isn't bad enough, now I have to deal with TEMPLATES?
vtables, templates, and a compiler that aggressively inlines.
this is SO MUCH FUN

OH GOODY they have a sentinel value in their vector implementation.
is it NULL?

nope. it's '#EOF'. as a pointer.
(void*)0x23454f46

This is safer than it sounds: The Wii's virtual addresses are in the 0x80000000 - 0xD3FFFFFF range, with 32 kb of registers up in 0xCD000000.

Nothing is ever mapped at 0x23000000.

I want to ask the developers so many things.
like why they're calling getBinaryData on a GuiAssetProvider (and providing a GuiResourceLock) to load a TEXT FILE.
I wonder if there's a way to get a zlib chunk to compress into a specific size. like a harmless way to pad it out? because otherwise I'm gonna have to figure out some more info about the format of these bundles, as any changes I make will make the file-regions change size.

turns out to be a non-issue. I modified the data to have more redundancy (overwrote one filename with another) and now it got bigger

what

it's mocking me. I swapped "classic" for "hawaii" and it got 2 bytes bigger.
so I tried "ny" instead. much shorter!
now the file is 6 bytes bigger.
boo. patching doesn't seem to work.
I'll have to understand the BundleManager system a lot better to figure out why that is, or if I am simply wrong. Anyways, I can always patch the files on the disc.
hot take: if "(int*)(this + 0x56990)" ever appears in the decompiled code, YOUR CLASS IS WAY TOO FUCKING BIG
ugh. I think I found an ad-hoc CRC function and a bloom filter. aka NOTHING THAT'S GONNA BE FUN TO REVERSE

and the CRC function matches the one from this CTF challenge.
what the heck. did someone put reverse engineering in my reverse engineering?

https://jctf.team/Mossad-Challenge-5779/

Mossad Challenge - 5779

Writeup for the 2019 Mossad challenge. Solved by Dvd848 YaakovCohen88.

JCTF.team
I changed one register during boot to enable debugging, and it crashed the game and then my emulator.
10 out of 10, would recommend again
hacking on big-endian code/data after so long on little-endian is weird.
why are the numbers in the right order? that's wrong. they're supposed to be all backwards!
wait does this really store chunk sizes as 24-bit integers in actual-size-minus-1 form?
I've got the decoded puzzles open in my text editor and IT ASKS TOO MANY QUESTIONS
found a clever thing they're doing. They have a virtualized filesystem, where multiple bundle files are mounted, and files are located in a reverse-added order. But they subclassed the bundle method so that instead of a filename, you can set up a bundle backed by a pointer+length.
why are they doing this?
because one of the bundles is statically compiled into the executable itself. They just do BundleManager::mountBundle(INTERNAL_BUNDLE_STRING,INTERNAL_BUNDLE_LENGTH);

wait why is there a method on the App class to parse commandline tokens.

this is a Wii game.
what command line?

AppWii::tickleDVD?

DO NOT TICKLE THE DVD

OH LOOK another case of magical sentinel pointers.
they just checked to see if a pointer was 0xBADBEEF.

LEARN TO USE NULLS

and I'm not even going to touch the virtual machine. it appears to be stack based, and it's very complicated.
also I did some more spelunking, and the getBinaryData method is literally only ever used to load text files.

I found where the engine lists all the file types, and they're specified with SUSPICIOUSLY win32 filter strings.

I wonder if they copy-pasted this out of some tool they used to build their files?

I kinda wonder if they generated this code out of some non-C++ language with a poor optimizer.

Everything has a vtable. Even classes where there's no subclasses, and there's only ever one object, because it's a big magical global singleton.

YOU DON'T NEED VIRTUAL FUNCTIONS WHEN THERE'S ONLY ONE OBJECT THAT NEVER CHANGES
also while I'm at it, when you call a function called "resolveName" that returns a pointer, you shouldn't have to check the result against both NULL and -1. Something went wrong if you think -1 is a valid pointer.

20 ::mbinary_search functions and no ::nonbinary_search. sad.

(hard mode: there's no templates here. they seriously wrote 20 variants on their binary-search algorithm)

okay yeah they DEFINITELY built this game on top of a cross-platform engine (their own, I believe).
I just found a function for getting the state of the mouse-wheel.

On a wii.

NOW LOOK, TECHNICALL YOU CAN PLUG A MOUSE INTO A WII, YES.

BUT WHY WOULD A GAME BOTHER TO SUPPORT IT?

The same developer did make a game for the PC the year before, but it's GONE.
Like, the only hits are wikipedia and the linked-in page for the project manager.
But the internet archive helps! Apparently this wasn't even exactly a game, it was some kind of training tool? interesting.
yeah I think wikipedia has the wrong title for this game. It's GeoStorm, not Geo-Storm.
Not that it helps.
I'm guessing this game didn't come out. All the info is from that one page and resumes of people who worked on it.
mind you if it was an internal training tool, why would it need to come out?
anyway I just did some checking. So the wii version has bundles that start with
"Pipeworks bundle v1.20 (big endian)".
Guess what the xbox 360 version says?
"Pipeworks bundle v1.30 (big endian)"!
And it's using the same script/VM system.
The Wii version is v3.01, xbox360 is v3.81.
oh hey, it turns out one of their next windows games (from 6 years after the wii game, and 4 after the xbox 360 game) is free to play on steam. Time to download 5gb of game I have no interest in playing just to look at some data files!

Nope. It's unreal engine.

Also, I accidentally launched it, and it has a broken EULA. You can't decline it, it just tells you to accept before you can continue. I'm not trying to continue, I'm trying to QUIT

grabbed another 2009 game for the wii: "Pipeworks bundle v1.03 (big endian)".

JUST HOW DEEP DOES THIS RABBITHOLE GO? AM I GOING TO HAVE TO BECOME THE OFFICIAL EXPERT ON THE PIPEWORKS ENGINE?

@foone that movie sucked
@cinebox completely unrelated! predates the movie by 8 years.
@foone I plugged an EA Wii microphone into my PC. It's just a logitech
@foone Metroid Prime would have been a lot more fun with a mouse and keyboard.
@foone or they did most of the gameplay on a PC and then finalized on console.
That's not uncommon.
@foone the interesting bit is going to be how they differ
@foone maybe it's a sentinel value for, IDK, "it's a secret" or "ask me again later".
@foone it's the last address in the ram stick that was manufactured directly before yours
@foone In this household we don't use NULL for invalid values, we use MAX_VALUE !
@foone 0 is too common, and valid, to be a signal value.
@Sdowney This is a wii game! there's no RAM at 0x0. So NULL is never valid.
@foone Not dereferencable, but a valid bit pattern for a pointer. End of linked list vs if you see this, there was a bug.
It's not great. Some architectures have trap values, but that's expensive.
@foone as an embedded software engineer: no, i refuse. i will instead reserve an entire page of memory for 4096 sentinel values.

@foone I've used this to find indirection through uninitialized pointers, in debug. Initialize your heap to 0xdeadbeef and test where you were getting the indirection through 0. One NULL value is just not enough.

Also, never let this test go to production. please.

@foone Or at least 0xDEADBEEF .Our traditions mean nothing.
@foone copy protection check?
@foone DO NOT TAUNT HAPPY FUN BALL
@foone maybe touchDVD would have been too much? I’m not sure it’s actually worse 🤔