"OKAY SKIFREE This is a game originally from 1991, developed by Chris Pirih, and included on one of the Windows Entertainment Packs. There's a modern 32bit version by the original developer, on the official site
OKAY SKIFREE
This is a game originally from 1991, developed by Chris Pirih, and included on one of the Windows Entertainment Packs.
There's a modern 32bit version by the original developer, on the official site:
ski.ihoc.net/
Jun 12, 2022 · 6:31 PM UTC · Twitter Web App
it's windows only and it's not got the source available.
which is a shame. crossplatform skifree, skifree mods/improvements would be cool, right?
I've also got the original 16bit EXE and some intermediate versions. but the 32bit version is much easier to analyze, so I'm gonna focus on that one.
It's all in one big ol' EXE. and by "big" I mean it's 116 kilobytes.
So there's no image files or anything.
So let's run wrestool on the EXE and see what we've got.
So, a ton of bitmaps...
So, icons. We've got this icon, which is available in three variants. 2 color, 16 colors, and 16 colors again. I think it's just 16 colors (but windows standard) and 16 colors (but custom).
This is the original icon, designed by Chris Pirih.
Then there's the other icon, which was designed by someone at Microsoft. Apparently they didn't like the original icon, and had someone design a fancier icon before it got into the entertainment pack.
same as before: 2 color, 16 color, 16 color but fancy.
then there's 8 files that weren't able to be decoded by wrestool. I think they're supposed to be icons? but they don't work. I'll leave them alone for now.
Then we've got 89 bitmaps which make up the rest of the game's graphics.
and despite this being the 32bit version, you can tell that these were designed for 16-color displays. They're manually dithered.
OKAY SO now it's time to look for code.
We've got an entry point (which'd be WinMain) and then 9 groupings of functions, based on their addresses. 401 through 409.
401 has 19 functions
402 has 25 functions
403 has 11 functions
404 has 16 functions
405 has 15 functions
406 has 23 functions
407 has 22 functions
408 has 11 functions
409 has 5 functions
so we have a total of 147 functions in the EXE.
that's a bunch.
but it's doable to reverse engineer these one by one, right?
and reimplement them? giving us a source-available clone of SkiFree?
at first that'd just give us a win32 version of skifree, which... we have.
but then the use of the win32 api could be replaced with SDL or similar, creating an identical but cross-platform skifree.
so we can figure out a lot of things by doing basic puzzle-solving logic.
like let's look at FUN_00405a40.
it sets a bunch of global variables, then calls a function, and if the result is 0, it calls another function with a string, which is basically "can't load bitmaps"
so clearly FUN_00405ab0 has something to do with loading bitmaps, and FUN_00404950 displays messages.
and if we check FUN_00404950, it calls some other function to get a caption, then calls MessageBoxA, a win32 API to display a message box, if you couldn't guess.
SO let's rename this. FUN_00404950 is DisplayMessageBox.
FUN_00401cf0 is a little more complicated.
But if we look into this, it seems that DAT_0040c61c is a global variable which is an array of pointers to strings, indexed by param_1.
if the entry is filled (non-NULL), it simply returns that string.
if it's not filled, it uses LoadStringA to go get it, then allocates some memory for it, puts it in the slot, and then returns it.
so this is just a cache around LoadStringA
LoadStringA pulls strings out of the specified EXE/module (it could be a DLL or something).
I'm gonna guess that it's actually pulling them out of this EXE, since there's no DLLs or anything else to load from.
The module instance it's using is DAT_0040c61c.
Let's see where that's set.
So it's read from 4 places and written from one. Time to check out where it's written.
so it's set to param1 of FUN_004052d0, which is a big call that does a bunch of things. Who calls this?
It's called in only one place, by FUN_004047e0, which calls it with... its own param1.
UGH. Let's see how calls this!
It's called in one place, by the entry function, which passes pHVar5, which is the result of GetModuleHandleA(NULL)
That returns the current module instance.
so, rewind the investigation stack.
DAT_0040c61c, the data we were looking at, it's a global variable pointing to the HMODULE handle to the current EXE.
So now we can rewind back to that string-caching function, and name (and type) the DAT_0040c61c data. It's now SkifreeEXE, and we can see it passed to LoadStringA.