Assets - Analyzing Mission Files
Traffic Department's mission files present a considerable challenge to analyze because they are have an arbitrary design and relate to conceptual ideas in the game that have no conventional format. The original developer could have stored any information in any order they wanted. Luckily for us, the 59 mission files are all the same size (20,080 bytes), which means we can assume that a byte in some position in one file has the same meaning across all the files. We'll study the byte statistics across this sample to determine their purpose. Like the other data assets, we only need to understand what the file is doing so we can incorporate it as-is in our prototype.
Mission File Format
The mission files contain the ship type data for 20 ships along with their mission paths that are directly applied to the map. An unusual point is that the first 19 ships have paths that include 501 points, while the last ship has only 481 points.
Mission File Analyzer in Python
We used a Python script to find the maximum byte values and section breaks across all positions among the 59 mission files. This gave us insight as to the purpose of the sections and byte values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import os, glob data = [] # Holds all bytes across all files data_max = [] # Holds max value for each byte position across all files # LOAD ALL FILES INTO DATA ARRAY for filename in glob.glob(os.path.join(os.getcwd(),'M*.TD*')): filedata = open(filename,'rb') byte_index=0 for line in filedata: for byte in line: try: data[byte_index].append(byte) except: data.append([byte]) byte_index+=1 filedata.close() # CALCULATE DATA_MAX VALUE ARRAY for bytearray in data: data_max.append(max(bytearray)) # CONVENIENT DISPLAY OF MAX DATA VALUES def show_data(minlines=0,maxlines=21000): offset=0 for item in data_max: if offset >= minlines: print(hex(offset),": ", item) offset+=1 if offset > maxlines: break # SHOW ALL BREAKS def show_breaks(): last_break=0 this_break=0 size=0 for i in range(len(data_max)): if data_max[i-1]==0: if data_max[i] > 0: this_break = i size = this_break-last_break-1 print(hex(this_break), "-- Size:", size) last_break = int(this_break) |
Map and Waypoint Analyzer in Python
We put all our knowledge together and extracted an NPC's mission waypoints and drew it out on the city map with this Python script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
file=open('TD.MAP','rb').read() # DANGLING FILE DESCRIPTOR mission=open('M1.TD1','rb').read() # SECOND PHASE map=[] #LOAD UP MAP for mapx in range(201): map.append([]) for mapy in range(201): if file[mapx*201+mapy] == 0x31: map[mapx].append('.') elif file[mapx*201+mapy] == 0x32: map[mapx].append('.') elif file[mapx*201+mapy] == 0x33: map[mapx].append('.') #elif file[mapx*201+mapy] == 0x3B: map[mapx].append('.') #elif file[mapx*201+mapy] == 0x3C: map[mapx].append('.') #elif file[mapx*201+mapy] == 0x3D: map[mapx].append('.') else: map[mapx].append('O') #LOAD UP SHIP WAYPOINTS SECOND PHASE for i in range(500): x_pos = mission[0x50+i] y_pos = mission[0x50+10000+i] map[x_pos][y_pos]='X' #PRINT OUT MAP for i in range(201): line='' for j in range(201): line+=map[j][i] print(line,end="") |
Mission Section Offsets
Reading in the correct data is important so we'll rely on the following offsets:
Section | First byte | Last Byte | Notes |
---|---|---|---|
Unused | 0x0000 | 0x0027 | One mission has a non-zero value in position 0x000A, but the reason isn't clear. This section is apparently unused |
Ship types | 0x0028 | 0x003B | One byte used per ship for twenty ships. Valid values are 0x00-0x08 and 0xFF. FF indicates an unused ship. |
Unused | 0x003C | 0x004F | Not used |
Ship 1 x-coord | 0x0050 | 0x0244 | Ship mission waypoints (501 points) |
Ship 2 x-coord | 0x0245 | 0x0439 | Ship mission waypoints (501 points) |
Ship 3 x-coord | 0x043A | 0x062E | Ship mission waypoints (501 points) |
Ship 4 x-coord | 0x062F | 0x0823 | Ship mission waypoints (501 points) |
Ship 5 x-coord | 0x0824 | 0x0A18 | Ship mission waypoints (501 points) |
Ship 6 x-coord | 0x0A19 | 0x0C0D | Ship mission waypoints (501 points) |
Ship 7 x-coord | 0x0C0E | 0x0E02 | Ship mission waypoints (501 points) |
Ship 8 x-coord | 0x0E03 | 0x0FF7 | Ship mission waypoints (501 points) |
Ship 9 x-coord | 0x0FF8 | 0x11EC | Ship mission waypoints (501 points) |
Ship 10 x-coord | 0x11ED | 0x13E1 | Ship mission waypoints (501 points) |
Ship 11 x-coord | 0x13E2 | 0x15D6 | Ship mission waypoints (501 points) |
Ship 12 x-coord | 0x15D7 | 0x17CB | Ship mission waypoints (501 points) |
Ship 13 x-coord | 0x17CC | 0x19C0 | Ship mission waypoints (501 points) |
Ship 14 x-coord | 0x19C1 | 0x1BB5 | Ship mission waypoints (501 points) |
Ship 15 x-coord | 0x1BB6 | 0x1DAA | Ship mission waypoints (501 points) |
Ship 16 x-coord | 0x1DAB | 0x1F9F | Ship mission waypoints (501 points) |
Ship 17 x-coord | 0x1FA0 | 0x2194 | Ship mission waypoints (501 points) |
Ship 18 x-coord | 0x2195 | 0x2389 | Ship mission waypoints (501 points) |
Ship 19 x-coord | 0x238A | 0x257E | Ship mission waypoints (501 points) |
Ship 20 x-coord | 0x257F | 0x275F | Ship mission waypoints (481 points) |
Ship 1 y-coord | 0x2760 | 0x2954 | Ship mission waypoints (501 points) |
Ship 2 y-coord | 0x2955 | 0x2B49 | Ship mission waypoints (501 points) |
Ship 3 y-coord | 0x2B4A | 0x2D3E | Ship mission waypoints (501 points) |
Ship 4 y-coord | 0x2D3F | 0x2F33 | Ship mission waypoints (501 points) |
Ship 5 y-coord | 0x2F34 | 0x3128 | Ship mission waypoints (501 points) |
Ship 6 y-coord | 0x3129 | 0x331D | Ship mission waypoints (501 points) |
Ship 7 y-coord | 0x331E | 0x3512 | Ship mission waypoints (501 points) |
Ship 8 y-coord | 0x3513 | 0x3707 | Ship mission waypoints (501 points) |
Ship 9 y-coord | 0x3708 | 0x38FC | Ship mission waypoints (501 points) |
Ship 10 y-coord | 0x38FD | 0x3AF1 | Ship mission waypoints (501 points) |
Ship 11 y-coord | 0x3AF2 | 0x3CE6 | Ship mission waypoints (501 points) |
Ship 12 y-coord | 0x3CE7 | 0x3EDB | Ship mission waypoints (501 points) |
Ship 13 y-coord | 0x3EDC | 0x40D0 | Ship mission waypoints (501 points) |
Ship 14 y-coord | 0x40D1 | 0x42C5 | Ship mission waypoints (501 points) |
Ship 15 y-coord | 0x42C6 | 0x44BA | Ship mission waypoints (501 points) |
Ship 16 y-coord | 0x44BB | 0x46AF | Ship mission waypoints (501 points) |
Ship 17 y-coord | 0x46B0 | 0x48A4 | Ship mission waypoints (501 points) |
Ship 18 y-coord | 0x48A5 | 0x4A99 | Ship mission waypoints (501 points) |
Ship 19 y-coord | 0x4A9A | 0x4C8E | Ship mission waypoints (501 points) |
Ship 20 y-coord | 0x4C9F | 0x4E6F | Ship mission waypoints (481 points) |
Mission File Usage
We'll eventually have to create the NPC ships in our prototype. We'll read in the mission file as-is and pull the 20 ship type headers to create the appropriate ships. Then we'll attach an AI object to each ship that holds the paths.