I love the 1990-ass design of Railroad Tycoon's save system.
not "which directory do you want to save your game?", but "which drive has your save game disk?"
You know, cause you'd put the game in drive A:, and if you were fancy, you'd save to a blank disk in drive B:
Too poor to have two drives? Gotta swap them to save, then swap back to keep playing.
okay, now that's interesting. So, backstory info:
Railroad Tycoon (1990) stores most images as .PIC files. The format is unknown but seems to start with either 06, 07, 0F, then a 00, then the width and height as uint16s. You can do the VN-trick to decode them: rename your target file to one of the ones the game shows at startup, and it'll display it for you. (credits2.pic, for example)
now when you save your game, it saves two files: RR1.SVE and RR1.MAP.
(The 1 can be 0-5 depending on which slot you save into/if it's an autosave).
Now the obvious guess is that the game saves things like "how much money you have" and "where your trains are" in the .SVE, and the current state of the map in the .MAP file, since the game world dynamically changes over time.
Here's the weird part: The .MAP file? It's actually a PIC!
I started a game in Western-US and saved my game after making a quick railroad, and then renamed my RR1.MAP to CREDITS2.PIC and started the game. This is what I got:
now that's super interesting because PIC files are compressed!
And the game can SAVE them, not just load them! that's unusual for games of this age!
interestingly the images in the MAP files are set to 320x200 (like all the other images), even though they're clearly actually 256x192. I guess it was easier to write a compressor/decompressor that just assumes 320x200.
anyway this also lets me do something I've always kinda wondered about but can finally tell for sure:
Yep, the game is dynamically generating the maps when you start, not just animating them fancy. Here's two starts of a Western-US map:
the rivers are identical, though.
And here's why: The WESTUS.PIC file is used as the base, and as you can see, it's got some basic stuff laid down already:
So the game takes that, then overlays mountains, industries, and cities, using some kind of procedural generation.
that's why it has to save the whole map when you make a save game: because your map is unique.
(and presumably re-generating it from the seed would take too long on some of the computers this game ran on)
arg. I can't easily hack the game's binary because it's compressed using LINK /EXEPACK AND it uses overlays, which apparently means the offsets to the overlays are encoded into the exe... based on the compressed version. So if I decompress it, the game won't load at all.
UNP says the game has 87365 bytes of image and 109305 bytes of overlay.
that's more overlay than non-overlay! HOW MUCH OVERLAY DO YOU NEED, MAN?
okay "unp e -g" seems to have fixed it. the regular mode messed up the overlay, but merging it in fixes it
awesome. I have officially broken it. But I have broken it CAREFULLY.
okay so I hacked it to go into an infinite loop after showing the first image of the intro sequence, then wrote a script to copy every image into the first image of the intro, start dosbox-x, then wait a couple seconds then screenshot it. So I now have all the images.
Here's the faces:
man I thought this game just had a bunch of random white-guy (well, redish-guys) portraits that it matched with some names of historical-guys-who-built-railroads.
NOPE! every name is matched to a face.
Here's all the names from the executable
I don't think these guys actually show up in the game. Interesting.
so according to the manual, the railroad operators come in three flavors: builders, robber barons, and mixed.
But looking at the profiles in the EXE, there's 4 numbers associated with each name, in the range 0-4. There's no correlation that I can see.
Interesting. There appears to be some code for verifying disk sectors: Given that there's no other code that works at the sector-level, I suspect that's disk-based copy protection. Like, they intentionally broke some sectors on the original disks, then made the game fail if those sectors weren't CRC-fails?
which is weird because the game has manual-based copy protection and supports being copied to the hard drive
I'm gonna have to dig up my original disks and image them
ahh, nope. not copy protection: I checked and the game calls this function to see if a disk is valid for saving onto, and it does it by looking if it can see sector #2 on the given disk drive.
help my train has negative horsepower
by manipulating the horsepower, you can get the grade/cars calculation to take some weird turns. This train cannot move if it's on flat ground with only 1 car, but as soon as there's a hill or more cars, it's a speed demon
interestingly, these values don't seem to actually correspond to train performance.
I set my train up to have a very high max speed with lots of (positive) horsepower, and it managed to drive 26 miles at 42 miles an hour.
that's... better than a plain 0-4-0 Grasshopper, but not by the amount you'd expect
there's 42 bytes of data for each train and I understand 26 of them. that's not good
I figured out two more bytes: There's a "year this train is invented" short int, and it seems that when you start a game, it adjusts the years randomly.
So although the EXE says the 4-2-0 Norris says 1836, when I started a game, it changed to 1833.
oh wow. they set an interrupt to trigger every time the mouse moves?
that's... that's a lot of times.
apparently Ghidra doesn't understand DOS overlays, which means it keeps getting confused by the fact that they involve data in the middle of a function
every "INT 3F" is actually followed by a byte and a word, but ghidra tries to decode that data as code. it doesn't end well
let's desync the variable-length instruction encoding! x86 is the best machine code!
"Interrupt 43: Note: This is not a callable vector!"
SO WHY IS INT 0x43 IN THIS CODE
I'm definitely a fan of the fact that ASCII text can be confused for x86 code. that's great.
Annoyingly it turns out I have two different versions of Railroad Tycoon here: 455.00 and 455.02
And all my copies of 445.00 are cracked
why does this code have "SUB AX,AX"?
that just changes AX to 0. But... the normal (and faster, I think) way to do that is XOR AX,AX.
Other than some flags (which this code doesn't use), they're the same. Strange.
can't believe GHIDRA doesn't have MSC 5.1 identification in its libraries.
but it looks like MSC 5.1 is the compiler used for this game
@foone I made a bunch of FunctionID databases for old DOS compilers, including MSC 5.1
https://github.com/moralrecordings/ghidra-fidb-dos-win16 
GitHub - moralrecordings/ghidra-fidb-dos-win16: Scripts for scraping vintage x86 C/C++ libraries in Ghidra, in order to generate FunctionId databases.
Scripts for scraping vintage x86 C/C++ libraries in Ghidra, in order to generate FunctionId databases. - moralrecordings/ghidra-fidb-dos-win16
GitHub