Table of Contents

Note: all position data referenced in this document refers to level coordinates, which for X goes from 0x0000 to 0x1000 and for Z goes from 0x0000 to 0x2000.

Also apologies, this explanation is super muddy and confusing, I wrote it in a rush.

Level Elevation Data

Level elevation data starts at 0x80182D00, 0x10 bytes per entry, with a break between the first and second height data sets. This is the format of each entry, seems to follow a typical vertex format:

XXXXYYYY ZZZZ—- SSSSTTTT CCCCCCCC

* X = X Position : 2 byte signed short * Y = Y Position : 2 byte signed short * Z = Z Position : 2 byte signed short * - = Unknown, seemingly always 0x00 * S = Unknown, seems to be related to X position * T = Unknown, seems to be related to Z position * C = Unknown, looks like color data? Typically 808080FF

There's two groups of these height data sets. The first one puts points at every 0x0100 in the map grid from 0x0000 by 0x0000 to 0x1000 by 0x2000 (so a total map of 17 x 33 points). The second draws all the in between points at 0x80 intervals (a total of 16 points in a hashtag block that is 0x0200 by 0x0200, for a total of 8×16 of the blocks). See below for more description on the format of the data.

Encoded Data

Each point (of which only the height data is stored) is preceded by a command byte that contains flags that define certain behavior from the point. It seems that the height data is always copied over, but the different command flags affect the other vertex data associated with the point.

The encoded data follows this sort of pattern:

[Command Byte] [Pre-Data] YYYYY [Post-Data]

Y = Y position : 2 byte signed short

Depending on the command byte, there might be pre-data or post-data, which are typically a byte or two. The most common command byte is 0xC0, which has no pre-data and post-data.

Vertex Order in Encoded Datasets

The first data set is handled fairly simply. It lays out a full row of points before continuing on to the next row. However, it skips every other point, then after the end it returns to the start and uses the skipped points. So since there are 17 points per row, the order of a single row of points will be:

1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16

The second data set is more complicated. It fills in the spaces between and around the points from the first dataset. It is stored in blocks of 0x200 by 0x200 positional data, going left→right and up→down. I expect that the second dataset is mostly included on close-up ground areas, as to reduce poly-count on far-away models.

Here's a table of the order of height data points from the second set. Every 16 points will follow this pattern for a specific 0x200 by 0x200 block. The X's represent data that is handled by the first height data set:

(columns are X values, rows are Z values)

0x0 0x80 0x100 0x180 0x200
0x0 X 1 X 2 X
0x80 3 9 10 11 4
0x100 X 12 X 13 X
0x180 5 14 15 16 6
0x200 X 7 X 8 X

Point of interest: the 4th, 6th, 7th, and 8th points will overlap with points 1, 2, 3, and 5 on other blocks. They will be duplicate data, which is a little inefficient.

For anyone curious, the process which decodes one of the two height data sets is at 0x802517C4. It appears to generate the vertex data separately before decoding and assigning height data to it.