Skip to main content
Evert's website

Reverse Engineering Pandora’s Box (1)

One game I have quite fond memories of is Microsofts Pandora’s Box (1999), a puzzle game for PC created by the designer of Tetris. The game consists of a total of 361 puzzles of 10 different types, spread over 7 stages with increasing difficulty. Of course, being designed for Windows 95 and 98, the game shows its age: It is hardcoded to use a full-screen resolution of 640×480, writes its save files to the ‘Program Files’ directory, and shows a black screen when the built-in videos are playing, leaving you with audio only.

Wouldn’t it be cool to bring the game to 2023, getting it to run flawlessly under modern Windows (and possibly other operating systems), and making it possible to add more puzzles, while gathering some reverse engineering experience on the way?

The first goal I set is being able to modify the first Focus Point puzzle, specifically, changing the image used by that puzzle.

Setting up the reverse engineering environment #

For my reverse engineering efforts I installed Windows 10 (64-bit, as I already knew the games runs fine under x64 Windows) on a virtual machine. Other tools I will be using in this post are:

Installing the game #

Let’s get started by installing the game. I used the CD that I still have from back in the day, but the ISO is floating around the web on various abandonware websites if you want to follow along. You also want to grab the 1.0a patch (link to archive.org, as the original has long been taken down), as the game will not run on Pentium III and up without it.

Pandora's Box installer
Installing the game on a VM

I chose to do the ‘Full’ installation, so all game data is copied to the C: drive. Afterwards, install the 1.0a patch linked earlier.

By changing a single registry key, we can trick the game so that the CD is not required for launching, which is particularly useful when you used a physical disk to install the game. Launch the registry editor and navigate to:

# 32-bit Windows:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft Games\Pandora's Box\1.0
# 64-bit Windows:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Microsoft Games\Pandora's Box\1.0

Change the value of the key ‘CDROMDIR’ to the path where the game was installed, e.g. ‘C:\Program Files (x86)\Microsoft Games\Pandora’s Box’. The game may now be launched without the CD in the (virtual) drive.

First steps #

Let’s start by taking a look at the game files we installed:

Pandora's Box files
Installed files

If you have played the game, you can map most directories to the various puzzle types by looking at the directory name:

Directory Puzzle type
3DSKIN Outer Layer
BGL Lens Bender
FNF Find and Fill
FPN Focus Point
IMG Image Hole
JES Jesse’s Strips
OVER Overlap
RTT Rotascope
SLICE Slices
TRIGRAMS Interlock

All the above directories initially contain only sound effects in the WAV file format. After playing a few puzzles, ‘.rim’ files started appearing in some of these directories. Next, we have another set of directories containing mostly WAV files:

Directory Contents
MUSIC Music played on the map and puzzle screens
pandora.htm
pandora.mgd
SCREENS Menu sound effects
VO Voiceovers

The ‘pandora.htm’ and ‘pandora.mgd’ files in the ‘MUSIC’ directory seem to describe some sort of map or graph. Opening the HTM file in Firefox results in the message ‘(Browser does not support controls.)’. Out of curiosity I tried opening the HTM file under Windows 98 SE/Internet Explorer 5, but it showed the same message.

Finally, we have the ‘HELP’ directory which contains two files: ‘message.m20’ and ‘pandora.m20’. Inspecting them using the ‘string’ command in WSL reveals that they contain text sequences used in the game, like the instructions shown during tutorials.

Jumping back to the root directory, we have the following files:

File Function
besttime.dat Presumably contains high scores per puzzle
bkant.ttf Book Antiqua font, used in the game
clcd16.dll SafeDisc (copy protection) library
clcd32.dll SafeDisc (copy protection) library
credits.dat Contains the list of people who developed the game
dplayerx.dll SafeDisc (copy protection) library
drvmgt.dll ?
EBUEula.dll ?
EBUSetup.sem ? (Empty file)
EULA.RTF End User License Agreement
int01.avi to
int07.avi
Trickster capture videos, played when a chapter is completed
langenu.dll ?
loadbar.png Shown next to the splash screen when starting the game
nameenu.dll ?
open.avi Opening movie, played when first starting the game
pandora.eul Exact copy of pandora.exe. Interestingly, on the CD this file matches the 1.0a executable, while the 1.0a patch is not even included on my disc!
pandora.exe Main game executable, version 1.0a
pandora.rof Game data (226 MB)
puzenu.dll ?
puzzle.dat Game data (70 kB)
r.bly Save data, only created after starting the game. Note: if the game is not launched as administrator, these files will instead be written to ‘%LOCALAPPDATA%/VirtualStore/Program Files (x86)/Microsoft Games/Pandora’s Box/’.
r.ply Save data, see above
Readme.rtf Game manual
screens.rof Another data file (68 MB)
SETUPENU.DLL ?
splash.png Game splash screen
statenu.dll ?
tricks.bmp ? (Not a bitmap image despite the file extension)
tricks.rtr ?
UNINSTAL.EXE Uninstaller

I determined the above information by inspecting the files with the ‘file’ and ‘strings’ commands under WSL. and by looking at their filenames.

The main files of interest are of course the large .rof files, as we haven’t seen any of the in-game asssets yet. Presumably these assets are combined into the .rof files.

Let’s take a look at the ‘pandora.rof’ file through ‘strings’:

strings pandora.rof | less

In the output we can immediately spot a few level and puzzle names, as well as a lot of PNG files:

3DSKIN
LEVEL.0
LEVEL.01.MAUI
LEVEL.02.PUCK
LEVEL.03.ERIS
LEVEL.04.COYOTE
LEVEL.05.MONKEY
LEVEL.06.ANANSI
LEVEL.07.RAVEN
LEVEL.08.SCALAWAG
OVER
SLICE
TRIGRAMS
302.PNG
BRACKET0.PNG
BRACKET1.PNG
BRACKET2.PNG
BRACKET3.PNG
DNARROW_D.PNG
DNARROW_DH.PNG
DNARROW_U.PNG
...

Let’s take a look through a hex editor:

Hex dump of the directory listing
Hex dump - directory listing

At 0x184 – 0x23A we immediately spot something that looks like a directory listing. A bit further in the file, at 0x436, we spot another listing, this time of PNG files:

Hex dump of the first file listing
Hex dump - file listing

Let’s write down the following information for now:

Let’s look at the start of the file next. As the game is designed to run on 32-bit Windows systems, we can assume all integers are 32 bits/8 bytes long, and the byte order is little endian.

The first integer (13 00 00 00 / 0x13 hexadecimal / 19 decimal) seems to denote the length of the directory index. We validate our hypothesis against the ‘screens.rof’ file: it starts with the sequence ’01 00 00 00′, and indeed contains only one directory named ‘SCREENS’.

Immediately next, at 0x4, we find the value 0xB6. This matches the length of our directory listing.

The next five integers seem to describe the first entry from the listing:

Again, we can check these hypotheses against either the ‘screens.rof’ file, or more conveniently, the next entry in the structure:

We can conclude that the metadata entries thus consist of 5 integers, and as such are 20 bytes long each.

Metadata structure
Metadata structure

We can conclude that the metadata entries thus consist of 5 integers, and as such are 20 bytes long each.

Now that we can parse directory listings, let’s look at the actual files embedded in the .rof file. In the second listing we see that there are PNG files present in. Wikipedia tells us all PNG files start with the sequence ’89 50 4E 47 0D 0A 1A 0A’. Let’s search the file for this sequence:

Hex dump PNG signature
Hex dump – PNG signature

We get a first match at offset 0x526F2. It seems the contents .rof files are not compressed or encrypted in any way. Furthermore, notice the file starts immediately after the listing, so we know there is no metadata behind the listings.

Because the second file in the listing is also a PNG file, we can search for the signature again. The second signature occurs at 0x82C28. Assuming there is no metadata between the files, we can dump the region 0x526F2…0x82C27 and the result should be a valid PNG image:

Extracted asset
Extracted asset

And indeed, the range contains an asset from the game. We can also use the offsets (0x526F2 and 0x82C27) and size (subtracting the offset leaves 0x30536) to look for the metadata describing it:

File metadata
Hex dump – file metadata

The offset is found in between the first and second listing; the size is found immediately thereafter. We can also spot the offset of the second file (0x82C28) at 0x256..0x259, so we know the file entries too are 20 bytes in size.

The two integers between the directory listing and the offset of the first file contain values 0x19 and 0x14A, which again correspond to the number of files and the size of the name table.

Using all the above information, we can write a simple parser in Python that can print the directories and the files in them:

Resulting in the following output:

3DSKIN
  302.PNG
  BRACKET0.PNG
  BRACKET1.PNG
  BRACKET2.PNG
  BRACKET3.PNG
  DNARROW_D.PNG
  DNARROW_DH.PNG
  DNARROW_U.PNG
  DNARROW_UH.PNG
  REVLF_D.PNG
  REVLF_DH.PNG
  REVLF_U.PNG
  REVLF_UH.PNG
  REVRT_D.PNG
  REVRT_DH.PNG
  REVRT_U.PNG
  REVRT_UH.PNG
  ROTATE_D.PNG
  ROTATE_DH.PNG
  ROTATE_U.PNG
  ROTATE_UH.PNG
  UPARROW_D.PNG
  UPARROW_DH.PNG
  UPARROW_U.PNG
  UPARROW_UH.PNG
...
LEVEL.0
  PUZZLE.1.IMH
  PUZZLE.10.BGL
  PUZZLE.11.TAN
  PUZZLE.12.FNF
  PUZZLE.2.ZOO
  PUZZLE.3.RTT
  PUZZLE.4.FSH
  PUZZLE.5.SKI
  PUZZLE.6.CHIS
  PUZZLE.7.JESS
  PUZZLE.8.RUB
  PUZZLE.9.SLICE
LEVEL.01.MAUI
  CITY.06.CAIRO
  CITY.10.HONOLULU
  CITY.16.MADRID
  CITY.21.NEW YORK
  CITY.22.PARIS
  PUZZLE.1.OVL
...

The files in the puzzle directories (3DSKIN, FNF, FPN etc.) seem to be UI elements only. Looking at the level data, it is a reasonable assumption that the ‘files’ below them are actually be subdirectories.

LEVEL.0 probably contains the tutorial puzzles as there is one puzzle of each type, and LEVEL.01 to LEVEL.07 contain a directory per city (which presumably contain the puzzles for that city) and one to three standalone puzzles for the ‘boss fight’. Interestingly, there is a directory named ‘LEVEL.08.SCALAWAG’, which possibly contains the 40 additional puzzles from the ‘Puzzle Game of the Year Edition‘ re-release. As far as I know however my version of the game is not the Puzzle Game of the Year Edition (it is the European release by Xplosiv), so this raises the question whether the extra puzzles have always been included.

It seems like both directories and files are described by the same 20 bytes of metadata. Let’s look for a directory/file flag. Recall that there is one field that we do not know the meaning of yet. Let’s log the size and the unknown field for every entry:

...
LEVEL.0 / 0 / 1
  PUZZLE.1.IMH / 0 / 1
  PUZZLE.10.BGL / 0 / 1
  PUZZLE.11.TAN / 0 / 1
  PUZZLE.12.FNF / 0 / 1
  PUZZLE.2.ZOO / 0 / 1
  PUZZLE.3.RTT / 0 / 1
  PUZZLE.4.FSH / 0 / 1
  PUZZLE.5.SKI / 0 / 1
  PUZZLE.6.CHIS / 0 / 1
  PUZZLE.7.JESS / 0 / 1
  PUZZLE.8.RUB / 0 / 1
  PUZZLE.9.SLICE / 0 / 1
LEVEL.01.MAUI / 0 / 1
  CITY.06.CAIRO / 0 / 1
  CITY.10.HONOLULU / 0 / 1
  CITY.16.MADRID / 0 / 1
  CITY.21.NEW YORK / 0 / 1
  CITY.22.PARIS / 0 / 1
  PUZZLE.1.OVL / 0 / 1
...

Clearly, the unknown field denotes the type of the entry: 0 means file and 1 means directory. Furthermore, all directories have a size of 0, so we can ignore this field when processing a directory.

The final directory metadata structure thus looks as follows:

Final metadata structure
Final metadata structure

Let’s rework our script so it can parse an arbitrary mix of directories and files:

And indeed there are a lot of nested directories below the ‘LEVEL’ folders:

...
LEVEL.01.MAUI:
  CITY.06.CAIRO:
    PUZZLE.01.FPN:
        PUZZLE.ZOO
    PUZZLE.02.SLC:
        1OBJ_1.PNG
        1OBJ_1A.PNG
        1OBJ_2.PNG
        1OBJ_2A.PNG
...

In the next post, we will take a look at the following tasks: