Format of the Cvx BGL Files
Introduction
CVX Files can be read by the TmfViewer.exe application provided with the FS SDK. They contain TERRAIN_VECTOR_DB sections. These sections and subsections describe the different layers (roads, railways, water polygons, etc.) along with the geographical coordinates.
In this document, I will often take examples from the cvx2815.bgl file since that is the one I used to figure out the structure of the cxv file and it describes the area I live in. This file is located in: (....)\Microsoft Flight Simulator X\Scenery\0301\scenery.
BGL Common Format
All BGL files share the same generic format. A BGL file is made of a header, sections, subsections and subsection data. Subsections are children of sections. The sections are here to help us locate the subsections in the file. Data specific information is contained in the subsections data and their format is dependent on the section type.
A BGL file always start with a header (Size = 0x38 bytes), followed by a list of section pointers. The number of section pointers is defined in the header.
File Header
The header consists of 0x38 (56) bytes. It contains the number of sections defined in the file as well as the bounding geographical coordinates of the covered squared area.
| Offset | Number of bytes | Description |
|---|---|---|
| 0x00 | 4 - DWORD | Magic Number #1 – Must be 0x01, 0x02, 0x92, 0x19 |
| 0x04 | 4 - DWORD | Header size : 0x38 |
| 0x08 | 4 - DWORD | Unknown – Maybe a timestamp |
| 0x0C | 4 - DWORD | Unknown – Maybe a timestamp |
| 0x10 | 4 - DWORD | Magic Number #2 – Must be 0x03, 0x18, 0x05, 0x08 Maybe to identify the FS version |
| 0x14 | 4 - DWORD | The number of sections following this header. |
| 0x18 | 32 | Array[8] of unsigned integers (DWORD). Each value describes the bounding coordinates of a squared subarea. Even if 8 slots are provided, it is not necessary to have all 8 values filled. The list stops at the first null (0x00000000) value. |
Example
For example, in cvx2815.bgl :
| Offset | Values | Description |
|---|---|---|
| 0x00 | 01 02 92 19 |
Magic Number #1 |
| 0x04 | 38 00 00 00 |
Header size |
| 0x08 | EF 82 DF E2 |
|
| 0x0C | E8 C7 C6 01 |
|
| 0x10 | 03 18 15 08 |
Magic Number #2 |
| 0x14 | 01 00 00 00 |
1 section following this header |
| 0x18 | E8 07 02 00 |
MinLatitude(Deg) = 46.40625 MaxLatitude(Deg) = 47.8125 MinLongitude(Deg) = -75.0 MaxLongitude(Deg) = -73.125 |
| 0x1C | E9 07 02 00 |
MinLatitude(Deg) = 46.40625 MaxLatitude(Deg) = 47.8125 MinLongitude(Deg) = - 73.125 MaxLongitude(Deg) = -71.25 |
| 0x20 | EA 07 02 00 |
MinLatitude(Deg) = 45.0 MaxLatitude(Deg) = 46.40625 MinLongitude(Deg) = -75.0 MaxLongitude(Deg) = -73.125 |
| 0x24 | EB 07 02 00 |
MinLatitude(Deg) = 45.0 MaxLatitude(Deg) = 46.40625 MinLongitude(Deg) = -73.124 MaxLongitude(Deg) = -71.25 |
| 0x28 | 00 00 00 00 |
|
| 0x2C | 00 00 00 00 |
|
| 0x30 | 00 00 00 00 |
|
| 0x34 | 00 00 00 00 |
You’ll notice that only 4 subareas are defined (on a possibility of 8) and the last 4 available slots are empty (Value = 0)
So the bounding coordinates of the area covered by cvx2815.bgl are:
- MinLatitude(Deg) = 45.0
- MaxLatitude(Deg) = 47.8125
- MinLongitude(Deg) = -75.0
- MaxLongitude(Deg) = -71.25
Sections
Following the header, at offset 0x38, there are as many sections as defined at offset 0x14 of the header. Each section has a size of 20 bytes and has the following structure:
| Relative Offset | Number of bytes | Description |
|---|---|---|
| 0x00 | 4 - DWORD | Section type: one of the following values:
|
| 0x04 | 4 - DWORD | Unknown |
| 0x08 | 4 - DWORD | Number of subsections in the section. |
| 0x0C | 4 - DWORD | File offset = position in the file where the first subsection starts. |
| 0x10 | 4 - DWORD | Total Size (in bytes) of all the subsections. This value should be equal to : 16 * nbSubSections |
Note that a cvx*.bgl file have at least a section of type 101 (0x65) = TerrainVectorDb.
Example
For example, in cvx2815.bgl, the only one section, at offset 0x38, has this:
| Offset | Values | Description |
|---|---|---|
| 0x38 | 65 00 00 00 |
TerrainVectorDb = 101 = 0x65 |
| 0x3C | 01 00 00 00 |
Unknown |
| 0x40 | 8D 07 00 00 |
0x078D = 1933 subsections in the section |
| 0x44 | 01 CD 1F 00 |
0x1FCD01 = File offset = position in the file where the first subsection starts. |
| 0x48 | D0 78 00 00 |
Size (in bytes) =0x78D0 = 30928 bytes |
Subsections
A subsection contains information about the geographical area it covers. If also contains the file offset of the subsection’s data. All subsections of a same section are contiguous meaning that they are following each other in the file.
A subsection has the following structure:
| Relative Offset | Number of bytes | Description |
|---|---|---|
| 0x00 | 4 - DWORD | Bounding coordinates of a squared area covered by this subsection. See Annexe A. |
| 0x04 | 4 - DWORD | Number of records (for RIFF subsections ?) |
| 0x08 | 4 - DWORD | File Offset = Position in the file of the subsection’s data |
| 0x0C | 4 - DWORD | Size of the subsection’s data |
The interpretation of the section data depends on the section type.
Example
| Offset | Values | Description |
|---|---|---|
| 0x1FCD01 | 00 FA 81 00 |
Bounding coordinates:
|
| 0x1FCD05 | 00 00 00 00 |
Number of records = 0 |
| 0x1FCD09 | 4C 00 00 00 |
0x4C = File Offset = Position in the file of the subsection’s data. |
| 0x1FCD0D | DD 00 00 00 |
Size of the subsection’s data = 0xDD = 221 bytes. |
Subsection for section of type Terrain_Vector_DB
This type of subsection contains values to retrieve geographical coordinates, organized as segments. These coordinates are used to draw lines (roads, railways, etc.) or polygons (lakes, airport bounds, etc.).
The Bgl file does not contain geographical coordinates per se but offsets to the lower left corner of the covered area (minimum latitude / longitude). The covered area is also defined in the subsection.
The data is organized in lists of pair values (One pair per geographical coordinate). Each pair can then be used to compute the final geographical coordinates. There are 3 ways to retrieve these lists of pairs.
The first value in a pair is longitude-related. The second value is latitude-related.
The algorithm to compute the geographical coordinates from a pair is:
void convertToCoordinates (double longitude_related_Value, double latitude_related_Value)
{
var deltaLongFactor = (MaxLongitudeDeg - MinLongitudeDeg) * 0.000030517578125;
var deltaLatFactor = (MaxLatitudeDeg - MinLatitudeDeg) * 0.000030517578125;
var LongitudeDeg = MinLongitudeDeg + (longitude_related_Value * deltaLongFactor);
var LatitudeDeg = MinLongitudeDeg + (latitude_related_Value * deltaLatFactor);
}