Line Rider File Format

a dodgy line-rider-readerA while back I tried to "reverse-engineer" the Line Rider shared object - the file that stores your Line Rider Tracks. The idea was to add a much required "erase" function. I mostly figured it out - but owing to my extremely short attention span it quickly entered my immense "1/2 finished projects" repository.

In an effort to get some more posts happening around here I've decided to unleash a few of these 1/2 finished projects. The Line Rider File Format is the first such post. I chose this one because there is supposed to be a new version of Line Rider due out very soon - once this happens this information will be totally useless, instead of just mostly useless.

Line Rider stores its track data using a Flash Shared Object. On Windows, Shared Objects are stored under the \documents and settings\username\Application Data\Macromedia\Flash Player\#SharedObjects directory. It will vary depending from what web site you loaded Line Rider flash file. The directory will contain a file called "undefined.sol".

"undefined.sol" contains all your Line Rider tracks. I opened this file up in a hex editor and started poking around. After changing a few of the values I noticed that I did indeed make a line move, so further investigation would be required. I found this good reference for the .sol file format on SourceForge which helped a lot. The rest was just drawing lines, and seeing the effect it had on the file. Heres what I got:

The header


==== Header =====
00 BF       // Start : 2 bytes
00 00 00 BF // Files size: 4 bytes (from after this byte)
54 43 53 4F // Ascii - 'TCSO' - Filetype : 4 bytes
00 04 00 00 00 00 // Always these 6 bytes

The header is standard to all Shared Object files.


==== Data Block ====
00 09  // Length of Shared object name : 2 bytes
75 6E 64 65 66 69 6E 65 64	
  // 'undefined' - the name of the shared object : 9 bytes
00 00 00 00  // 4 empty bytes

The data block has the file name - in our case "undefined" (nice naming work there :)


==== Items ====
00 09 // Length of the root object name : 2 bytes
74 72 61 63 6B 4C 69 73 74
  // 'trackList' - the root object name : 9 bytes

The list of tracks is called the "trackList". That's better naming work there. All Line Rider files are the same up to this point. Now we move on to the good stuff...

The good stuff

=== Saved Tracks Array ====
08  // Saved Track array: 1 byte
00 00 00 01			
  // Number of tracks you've got saved
  // (just one here in this case): 4 bytes.

00 01 30
  // Track Array index : 3 bytes
  // [increases by 1 each track]

This shows the number of tracks you have got saved. All of the array indexes are written in this format 00 01 30, 00 01 31, 00 01 32 etc... I was confused as to why they started at 30, until I found another great Shared Object reference on SourceForge - which showed that 30 in ascii is "0". Still seems weird, but there you go.

Ok, next we move on to the guts of the file - this is where all the lines for each track are stored.


=== Track Object ===
03 					// Declare object for track : 1 byte
		
  === Track Object Data ===
  00 04				// Object property 1 - name length : 2 bytes
  64 61 74 61			// Ascii for "data" - this is the object name : 4 bytes

      ==== Line Array ====
      08			// Line data array : 1 byte
      00 00 00 01	// Number of lines in this track - just one here! : 4 bytes

      00 01 30 		// Line array index : 3 bytes [increases by 1 each line]

        ==== Line Point Array ===
				
        08			// Line Point array : 1 byte
	00 00 00 05	// Number of points per line : 4 bytes

	/* 
	    Each straight line segment is made up of 5 points 
	    X1, Y1, X2, Y2 and a blank one.					
	*/
				
        00 01 30	// Point index 1 : 3 bytes
	00			// Declare number variable : 1 byte
	3F F0 00 00 00 00 00 00 	// Initial X point position : 8 bytes

	00 01 31 	// Point index 2 : 3 bytes
	00			// Declare number variable : 1 byte
	3F F0 00 00 00 00 00 00 	// Initial Y point position : 8 bytes

	00 01 32	// Point index 3 : 3 bytes
	00			// Declare number variable : 1 byte
	40 28 00 00 00 00 00 00		// Ending X point position : 8 bytes

	00 01 33	// Point index 4 : 3 bytes
	00			// Declare number variable : 1 byte
	3F F0 00 00 00 00 00 00		// Ending X point position : 8 bytes

	00 01 34	// Point index 5 : 3 bytes
	00			// Declare number variable : 1 byte
	00 00 00 00 00 00 00 00		// Empty : 8 bytes

        00 00 09	// End Point Array : 3 bytes
			
      00 00 09		// End Line Array : 3 bytes

THAT is the cool bit! Changing the 8 byte numbers (Stored in Big Endian order - thanks Lee!) will change the start and end points of your lines! Line Rider saves the .sol file in memory until you close, or navigate away from the line rider page. This means that for your changes to take effect you must navigate away from the line rider page, then make your changes, then re-load Line Rider.

Wrapping it up

=== Track Object Names ===
00 05			
  // Object property 2 - name length : 2 bytes
6C 61 62 65 6C	
  // Ascii - 'label' - labels for track names : 5 bytes
02     // Declare string type for track name : 1 byte
00 03  // Track name length : 2 bytes
6F 6E 65		
  // the ascii track name given by user -
  // in this case its creatively called 'one'
		
00 00 09  // End of Track object : 3 bytes
00 00 09  // End of Saved track array : 3 bytes	
00        // Game over. 1 byte.

The tricky bit

The only gotcha left in the file format is that subsequent saved tracks do not store all the lines that are in previous saved tracks - only the new lines. So for example, if you make a track called "one" with 5 lines in it, then you draw 5 more lines and save it as "two", then track "two" does not store all the details of the first 5 lines from track "one".

To save space, the duplicated lines are stored as pointers to the original lines. In the Line Array details above, duplicated lines are stored as a pointer (shared object type 07, instead of the array type 08). The 2 bytes following "point" to the index of the original line.

So there you go! A real saved line rider file will be much bigger that above as this file only had one saved track, with one line in it. However, it's pretty easy to write a parser for it - I made one in dot net that opened a line rider file and printed out your track using GDI. It could even delete lines too! But it is wayyyy to flaky to put up here. It's the kind of code I'd prefer no one know that I'm capable of writing.