I recently rediscovered an obscure 1997 Simon & Schuster / Marshall Media edutainment game for Windows 95 that I played as a kid: Math Invaders. In this part, we’ll investigate whether we can enter an “editor mode”, hinted at within the strings contained within the program. There’s even a ✨surprise ending✨ that I didn’t see coming!


Here’s where we left off, investigating the disassembly of a function that references a mysterious string: *** EDITOR MODE ***. Cleaning the disassembly up and commenting to be a bit to be more readable gives us:

// Because `param_1` from the disassembly (aka `this`) is passed to
// `CWnd::SetWindowTextA` at the end, this function probably belongs
// to a class inheriting `CWnd`.
class CGameWnd : public CWnd;

void CGameWnd::_updateWindowTitle() {
	// Don't do anything if we're in fullscreen mode.
	if (gFullscreen) return;
	char buffer[256];
	if (this->unknown_334 != 0) {
		// If the variable at offset 0x334 is not 0/FALSE/NULL,
		// we have a file loaded.
		if (this->unknown_3714 == 0) {
			// If the variable at offset 0x3714 is 0/FALSE/NULL,
			// we're in "editor mode"... whatever that is!
			// Append text indicating this to the buffer.
			strcat(buffer, " - *** EDITOR MODE ***");
		}
	}
	// Set the window title to the contents of the buffer.
	this->SetWindowTextA(buffer);
}

Ok! So to activate editor mode, we need to 1) not be fullscreen, 2) have CGameWnd->unknown_334 be non-zero, and 3) have CGameWnd->unknown_3714 be zero. Enabling fullscreen (via the 3d.ini file described in part II) no longer seems to crash my game (that must have been a mistake of mine!). The game starts and plays in fullscreen, and the title even updates between “Paused, Press ‘p’ to resume.” and “Running…” when we press P!

But try as I might, no amount of reverse engineering is allowing me to toggle the unknown_3714 variable. No code even exists (that I can find) to change it, except during initialization or loading of levels, when it’s always set to TRUE. So I have a theory: there was an editor mode, but its functionality has been “removed” behind something like #ifdef EDITOR.

Well then! Without a deus ex machina, it looks like we’ll never break into the “editor mode”. I reached out to the community to see if anyone knew more about the game itself, or had heard of a source code leak for this nearly 30 year old game. A few people were even so kind as to search Usenet for me. But nothing was turning up. A few months passed and I’d pretty much given up on ever finishing this part III post.

Vindication!

You could try downloading this SSK@YoUTHinkYOUrecLeVeRdONtYou?/SSPYTH.zip
Its password seems to be the file name.

Months after I’d given up I received an encrypted message, sent to the SimpleX address listed in my website footer. It didn’t contain much more than a Freenet hash. I hurriedly installed a client and accessed the hash — the file downloaded — the password worked — and inside? Beautiful, wonderful source code, with “last modified” dates ranging from December 1995 to January 1997! And sure enough, there are several instances of #ifdef EDITOR that block the “Editor Mode” from being used, as I suspected! In fact, editor mode is implemented as a completely separate static library, that is only linked into the executable when editor mode is to be used. No wonder none of the relevant code can be found when reverse engineering the released binary!

Now that we have source code and can fully analyze the game in the ground truth, let’s poke around and see what we can find. The source code is laid out as follows:

  • 📁 3DLIB - The core 3D engine.
    • 📄 Various Assembly, and C++ source and header files.
  • 📁 EditLib - Editor Mode functionality.
    • 📄 Various C++ source and header files.
  • 📁 RES - The game icon in BMP and ICO formats.
  • 🔨 Various Microsoft Developer Studio (Visual C++) files.
  • 📄 Various C++ source and header files.
  • 📄 Some example save files.
  • ⚙️ SSPYTH.EXE - A compiled binary.
  • 🔧 3D.INI - An example configuration file.

Cheat Codes

The game has a few cheat codes! They can be activated by typing the code in during play, as it keeps a buffer of the last 20 key presses. All cheat codes begin with K and are committed with L:

                Cheat Code Message Description
KEY1-4L Add Key Gives the specified key (1-4).
KWN0-9L Add Weapon Gives the specified weapon (0-9) with max ammo.
KVURL Add Strength, Shield Sets max strength, max shield, and dons the spacesuit.
KHJ1-6L None Gives the specified item (1-6), however items 2, 3, and 6 are not allowed to be given via this cheat.
The items 1-6 are: “health pack”, “light divider”, “time warper”, “drainer field”, “ultra drainer field,” and “reflection”.
KYHRL Add Everything Adds all keys, weapons, and allowed items.
KNNL Problem Debug Mode ON
Problem Debug Mode OFF
Toggles a mode in which math problems’ expected answers are printed.
K01-27L None Go to level 01-27.

Building a 27 Year Old Game

Of course, the pièces de résistance of having access to the source code: editor mode! Let’s see what it takes to get it working. First, we’ll need to get the source code building. Thanks to the lovely project DOSBox-X, emulating older Windows operating systems is as simple as following a guide. I also sourced the following disk images: Windows 95 OSR21, Visual C++ 4.2, the DirectX 1.0 SDK, and the ActiveMovie SDK2 (this disk contains many other interesting installers as well). If you’re planning on building up such a virtual machine yourself, you’ll have to find your own product keys, sorry.

Breezing through the guide leaves us with a fully functional Windows 95 install, with Visual C++ 4.2 (which includes the Microsoft Developer Studio IDE - which we’ll need), the DirectX SDK, and the ActiveMovie SDK. Our source code contains an .mdp file, which is a Developer Studio project, so let’s open it and build the default project with Ctrl+B!

--------------------Configuration: 3dlib - Win32 Debug--------------------
Compiling...
decomp.cpp
fatal error C1083: Cannot open source file: 'C:\Sspyth\3dlib\decomp.cpp': No such file or directory

Error executing cl.exe.
Sspyth.exe - 1 error(s), 4 warning(s)

Alright, some errors, but nothing we can’t solve. The first thing we notice is we’re missing a file 3DLIB\DECOMP.CPP. Poking around, we find there’s a file named 3DLIB\aviDECOMP.CPP. A simple file rename gets us past this error. Re-running the build gives us another error, now in the linking process. It can’t seem to find the 3DLib and ActiveMovie libraries:

e_frame.cpp
.\.\ztest.hpp(9) : fatal error C1083: Cannot open include file: 'strmif.h': No such file or directory

Game not installed

That’s as easy as adding the full path to the ActiveMovie SDK’s include directory to the compiler path, and adding the lib\StrmBase.lib file and the 3DLib output file to the linker properties. Our project now builds, and we can verify that the game runs! Well, it tells us Game not installed, run the setup program., but commenting out a few lines in CSspythApp::InitInstance() fixes that:

// Check for game installed
// char buffer[260];
// strcpy(buffer, "");
// GetRegString("Version", buffer, 20);
// if (strcmp(SSP_VERSION, buffer)) {
// 	AfxMessageBox("Game not installed, run the setup program.", MB_OK | MB_ICONSTOP);
// 	return FALSE;
// }

Installing the game and pointing the pakpath setting in 3D.INI to the install directory allows the game to load assets and run!

Game running

Editor Mode

So what do we need to enable editor mode? Let’s create a new build configuration just for this use case. We already know we have to add /D EDITOR to the compiler settings, and doing so builds… and fails. Why now?

sspyth.obj : error LNK2001: unresolved external symbol "public: void __thiscall CEditFrame::EditDoor(void *)"(?EditDoor@CEditFrame@@QAEXPAX@Z)
sspyth__/Sspyth.exe : fatal error LNK1120: 7 unresolved externals
Error executing link.exe.
Sspyth.exe - 8 error(s), 0 warning(s)

Ah, EditLib! Let’s add that to our linker options as well and try again. This time the build succeeds and we can run the game as before. Now, how do we activate it? We know so far that: 1) the game has to be in windowed mode, so we set fullscreen=0 in 3D.INI; and 2) there are some mystery values in the main window class that must be set just-so to be “in editor mode”. Thankfully now we can look at actual code! It turns out our CGameWnd::_updateWindowTitle() decompilation above is actually named CMainFrame::ShowPauseState():

void CMainFrame::ShowPauseState(void)
{
	if (g_FullScreen)
		return;

	char buffer[256];
	if (m_game.m_pscene != NULL) {
		char drive[5],directory[200], name[30], extension[5];
		_splitpath(FileName, drive, directory, name, extension);
		sprintf(buffer, "  S.S. Pythagoras  -  '%s'  ", name);
		if (!m_game.m_GameMode)
			strcat(buffer, " - *** EDITOR MODE ***");
		if (m_game.Paused())
			strcat(buffer, " - Paused,  press 'p' to resume.");
		else
			strcat(buffer, " - Running...");
	} else {
		sprintf(buffer, "  S.S. Pythagoras  -  NO ACTIVE LEVEL");
	}

	SetWindowText(buffer);
}
void CGame::Update(CKeyboard& keys) {
	if (keys.KeyDownWasUp('G'))	{
		m_GameMode = !m_GameMode;
		GetMainFrame()->ShowPauseState();
	}
}

Awesome, my guesses were really close. unknown_334 is m_game.m_pscene, and unknown_3714 is m_game.m_GameMode. Let’s see if I’m right, and m_GameMode is changed with a #ifdef EDITOR-surrounded key input. m_GameMode is only changed in two places in the code, both in GAME.CPP. The first is during initialization, where it is set to TRUE. The second place is further down the file, in CGame::Update(CKeyboard&):

Ok, this isn’t surrounded by #ifdef EDITOR… I suspect again that the “final” version of the game saw a few code changes that weren’t included in the copy of the code I have. But a little digging shows that m_GameMode alone has no real effect, because just a little further down is the code to actually perform editor mode:

////////////////////////////////////////// EDITOR
#ifdef EDITOR
if (keys.KeyDownWasUp('I')) {
	GetApp()->ShowEditFrame();
	GetApp()->m_editframe->OnInsertButton();
}
#endif

So, we need to press G to switch the game mode, and then I (I suspect for Insert or Inspect) will show the editor controls! Once editor mode is active you can also press E to Edit door and entity instances, or T to edit the focused object’s Type. Let’s give it a go:

Editor Mode Click to zoom.
Editor Mode

Next Steps

If there’s ever going to be a follow-up to this three-part post, there’s a few things I’d like to try:

  • Add modern WASD keyboard controls — the current control scheme is A/Z for forward/back, and Shift/X for left/right.
  • Get the game running on modern versions of Windows — I’d like to do this by getting it running “well” on each newer OS starting with Windows XP and moving forward.
  • Properly handle texture files when extracting — PAKrat (from part I) doesn’t extract the PCX textures correctly.
  1. The 3rd release of Windows 95, “OEM Release 2”, added support for FAT32 drives. 

  2. Alternative source in disk 4 of Storm #1 - Internet Archive