[By Josef: This is a page written by Chris Becke, which disappeared from the web some time ago. Because it still contains useful information, I present it here. Everything here was written by Chris Becke, except for this introduction.]
Here is some stuff I have relating to Transport Tycoon.
Transport Tycoon VersionsThese are the versions of Transport Tycoon I know of:
Box Name | Released By | Description |
---|---|---|
Transport Tycoon Playable Demo | Microprose | The original Transport Tycoon playable demo runs for two years, and only trains are available. |
Transport Tycoon | Microprose | The original Transport Tycoon |
Transport Tycoon World Editor | Microprose | The original Transport Tycoon, with a scenario editor and an addition tileset for the surface of mars. |
Transport Tycoon Deluxe | Microprose | The enhanced version of the game features 4 environments: classic (temperate), tropical / dessert, alpine and toyland. Mars is NOT included. |
Transport Tycoon Deluxe for Windows | Hasbro | A windows port of Transport Tycoon Delux shipped as part of the Hasbro Tycoon Collection - a package set that includes Rollercoaster Tycoon, Railroad Tycoon II and Transport Tycoon Deluxe |
After installation the tycoon.bat file must be edited. It runs ttdlx.exe with a parameter indicating where to find the cd files. Either copy the files from the root of the cd-rom into the tycoon directory (and delete the command line parameter), or change the parameter to point to where the cd-rom image is stored.
Phil McCrum's and Bill & Mikes Tycoom pages are excellent and full of info, even if they do overlap to a large degree. When they finally bore you, there are many other good pages that can be found searching the Transport Tycoon Webring.
The files in the Transport Tycoon Delux CD and directory are generally of one of the following types:
tttools.zip (5Kb) is a small archive containing some interesting things.
Yes these tools lake a little finesse :) I can't really be bothered to put a lot of work into them.
The cat file begins with an index with 4 byte values alternately indicating the position in the file of the next BLOB, followed by the length of the BLOB.
There is no count of the number of entries in the index - a reader must simply stop reading the index when it reaches the first BLOB
The actual BLOB data is made up of two parts: A name field, and the source file data.
The name field begins with a byte counting the number of bytes of name that follow (not counding the initial byte). The bytes that follow seem to be a zero terminated ascii string.
This code fragment shows you might read in a BLOB. pfile is a file pointer that has already beek seeked to the BLOB offset as obtained from the index:
unsigned char cb;
fread(&cb,1,1,pFile);
fread(pName,cb,1,pFile);
fread(pBuffer,length,1,pFile);
The GRF files appear to be a catalog of sprites. The file consists of a variable number of sprite blobs, and finishes with a 4 byte value being the sum of the lengths of the sprite blobs.
Each sprite blob begins with a 16bit number being the number of bytes that follow the length field, before the next sprite blob (or the end of the file). These 16bit headers are not counted in the file size written to the end of the file
At this time I don't know how to decode the sprite data itself. It appeard the 1st byte has some significance - 0xff & 0x02 are checked by the loading code. The sprite painting routines appear the skip the 1st byte.
The save consists of three streams of compressed data, followed by a 4 byte checksum value calculate a rather unorthodox way. Was Chris Saywer really tring to protect the files from corrutpion? Or hacking? Well, here the formula is, which only goes to show that game authors should spend more time improving the game, and waste less time trying to stop creative editing :)
this table describes the basic file layout. Whereever two sizes are given, the first size refers to the size of the structure in TTO, the 2nd size is the size in TTDLX
field | Size (bytes) | Description |
---|---|---|
name | 39 / 47 | The name the file was saved as |
name-check | 2 | A 16bit checksum of the name to allow the load dialog to display a list of games without checksumming the entire file. |
data | (variable) | The RLE compressed data. |
file-check | 4 | A checksum of the entire file, including the name, name-check, and data fields. |
The name checksum is calculated by adding each byte in the name field to a 16 bit accumulator, rotating the accumulator left one bit after each addition. Finally, the checksum is xored with 0xAAAA.
This C-code function demonstrates how.
WORD CalcTitleChecksum(
char* pszTitle, // A pointer to the name of the savegame
int cb) // set to either 39 or 47 for TTO or TTD savegames
{
WORD sum = 0;
for(int i=0; i<cb; i++)
{
sum += pszTitle[i];
sum = (sum << 1) + (sum >> 15);
}
return sum ^ 0xAAAA;
}
The checksum is calculated by summing each byte in the file, in sequence, into a 32bit checksum value. The result of the addition is truncated to the lower 8 bits. The checksum is then rotated left 3 bit positions. Finally, 105116 is added to the checksum value. The following pseudo code demonstrates how to calculate the checksum:
Transport tycoon deluxe changes the checksum calculation only slightly. Instead of adding 105116, 201100 is added. Or subtracted, in the case of title.dat (the map thats loaded to display in the background of the main menu!)
unsigned long calcchecksum(unsigned char* pbData, int cbData)
{
union
{
unsinged long l;
unsigned char b;
} checksum = 0;
for(int i=0; i<cbData; i++)
{
checksum.b += pbData[i];
checksum.l = _lrotl(checksum.l,3);
}
if(version == 1)
checksum.l += 0x019a9c;
else if(loading_title)
checksum.l -= 201100;
else
checksum.l += 201100;
return checksum.l;
}
If you actually use this, don't forget that the file size you pass in must be less the 4 byte checksum itself.
The compressed data section, once expanded, contains the following game structs:
The game structs are:
The compressed section, once expanded, contains the following game structs:
The decompression algorithm is a RLE variant that works like this:
Starting at position 41 / 49 read a byte - this we will call the rlecode. If the rlecode byte is positive then read the next (rlecode+1) bytes and write them to the decompressed data. If the rlecode is negative then the next byte is repeated (-rlecode+1) times in the decompressed data. Immediately following the (one or many) bytes just read is the the next rlecode byte (unless of course you have run out of file).
Once again - an incredible stupid C function demonstrating the procedure
void rle_expand(
BYTE* pBuf, // For TTD, a pointer to a 618873 byte buffer
BYTE* pSrc, // points the the source RLE data.
int cb) // the size of the sorce data.
{
BYTE* pEnd = pSrc + cb;
while(pSrc < pEnd)
{
int code = (char)*pSrc++;
if(code < 0)
{
BYTE v = *pSrc++;
do
*pBuf++ = v;
while(code++);
}
else
{
do
*pBuf++ = *pSrc++;
while(code--);
}
}
}