2 hours of fun completing the game. 20+ hours of fun hacking the game.

James Read
Level Up Coding
Published in
7 min readApr 27, 2022

--

A bit of curiosity can lead to a lot of learning.

Screenshot from the Facility 47 game

This is a blog post about how I’ve enjoyed a little game called Facility 47. It was initially fun playing it, but it was more fun hacking it.

I invested the time in hacking the game because I really enjoy challenges like this, and I’m writing a blog article about it to encourage you to take up ambitious projects like this — because you don’t know where they might lead.

What is this game?

Facility 47 is a simple “point and click” adventure style game, released onto Steam in 2014. It was about $5 when I bought it, probably in a sale. If you’re trying to compare it to a game you’ve played, the biggest well-known game in like it is probably Myst.

It’s also available for Android, and other platforms too.

Why did you want to hack it?

I played through the game normally the first time, it took me perhaps 2 hours in total. I just enjoyed the aesthetic, the mood, and the style of the game. Its puzzles are well-designed. However, when you know the solution to any puzzle games, you can complete the game significantly quicker. On the second attempt, I think I managed to complete the whole game in 25 minutes —as I had the solutions in recent memory.

As a fan of game speedrunning, I was curious how quickly an experienced player, who know the solutions to all the puzzles, could complete this game. After a little bit of searching for game speedrun videos, I found zero results. Nothing on speedrun.com. Woah, nobody has done a speedrun of this game before? Hah, this is an opportuity then! If I speed run this game, I’m going to become a world record holder!

So, after a bit of searching into how these things are done, I created a page on speedrun.com — https://www.speedrun.com/facility_47

I uploaded my first attempt, and then shortly after, a second attempt;

If you watch the speedrun video, you’ll see a small timer in the bottom left-hand corner. That is a speedrun timer called LiveSplit. It measures timings and splits. Now, the important point is that during this video, I was controlling LiveSplit manually using the numpad. Numpad 1 to go to the next split, and once completed, it records your final time.

Surely this cannot be how “professional” speedruns are controlled? It isn’t.

LiveSplit supports a concept called AutoSplitters — a script that runs in the background and reads directly from the game’s memory, as the game is running. The script that is written, on a per-game basis, tells LiveSplit exactly which pointers, and areas of memory to watch. Writing these scripts is simple. Finding the areas of memory to watch is what takes time. I had a challenge in front of me, here I go down the rabbit hole…

How does LiveSplit know where to look in memory?

So that you can understand what we’re aiming for — let’s skip to the end quickly. This is the complex part of the auto-splitter code;

state("Facility47") 
{
// currScene:sz:COutsideCell
string50 scene : "Facility47.exe", 0x0051887c, 0x8, 0xc, 0x4, 0x38, 0x0, 0x8, 0xA
}

The first address —0x0051887c, is a static address in memory, that always has the same structure, game every time the game loads. The following values, 0x8, 0xc are pointer offsets, into that structure, that point to the value we really want to read.

The next section of the blog explains how I went about finding those values (that took in excess of 20 hours)…

Starting at the beginning, what is Facility47.exe?

Technically, it’s a Win32 executable (although there are ports to Android and other platforms). However, after just a brief bit of poking around in the game’s directories, I found that that most significantly, it’s Flash. Yeahhh.

Decompiling Flash

Okey-dokey, well what I know of Flash is that it’s pretty straightforward from a scripting perspective. It uses ActionScript (much like JavaScript), with a bunch of media assets — jpegs, audio, etc. It’s also readily decompilable with freeware and shareware. It’s easy to find a bulky 35mb SWF file to get started with — assets.swf.

Time to grab a SWF (Flash) decompiler. A few moments later, I was looking at a directory dump of .as (ActionScript) files. I’m not going to provide any examples here, because this is a closed source, commercial game and I want to respect the publisher’s rights not to have their work exposed, while still doing reverse engineering on my own computer.

However, looking through these files, there are a lot, and at glance they seem pretty homogenous. So, I ran them through a bit of basic frequently analysis and started to get a feel for what is really inside them. I filtered out many common keywords, like public, function, Movie, etc. and I found stuff like CIceCave_scene, CIceLakePool_scene, CIceTunnel_scene, etc. Nice, those are level names! I’ve got a starting point to creating an autosplitter script.

Finding levels in memory

Now, there is no way I could take the decompiled output, and recompile it. That just isn’t the right approach either. Instead, I need to attach a debugger to the game. An extremely popular debugger for games, is the fantastic CheatEngine. I load it up, attach to the running game, and start to search for a level name that probably matches the level I’m on.

Okay, CCell is the starting level, and there are 80 addresses in memory that contain that string. I’m going to need more coffee if I’m going to search through all of those.

Even if this game is pretty tiny, the amount of raw bytes to search through is just incredible. Narrowing the search space is essential. A good way of doing this is clicking to another “level”, and then back again, and see what values changed.

Thankfully, many values changed, meaning they were probably just a temporary store of the current level, and not the real pointer in memory. Clicking around a bit more, and narrowing down values that are static, gets me down to 2–4 addresses that might be relevant. Awesome — 2–4 addresses is much better than the 80 we started with.

Finding the right structure

After reducing the search space down, and looking at the few values there are left, I found a structure in memory that looks very promising;

At this point, I’m 3 hours or so into this project. Perhaps less — I’m not sure, that was many nights ago. I thought this would be easy, if it wasn’t for one simple issue…

The problem is that pointers move around

I set up a watch on the location in memory that stored the current level, and I clicked around the first 10 levels or so. It was kept up to date, and it was in sync with my script. Awesome.

I clicked on about the 11th level, and boom, everything now looks like rubbish. Huh. I start my search again — and I find the structure in memory easily, this time searching for currScene (the beginning of the structure). It was in an entirely different area of memory.

This could be almost anything causing this, maybe one of the structures it depends on was deleted and re-written, who knows. Annoyingly, it moves around as I played the game.

The next 17 hours or so were spent looking for fixed addresses in memory, and then building a list of reliable pointer offsets that is the same, every time the game is played.

The result was an Open Source autosplitter script, available on GitHub here; https://github.com/jamesread/facility-47-speedrun/blob/main/facility47.asl . This was also accepted as a contribution into the LiveSplit project, meaning that others can benefit from this script easily :-)

Summary

I’ve written about projects like this before, which I call Odyssey projects — a bit of curiosity led to a lot of learning. I learned a lot more about memory, I refreshed some skills on reverse engineering, and I got an Open-Source contribution accepted in the process. Likewise, I didn’t expect any of this when I first picked up the game and started playing.

Not only that, but I would encourage you to have a bit of curiosity too, and consider reading up a little more on what an Odyssey project is.

--

--

Public Cloud and Open Source advocate. Red Hat Solution Architect during the day. Enthusiastic developer at night :) http://jread.com