SpriteTile level file format specifications

Version 3

Datatypes used:
byte (1 byte, System.Byte)
short (2 bytes signed integer, System.Int16)
ushort (2 bytes unsigned integer, System.UInt16)
int (4 bytes signed integer, System.Int32)
float (4 bytes single-precision floating point, System.Single)

1. Header

The first 24 bytes consist of a header:

The first 15 bytes should consist of the characters "SpriteTileLevel" in UTF-8 format.

The next byte is either 0 or 1, where 0 = big endian and 1 = little endian. This refers to whether data in the file that consists of more than one byte, such as floats or shorts, is stored in little endian format or not.

The next 4 bytes are an int that stores the SpriteTile level version. Currently this is 3.

The next 4 bytes are an int that indicates the number of tags used in the file. There should always be at least one tag.

2. Tags

Tags are a sequence of 7 characters in UTF-8 format. Each tag is followed by an int, which contains the location in the file of the relevant tag data. The location is the number of bytes from the start of the file, beginning at 0.

Saving a level normally only requires one tag, "lvlayrs", which is the level layers data. So the next 7 bytes are "lvlayrs" in UTF-8, and the next 4 bytes are an int containing the value 35, the location of the level data, since bytes 0-34 are header/tag data.

However, if the number of tiles in a set is different from the default of 1024, then the "numsets" tag should also be used. In this case an additional 11 bytes are used for the "numsets" tag and the 4 location bytes, so the location of the level data can be 46 if it's stored directly after the header/tag data, or 50 if it's stored after the "numsets" data (which consists of a single int). If the level data is stored at 46, then the location of the "numsets" data depends on the size of the level.

(Additional tags exist, but are used for TileEditor preferences such as preview size, collider overlay color, etc., and are not covered in this document. If these optional tags are not found when loading a file into the TileEditor, it simply uses default values.)

So a level file can be either of these:

Header data (24 bytes)
"lvlayrs" tag (7 bytes)
An int for the lvlayrs data location (4 bytes)
The lvlayrs data (size varies)

or

Header data (24 bytes; make sure the number indicating the number of tags is 2)
"lvlayrs" tag (7 bytes)
An int for the lvlayrs data location (4 bytes)
"numsets" tag (7 bytes)
An int for the numsets data location (4 bytes)
The lvlayrs data (size varies)
The numsets data (4 bytes)

In this case the lvlayrs data and numsets data can be switched; just make sure the location for each tag points to the correct place in the file.

3. "lvlayrs" tag data

The first 4 bytes are an int containing the number of layers.

For each layer:

7 bytes for a "lyrdata" tag in UTF-8 format.

4 bytes (float) for the layer's X tile size. Note that this is not how many tiles wide the level is; that comes later.

4 bytes (float) for the layer's Y tile size (same as X if the layer uses square tiles). This is not how many tiles high the level is.

4 bytes (int) for the layer's X scroll position. This is only used by the TileEditor, so just using 0 is fine.

4 bytes (int) for the layer's Y scroll position. This is only used by the TileEditor, so just using 0 is fine.

4 bytes (int) for the layer's preview size. This is only used by the TileEditor, but 64 is the default, so using 64 is a good idea in case the level is loaded in the TileEditor.

4 bytes (float) for the layer's Z position in world space.

4 bytes (int) for the layer's LayerLock value. This can be one of four values:

0, which indicates LayerLock.None
1, which indicates LayerLock.X
2, which indicates LayerLock.Y
3, which indicates LayerLock.XandY

4 bytes (int) for the layer's addBorder value.

4 bytes (int) for how many tiles the layer is wide (i.e., the X axis).

4 bytes (int) for how many tiles the layer is tall (i.e., the Y axis). This number times the previous number should equal the total number of tiles in this layer.

This is followed by the layer's map data, for which the number of bytes depends on the size of the layer. Each map cell uses 7 bytes, so e.g. a 10x10 level would take 700 bytes. The map data is written starting from position (0, 0), or in other words the lower-left corner. The position advances along the first row (that is, the bottom-most row), and upon reaching the end, starts over with the second row, and so on, ending at the top-right corner.

For each map cell the data must be written in this order:

2 bytes (short) for the tile info. This consists of two numbers, the tile set and the tile number, combined into one short. The set number depends on how many tiles per set are allowed, which is defined when the TileManager is first started. There are 5 possible values: 16384, 8192, 4096, 2048, and 1024. The default is 1024, which means there is a maximum of 32 sets. If something other than the default is used, the "numsets" tag should be used (see below). In order to get the combined tile number, multiply the tile set number by the number of tiles in a set, then add the tile number. For example, using the default of 1024 tiles in a set: TileInfo(2, 14) would be 2*1024 + 14 = 2062. Empty tiles are indicated by TileInfo(0, -1), which is stored as -1.

2 bytes (ushort) for miscellaneous map cell data, which currently consists of a bit for the collider state, a bit for X flip, a bit for Y flip, and 11 bits for rotation. The remaining 2 bits are unused at this time. To compute the rotation, multiply by 5 and convert to a ushort. For example, if the rotation is 45.2 degrees, then 45.2 * 5 = 226. This allows 360 degrees to fit in 11 bits with a precision of 0.2 degrees. (The rotation must be between 0.0 and 360.0; no negative values or values higher than 360.0.) The rightmost 11 bits are used for the rotation, the 12th bit is used for the Y flip, the 13th bit is used for the X flip, and the leftmost (16th) bit is used to indicate the collider state, where active = true (1) and inactive = false (0). So if the Y flip is active, add 2048 to the rotation, otherwise add nothing. Similarly, if the X flip is active, add 4096. Finally, if the collider state is active, add 32768. For example, a cell with a rotation of 45.2, flipped on the X axis, and an active collider, is (ushort)(45.2 * 5 + 4096 + 32768) = 37090.

2 bytes (short) for the order-in-layer value. This ranges from -32768 to 32767.

1 byte for the trigger value. This ranges from 0 to 255.

So, an example of a level with 3 layers would have this data layout:

Number of layers (4 bytes)
Header data for layer 0 (47 bytes)
Map data for layer 0 (size varies)
Header data for layer 1 (47 bytes)
Map data for layer 1 (size varies)
Header data for layer 2 (47 bytes)
Map data for layer 2 (size varies)

4. "numsets" tag data

This is simply an int that contains the maximum number of tile sets used for this file. If the TileManager in the Unity project has been set up with a different number than that contained in the file, the level may not load correctly. There are 5 possible values: 32 (the default), 16, 8, 4, and 2. If the default of 32 is used, the "numsets" tag and data may be omitted.

5. Compression

SpriteTile levels are compressed using LZF. After all the data is added, the entire thing should be LZF compressed before saving. Source code for LZF compression can be found here.

6. File extension

If the SpriteTile level is intended to be placed into a Unity project, it should have the file extension ".bytes".