Past articles

Owning classic PC games in DOSBox -

If you’re old enough to have seen the PC gaming scene in early nineties, you might remember all the hexediting cheats in game magazines, trainers integrated into pirate copies and all kinds of tools available for download in the local BBSs.

Nowadays when the retrogaming bug bites it’s easy enough to grab a copy from abandonware site and load it up in DOSBox, but after a few minutes it usually becomes apparent your old favorites ask for way more patience than you’re ready to give in comparison to modern titles. And you might find it’d be nice to enable a cheat just to browse around..

That’s what I’ve been thinking about anyway, so first, there are a few ways to do memory scanning, for one you might take a MEMDUMPBIN from DOSBox debugger, scan for a value in hexeditor then take a second dump and crossreference changes, but you can see how this just won’t do.

Second, there are a few memory scanners also for Linux; scanmem (with a GUI called gameconqueror) seems to works just as well as the Windows ones and would normally be the way to go. Except, they depend on the specific version of the emulator because the base address of the emulated memory changes. And especially if you’re compiling new versions all the time, keeping the addresses up to date is just silly.

Third, I wanted something like the trainers of the past, that’s just ready to go once you load the game.
So here’s cheat.py

To utilize it you need DOSBox Python support
For Windows there is pre-built installer on the download page
To install it in Linux try this:
git clone git://github.com/stt/dosbox-python.git; cd dosbox-python
sh autogen.sh; ./configure --enable-debug=heavy --enable-python; sudo make install

Currently the script supports SQLite database for loading/saving variable locations and code change locations. Also rudimentary memory scanning/filtering/holding operations are available.
Once the script is loaded into DOSBox, when you break into the debugger and run HELP, you’ll also get the commands that the script supports..

---(Output          Scroll: home/end    )---
cheat.py commands:                                                              
CLST     List available code changes for running binary                         
COFF     Reverse previous code change                                           
CON      Alter the code / apply a cheat                                         
MFILT    Filter previously searched locations with a new value                  
MHOLD    Watch a memory location and prevent changes (16bit values atm)         
MLST     List previously searched/filtered values                               
MSAVE    Save current set of variables                                          
MSRCH    Search allocated memory for a value                                    
MV       Modify variable
Debugger commands (enter all values in hex or as register):                     
F3/F6                     - Previous command in history.                        
F4/F7                     - Next command in history.                            

Since it’d just be silly to start collecting cheats to each game from scratch, I tried to find some software that had an existing library of cheats and try to reuse those.
From what I gathered from google there seems to be surprisingly few projects still alive, ArtMoney was the simplest to adapt so I wrote this parser for the table files..

It supports .amt file format versions 2-11 and should parse cheats for the 220 DOS games available from the official site.
One thing though with these premade tables is that pretty much all of them have a different zero address, so thus far I’ve been using this small script to readdress the variables based on one known offset.

Each time a binary file is executed a hash is calculated (using MurmurHash3 algorithm, it’s one of the fastest and most collision resistant hashes) and if cheat.py finds a match for the hash in cheat.db SQLite database it loads the variables to the debugging UI.

Here’s an example of a game with fields:

---(Variable Overview                   )---
Item 8           0x0000   Item 6           0x0000   Item 3           0x0000     
Item 2           0x0000   Item 1           0x0000   Item 7           0x0000     
Gun              0x0000   Item 5           0x0000   Item 4           0x0000     
Health           0x0006   Highlighted Ite  0x0000                               

---(Output          Scroll: home/end    )---
         0: EXEC:Execute BLACK.EXE 0                                            
Running hash 54fc432a                                                           
11 fields and 0 codes for Black Thorne
Item 8 11ae8                                                                    
Item 6 11ae4                                                                    
Item 3 11ade                                                                    
Item 2 11adc                                                                    
Item 1 11ada                                                                    
Item 7 11ae6                                                                    
Gun e06c                                                                        
Item 5 11ae2                                                                    
Item 4 11ae0                                                                    
Health 112da                                                                    
Highlighted Item 11ad8

cheat.py also adds MV (Modify Variable) that takes a variable name (case-sensitive) and hex value to set to the variable, but you can of course take an offset from the field list in the log and do D 0:112da to verify and then SM 0:112da 06 to set memory to a value (keep in mind it’s little-endian).
Or, if you command MHOLD Health for example, it creates a breakpoint that triggers a callback when a memory location is changed and restores the original value.
(If held memory area is written to constantly there’s likely to be some performance drawbacks, for e.g. immobilizing enemy it’d make more sense to NOP some code.)

Example of MHOLD output

holding Health{70362: '\x04\x00'}                                               
DEBUG: Set memory breakpoint at 0000:112DA                                      

And an example of a code change, last two lines are from CLST and CON Invincible commands

---(Output          Scroll: home/end    )---
      5853: EXEC:Execute C:\EOB.EXE 3                                           
Running hash e7565a0a                                                           
0 fields and 1 codes for Eye of the Beholder 1
Invincible: found at offset 59812                                          
Invincible applied

There is a wx GUI in the works to manage the cheats without having to write commands, but that’s a post for another day.
Until then, happy gaming.

» Comments