View Source Code on GitHub


How it works

SimCopterX patches various game functions by overwriting assembly instructions in the Windows PE (portable executable). Since the game was made before the ASLR standard, the patching process is straight-forward in converting relative virtual addresses to file offsets. Supporting various versions of SimCopter with a single patcher however is a somewhat complicated process, namely I can't just open a disassembler such as IDA and run a diff patcher. Instead each function must be compared amongst all the versions and a commonality must be identified. When patching the function there is a space limitation to what I can accomplish "inline", if my patch is too big then a detour is written to an area in the PE which is free. On the first version of SimCopterX, it only supported one version of SimCopter so I just chose an area in the PE already available. Now since I support multiple versions, the addresses where I keep my detour can't be the same so I added a new section to the PE called ".detour". This detour section is a safe and reliable space where I can write my detour code for functions that are too small to hold that data normally.

Having this dynamic detour section is only one component of having dynamic assembly patcher code. As you'll see below, I created a pseudo assembly class to help write-up my patches. Each function will have a brief descripion of what it does, their virtual addresses, and of course my code. View the source code on GitHub by following the link at the top of the page.

Patched Function List

Below is a list of identified functions, a brief description, and a before-and-after graph view. I use SimCopter Classics Version (1998) as the basis for the graph views (and generally as my starting point for disassembly). Please see the source code for corresponding function address entries (virtual).

Main loop
Description: This is where I added a Sleep after the PeekMessage routine. It loads in the Sleep time (ms) based on the option submitted through the SimCopterX Launcher - which stores that variable in the .detour section.
Original Graph: Click to view
Patched Graph: Click to view


Global Initialization
Description: Originally this function loaded the hardcoded width/height for the game, and manually set the 'resolution type' variable to "1" (640x480). Now this function calls the 'resolution lookup' function instead.
Original Graph: Click to view
Patched Graph: Click to view


CD Check
Description: Only one instruction was changed to avoid CD checks, this was done because both the game CDs and CD-ROM drives themselves are getting very scarce.
Original Graph: Click to view
Patched Graph: Click to view


Chopper UI
Description: This was one of the more tricky parts for SimCopterX because the UI for the chopper is statically designed against 640x480. On the higher resolutions I came up with a placement scheme which places the UI elements dependent on the width/height - while retaining the original layout for the original resolution.
Original Graph: Click to view
Patched Graph: Click to view


Flap UI
Description: The flaps are the chopper controls such as the bambi bucket, rescue harness, and megaphone. These elements are separate from the chopper UI. Their placement was originally static, however it was completely replaced with a dynamic placement scheme without sacrificing the original 640x480 positions.
Original Graph: Click to view
Patched Graph: Click to view


Chopper Clip
Description: The chopper view clip is how the game is clipped while in the chopper, which is different than when you are walking around because of the UI. Since the chopper UI takes off 80px from both width and height, the area behind the UI isn't rendered in the chopper view for 640x480. For higher resolutions, the UI doesn't completely cover the screen so the chopper clip isn't performed.
Original Graph: Click to view
Patched Graph: Click to view


Resolution Lookup
Description: This function is strange in the fact that it was a function which was definitely supposed to be used, but the SimCopter development team did not end up using it - so I did as the basis for my multiple resolution support. Originally the "resolution type" is always set to "1" (see global init function above), so this function would always return 640x480. Most of the time this function was never even called, however any place where a resolution type check was performed I replaced it with a call to this function. The only changes here are the resolution width/height values.
Original Graph: Click to view
Patched Graph: Click to view


Screen Clip
Description: I'm still not entirely sure exactly the full extent to which this entire function affects, but currently I'm using this to ensure placement of flap menus are correct on high-resolution modes. Without making this patch, flap menus (not the flaps themselves) would appear at their old 640x480 locations.
Original Graph: Click to view
Patched Graph: Click to view


DirectDraw Palette
Description: It seems that in windowed mode the LOGPALETTE PALETTEENTRYs are generated manually, whereas in Fullscreen mode there's a call to GetSystemPaletteEntries. The manual palette populating formula seems to have problems with both the colors and flags so the former is now always used.
Original Graph: Click to view
Patched Graph: Click to view