BGL File Format
Introduction
The information contained in this wiki comes from different sources:
- "FSX File structure" by Winfried Orthmann
- This is the first document about BGL file format. It describes the generic format of a BGL file and the content of some important sections like Airport, Scenery Objects,etc.
- "FS9 FSX_BGL_Format.doc" by Jon Masterson (a.k.a. ScruffyDuck)
- Some reverse engineering work on the TmfViewer and BglComp applications by Patrick Germain
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 is really a big container where all kind of information can be stored (in the subsection).The meaning of the data contained in the subsections is known only by the application using it. Th only contraint is for the file to comply to the generic format described below.
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 | dwLowDateTime of the FILETIME structure. Date and Time the file was created The FILETIME structure represents the number of 100-nanosecond intervals since January 1, 1601 |
| 0x0C | 4 - DWORD | dwHighDateTime of the FILETIME structure |
| 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 |
Low = 3806298863 |
| 0x0C | E8 C7 C6 01 |
High = 29804520 => February 27, 2007 |
| 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. A same file may contain sections of different type. Each section has a size of 20 bytes and has the following structure:
| Relative Offset | Number of bytes | Description |
|---|---|---|
| 0x00 | 4 - DWORD | Section type (as defined by Microsoft Flight Simulator): 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 |
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 Computing the bounding coordinates from a DWORD value. |
| 0x04 | 4 - DWORD | Number of records contained in the subsection's data. It is 0 for TerrainVectorDb (which actually means only 1 record). |
| 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
In CVX2815.bgl:
| 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. |
AIRPORT
Each airport record consists of a fixed part with the length of 0x38 bytes, followed by a variable part with 0..n subrecords of different types. The structure of the fixed part is as follows
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x003C) | |
| 0x02 | DWORD | Size of the airport record | |
| 0x06 | BYTE | Number of RUNWAY subrecords | |
| 0x07 | BYTE | Number of COM subrecords | |
| 0x08 | BYTE | Number of START subrecords | |
| 0x09 | BYTE | Number of APPROACH subrecords (?) | |
| 0x0A | BYTE | Bit 0-6 : Number of aprons (?) Bit 7: flag for deleteAirport record | |
| 0x0B | BYTE | Number of HELIPAD subrecords | |
| 0x0C | DWORD | Longitude | |
| 0x10 | DWORD | Latitude | |
| 0x14 | DWORD | Altitude (in meters) | |
| 0x18 | DWORD | Tower Longitude (if present) | |
| 0x1C | DWORD | Tower Latitude (if present) | |
| 0x20 | DWORD | Tower Altitude (in meters) | |
| 0x24 | FLOAT | Magnetic Variation (Deg) | |
| 0x28 | DWORD | ICAO Ident (Special Format) | |
| 0x2C | DWORD | Region Ident (Special Format) | |
| 0x30 | DWORD | Unknown | |
| 0x34 | BYTE | Unknown | |
| 0x35 | BYTE | INT(Traffic Scalar * 255) | |
| 0x36 | WORD | Unknown |
The following subrecords can be present after the main airport record:
Name
This subrecord seems to be present in every airport record, and it is always the first one immediately after the fixed part.
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0019 ) | |
| 0x02 | DWORD | Size of the Name subrecord | |
| 0x06 | STRING | AirportName (padded with NUL (0x000 bytes) |
Included Tower Scenery Object
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0066 ) | |
| 0x02 | DWORD | Size of the subrecord | |
| 0x06 | DWORD | Size of the included scenery object |
After this record we find an included scenery object with an internal structure identical to that of other scenery objects (see below) and including possible attachments. The BglComp compiler allows only one scenery object to be included at this point, but in some FS X scenery files we find more than one objects included here. If present, the subrecords of this type appear immediately after the Name subrecord.
Runway
The runway subrecord consists of a fixed part with a length of 0x34 bytes and a variable number of sub-subrecords. The fixed part has the following structure:
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0004 ) | |
| 0x02 | DWORD | Size of the runway subrecord | |
| 0x06 | WORD | Type of surface:
| |
| 0x08 | BYTE | Primary runway number (01 - 36) | |
| 0x09 | BYTE | Primary runway designator:
| |
| 0x0A | BYTE | Secondary runway number | |
| 0x0B | BYTE | Secondary runway designator | |
| 0x0C | DWORD | ICAO Ident. for primary ILS (Special Format), 0x0000 if none. | |
| 0x10 | DWORD | ICAO Ident. for seconday ILS (Special Format) | |
| 0x14 | DWORD | Longitude | |
| 0x18 | DWORD | Latitude | |
| 0x1C | DWORD | Elevation (in millimeters) | |
| 0x20 | FLOAT | Length (in meters) | |
| 0x24 | FLOAT | Width (in meters) | |
| 0x28 | FLOAT | Heading (Deg) | |
| 0x2C | FLOAT | Pattern Altitude (in meters) | |
| 0x30 | WORD | Marking flags: BIT 0: edges | |
| 0x32 | BYTE | Lights flags: BIT 0-1: edge
BIT 2-3: center (as with edge) | |
| 0x33 | BYTE | Pattern flags: BIT 0: primaryTakeoff (0 = YES) |
The following sub-subreports can be present within a runway subrecord:
OffsetThreshold
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id Primary (0x0005 ) Id Secondary (0x0006 ) | |
| 0x02 | DWORD | Size of sub-subrecord (0x10) | |
| 0x06 | WORD | Surface (same as in runway) | |
| 0x08 | FLOAT | Length in meters | |
| 0x0C | FLOAT | Width in meters |
BlastPad
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id Primary (0x0007 ) Id Secondary (0x0008 ) | |
| 0x02 | DWORD | Size of sub-subrecord (0x10) | |
| 0x06 | WORD | Surface (same as in runway) | |
| 0x08 | FLOAT | Length in meters | |
| 0x0C | FLOAT | Width in meters |
Overrun
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id Primary (0x0009 ) Id Secondary (0x000A ) | |
| 0x02 | DWORD | Size of sub-subrecord (0x10) | |
| 0x06 | WORD | Surface (same as in runway) | |
| 0x08 | FLOAT | Length in meters | |
| 0x0C | FLOAT | Width in meters |
Vasi
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id Primary Left (0x000B )
Id Primary Right (0x000C ) | |
| 0x02 | DWORD | Size of sub-subrecord (0x18) | |
| 0x06 | WORD | Type:
| |
| 0x08 | FLOAT | BiasX | |
| 0x0C | FLOAT | BiasZ | |
| 0x10 | FLOAT | Spacing | |
| 0x14 | FLOAT | Pitch |
Approach Lights
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id Primary (0x000F ) Id Secondary (0x0010 ) | |
| 0x02 | DWORD | Size of sub-subrecord (0x08) | |
| 0x06 | BYTE | Bits [0-5]: System
Bit 5: Endlights (0 = false, 1 = true) | |
| 0x07 | BYTE | Number of strobes |
Helipad
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0026 ) | |
| 0x02 | DWORD | Size of subrecord (0x24) | |
| 0x06 | BYTE | Surface (as in Runway) | |
| 0x07 | BYTE | bit 0-3: Type
bit 4: transparent | |
| 0x08 | BYTE[4] | color (cannot be set with bglcomp because of error kin compiler) | |
| 0x0C | DWORD | Longitude | |
| 0x10 | DWORD | Latitude | |
| 0x14 | DWORD | Altitude * 1000 | |
| 0x18 | FLOAT | Length | |
| 0x1C | FLOAT | Width | |
| 0x18 | FLOAT | Heading |
Start
(the keywords “Start” and “RunwayStart” produce identical subrecords)
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0011) | |
| 0x02 | DWORD | Size of subrecord (0x18) | |
| 0x06 | BYTE | Runway Number | |
| 0x07 | BYTE | bit 0-3: Runway Designator (as with runway subrecord) bit 4-7: Start Type
| |
| 0x08 | DWORD | Longitude | |
| 0x0C | DWORD | Latitude | |
| 0x10 | DWORD | Elevation (in millimeters) | |
| 0x14 | FLOAT | Heading |
Com
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0012) | |
| 0x02 | DWORD | Size of subrecord: variable | |
| 0x06 | WORD | Type. The following numbers have been identified:
| |
| 0x08 | DWORD | Frequency x 1000000 | |
| 0x0C | STRINGZ | Name. Maximum Length = 48 (0x30) |
Delete Airport
The DeleteAirport subrecord has a fixed and a variable part. The fixed part has the following structure:
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0033) | |
| 0x02 | DWORD | Size of subrecord: variable | |
| 0x06 | WORD | delete flags
| |
| 0x08 | BYTE | Number of individual runways to delete | |
| 0x09 | BYTE | Number of individual starts to delete | |
| 0x0A | BYTE | Number of individual frequencies to delete | |
| 0x0B | BYTE | Unused (?) |
according to the number of individual features to delete there are the following parts of the record added:
for Runways
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | BYTE | Surface (as in runway subrecord) | |
| 0x01 | BYTE | Runway number primary | |
| 0x02 | BYTE | Runway number secondary | |
| 0x03 | BYTE | bit 0-3: Runway designator primary bit 4-7: Runway designator secondary |
for Starts
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | BYTE | Runway number | |
| 0x01 | BYTE | Runway designator | |
| 0x02 | BYTE | Type of start
| |
| 0x03 | BYTE | Unused (?) |
for Frequencies
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | DWORD | bit 28-31: type (as with COM records) bit 0-27: frequency * 1000000 |
Apron
There are 2 subrecords for each apron which follow each other. Both have variable length.
First record
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0037) | |
| 0x02 | DWORD | Size | |
| 0x06 | BYTE | surface (as with runway subrecord) | |
| 0x07 | WORD | Number of vertices | |
| and then for each vertex: | |||
| DWORD | Longitude | ||
| DWORD | Latitude | ||
| and then | |||
| zero-fill to next DWORD boundary | |||
Second record
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0030) | |
| 0x02 | DWORD | Size | |
| 0x06 | BYTE | surface (as with runway subrecord) | |
| 0x07 | BYTE | flags:
| |
| 0x08 | WORD | Number of vertices | |
| 0x0A | WORD | Number of triangles to draw | |
| and then for each vertex: | |||
| DWORD | Longitude | ||
| DWORD | Latitude | ||
| and then for each triangle to draw: | |||
| WORD | Index of first point | ||
| WORD | Index of second point | ||
| WORD | Index of third point | ||
Apron Edge Lights
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0031) | |
| 0x02 | DWORD | Size | |
| 0x06 | WORD | Unknown | |
| 0x08 | WORD | Number of vertices | |
| 0x0A | WORD | Number of edges | |
| 0x0C | DWORD | 0xFF0000FF: Unknown, probably color of lights | |
| 0x10 | FLOAT | 0x3F800000: Unknown (value 1) | |
| 0x14 | FLOAT | 0x44480000: Unknown (value 800) | |
| and then for each vertex | |||
| DWORD | Longitude | ||
| DWORD | Latitude | ||
| and then for each edge | |||
| FLOAT | Unknown (value 60.96) | ||
| WORD | Index of start vertex | ||
| WORD | Index of end vertex | ||
Taxiway Point
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x001A) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | WORD | Number of taxiway points present | |
| and for each taxipoint: | |||
| BYTE | Type
| ||
| BYTE | Flag
| ||
| WORD | Unknown | ||
| DWORD | Longitude | ||
| DWORD | Latitude | ||
Taxiway Parking
This record type has a short fixed part for all TaxiwayParking records together and a longer variable part with sections for each TaxiwayParking. The fixed part is 8 bytes long:
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x003D) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | WORD | Number of taxiway parking records present |
The record sections for each TaxiwayParking are again of variable length, depending on the number of airlineCodes present:
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | DWORD | bit 31-24: count of airlineCodes present bit 23-12: number
bit 7-6: pushback
bit 5-0: name
| |
| 0x04 | FLOAT | Radius | |
| 0x08 | FLOAT | Heading | |
| 0x0C | FLOAT | teeOffset1 | |
| 0x10 | FLOAT | teeOffset1 | |
| 0x14 | FLOAT | teeOffset3 | |
| 0x18 | FLOAT | teeOffset4 | |
| 0x1C | DWORD | Longitude | |
| 0x20 | DWORD | Latitude | |
| ... | STRING | Airline Designator (0..n times repeated) |
Taxiway Path
This record has a fixed length of 8 bytes and a variable part with records for each path. It has the following structure:
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x001C) | |
| 0x02 | DWORD | Size | |
| 0x06 | WORD | Number of paths defined | |
| and for each path: | |||
| 0x00 | WORD | index of start point. For type TAXI, the index of the start and of the end must both refer to a TaxiPoint. | |
| 0x02 | WORD | Bit 0-11: index of end point Bit 12-15: runway designator | |
| 0x04 | BYTE | Type
| |
| 0x05 | BYTE | runway number / index into TaxiName | |
| 0x06 | BYTE | bitfield
BIT 0: centerline
| |
| 0x07 | BYTE | Surface | |
| 0x08 | FLOAT | Width | |
| 0x0C | FLOAT | Weight Limit | |
| 0x10 | DWORD | ?? | |
TaxiName
This record has variable length, it consist of 8 bytes as a fixed part and then 8 bytes for each Name.
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x001D) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | WORD | Number of name entries | |
| and for each name: | |||
| STRING | Taxiname |
TaxiwaySign
These records are coded in the section for scenery objects (0x25) with a separate type of entry. Apparently all Taxiway signs for one airport are coded together on one record. There seems to be no coordination of this record with the airport record to which it belongs!
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x000E) 0x05 for FS9 | |
| 0x02 | WORD | Size: variable | |
| 0x04 | DWORD | Longitude | |
| 0x08 | DWORD | Latitude | |
| 0x0C | DWORD | Altitude (?) cannot be coded with the compiler | |
| 0x10 | WORD | 0x0001: AltitudeIsAGL cannot be coded | |
| 0x12 | WORD | Pitch(?) - Cannot be coded | |
| 0x14 | WORD | Bank(?) - Cannot be coded | |
| 0x16 | WORD | Heading(?) - Cannot be coded | |
| 0x18 | WORD | ImageComplexity (?) - Cannot be coded | |
| 0x1A | WORD | Unknown | |
| 0x1C | WORD | Number of taxiway signs for this airport | |
| and then for each sign: | |||
| 0x00 | FLOAT | Longitude offset from value in main record | |
| 0x04 | FLOAT | Latitude offset from value in main record | |
| 0x08 | WORD | Heading as coded | |
| 0x0A | BYTE | Size (SIZE1 .. SIZE5) | |
| 0x0B | BYTE | Justification (1 = right, 2 = left) | |
| 0x0C | STRINGZ | Label (zero filled to next WORD address) | |
Jetway
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x003A) 0x05 for FS9 | |
| 0x04 | WORD | Size: variable | |
| 0x06 | WORD | Parking number (refers to an existing parking) | |
| 0x08 | WORD | Gate Name | |
| 0x0A | DWORD | Size of the scenery object data to follow (0x40) | |
| 0x0E | LibraryObject record |
Approach
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0024) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | BYTE | Suffix | |
| 0x07 | BYTE | Runway Number | |
| 0x08 | BYTE | bit 0-3: Type
bit 4-6: Runway designator | |
| 0x09 | BYTE | Number of transitions (?) | |
| 0x0A | BYTE | Number of approach legs | |
| 0x0B | BYTE | Number of missedApproach legs ? | |
| 0x0C | DWORD | fixIdent
BIT 0-4: fixType
BIT 5-31: fixIdent | |
| 0x10 | DWORD | bit 0-10: fixRegion bit 11-31: ICAO Id of relevant airport | |
| 0x14 | FLOAT | Altitude | |
| 0x18 | FLOAT | Heading | |
| 0x1C | FLOAT | missedAltitude |
after this the following record can occur:
ApproachLegs
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x002D) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | WORD | Number of legs to follow |
each leg is a structure with a fixed length of 44 bytes
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | BYTE | ID of the leg types found:
| |
| 0x01 | BYTE | Altitude Descriptor
| |
| 0x02 | WORD | Flags
| |
| 0x04 | DWORD | bit 5-31: fixIdent bit 0-4: fixType | |
| 0x08 | DWORD | bit 0-10: fixRegion bit 11-32: ICAO Id of relevant airport | |
| 0x0C | DWORD | bit 5-31: recommendedIdent bit 0-4: recommendedType | |
| 0x10 | DWORD | Recommended region | |
| 0x14 | FLOAT | Theta | |
| 0x18 | FLOAT | Rho | |
| 0x1C | FLOAT | trueCourse / magneticCourse (depending on flag) | |
| 0x20 | FLOAT | Distance / Time | |
| 0x24 | FLOAT | Altitude 1 | |
| 0x28 | FLOAT | Altitude 2 |
MissedApproachLegs
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x002E) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | WORD | Number of legs to follow |
Transition
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x002C) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | BYTE | transitionType 1 = FULL, 2 = DME | |
| 0x07 | BYTE | Number of TransitionLegs (?) | |
| 0x08 | DWORD | bit 0-4: fixType
bit 5-31: fixIdent (Special Format) | |
| 0x0C | DWORD | bit 0-10: fixRegion
bit 11-31 : airportID of relevant airport | |
| 0x10 | FLOAT | Altitude | |
| If transitionType = DME and DmeArc record exists, then the following 16 bytes are present | |||
| 0x14 | DWORD | dmeIndent | |
| 0x18 | DWORD | bit 0-10: dmeRegion
bit 11-31: airportID of relevant airport | |
| 0x1C | DWORD | Radial | |
| 0x20 | FLOAT | Distance |
TransitionLegs
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x002F) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | WORD | Number of legs to follow. |
WayPoint
The waypoint record can be part of the Airport group or can be entered independently. In both cases the output for the BGL is the same but for the DWORD at offset 0x18.
| Offset | Number of bytes |
Format | Description |
|---|---|---|---|
| 0x00 | WORD | Id (0x0022) | |
| 0x02 | DWORD | Size: variable | |
| 0x06 | BYTE | Type
| |
| 0x07 | BYTE | Number of Route entries to follow | |
| 0x08 | DWORD | Longitude | |
| 0x0C | DWORD | Latitude | |
| 0x10 | FLOAT | Magnetic Variation | |
| 0x14 | DWORD | WayPoint Ident (Special Format) | |
| 0x18 | DWORD | bit 0-10: waypointRegion (Special Format)
bit 11-31: ICAO ident of the relevant airport, if it is a terminal waypoint, defined within an airport record. | |
| Optional, if Route is given | |||
| 0x1C | BYTE | Route type
| |
| 0x1D | char[8] | Name (zero padded), name cannot be longer than 8 characters | |
| for Next: | |||
| 0x25 | DWORD | BIT 0-2: Type
BIT 5-31: waypointIdent (Special Format) | |
| 0x29 | DWORD | Bit 0-10 waypointRegion (Special Format)
Bit 11-31 airportId if terminal waypoint | |
| 0x2D | FLOAT | Altitude Minimum | |
| for Previous: | |||
| 0x31 | DWORD | BIT 0-2: Type
BIT 5-31: waypointIdent (Special Format) | |
| 0x35 | DWORD | Bit 0-10 waypointRegion (Special Format)
Bit 11-31 airportId if terminal waypoint | |
| 0x39 | FLOAT | Altitude Minimum | |
Note: it is not necessary for any route to have both previous and next defined, in that case the fields for this part of the record are all zero.
AirportSummary
The record has a fixed size of 44 bytes.
| Offset | Number of bytes |
Format | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0x00 | WORD | Id (0x0013)
-}
ILS/VORThe records for ILS and VOR are in the same section and they are identical for the fixed section. ILS records can have an additional subrecords. The fixed part is 40 bytes long and has the following structure:
The following subrecords can follow: Localizer(For ILS)
GlideSlopeFor (ILS)
DmeFor (ILS/VOR)
After these subsections, a name subsection is added: Name
If VisualModel is added in the source file, the compiler adds another section to the file with a record of type 0x0025 (SceneryxObject) with the GUID for the object referenced.
The coordinates for this objects are taken from the ILS/VOR and adjusted, if BiasXYZ is added to the VisualModel.
NDBThe NDB records are stored in a separate section. The have a 40 bytes long fixed section and a name section of variable length. The fixed section has the following structure:
The name subsection has the following structure
SCENERYOBJECTLibraryObjectThe record has a fixed length of 0x40 bytes with the following structure:
If an AttachedObject exists, there are 3 other records following: AttachedObject
Effect
GenericBuildingNB.: BuildingBias is not implemented in the compiler.
WindsockRecord with fixed length of 46 bytes.
Extrusion Bridge
TriggerThe record consists of a fixed part and a variable part. The fixed part is 34 bytes long and has the following structure:
MARKERThe Marker record has a fixed length of 28 bytes with the following structure:
The structure below is taken from the source code of BGLXMl.
BOUNDARY
on this follows a record describing the drawing of the lines
GEOPOL
MODEL DATAThe model data structure has a fixed length of 24 bytes.
EXCLUSIONRECTANGLEThis record has a fixed length record of 20 bytes.
NAMELISTThe namelist contains only one record of variable length. It consists of a fixed part and a variable part. The fixed part is 42 bytes long and has the following structure:
The lists for region, country, state, city and airport names have all the same structure: an index with 1 DWORD for each entry in the list, containing the offset of the nth name from the beginning of the names part (i.e. after the index) followed by the names in form of zero-terminated strings The ICAO list has a different structure. It contains n entries (one for each ICAO name), each of them 20 bytes long, with the following structure:
TERRAIN_VECTOR_DBThis 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.).
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);
}
Subsection HeaderEach subsection starts with the following structure:
Entity StructureThen for each entity (the number of entities is defined at relative offset 0x0C of the subsection header (see Subsection Header above), we have the following structure:
Segment StructureThen for each segment (the number of segments is defined at relative offset 0x00 of the entity structure (see Entity Structure above), we have the following structure:
Method 1By far the most complex method. I have some code for it. But I still have to figure out the big picture.
Method 2The data buffer for this method has the following structure:
AlgorithmThe algorithm used by Method2 to build the list of pairs is:
/*
Fills an array listOfValues of DWORD
*/
PositionInPair = 0; /* 0 = first value in pair, 1 = second value in pair */
PairIndex = 0 ;
ShiftValue = 0 ;
NbBytesLeftToRead = ((rootMask * nbPoints) * 2 + 7) >> 3;
Mask = 1 << rootMask ; /* 2 ^ rootMask */
/* Read first value */
if (NbBytesLeftToRead > 3)
{
valueFromFile = read (4 bytes)
NbBytesLeftToRead -= 4 ;
}
else
{
valueFromFile = read (NbBytesLeftToRead bytes)
NbBytesLeftToRead = 0 ;
}
while (PairIndex < nbPoints)
{
if (ShiftValue < 0)
{
/* Shift Left */
result = (uint)((valueFromFile << (-ShiftValue)) & Mask);
/* Add to existing pair value */
result += listOfValues[PairIndex * 2 + PositionInPair];
listOfValues[PairIndex * 2 + PositionInPair] = result;
}
else
{
/* Shift Right */
result = (uint)((valueFromFile >> ShiftValue) & Mask);
listOfValues[PairIndex * 2 + PositionInPair] = result;
}
if (rootMask + ShiftValue >= 32)
{
if (NbBytesLeftToRead > 3)
{
valueFromFile = read (4 bytes)
NbBytesLeftToRead -= 4 ;
}
else
{
valueFromFile = read (NbBytesLeftToRead bytes)
NbBytesLeftToRead = 0 ;
}
ShiftValue -= 32; /* now negative */
}
else
{
/* continue processing value from file */
ShiftValue += rootMask;
PositionInPair++;
}
if (PositionInPair == 2)
{
PairIndex++; /* Next entry in the list of pair values */
PositionInPair = 0;
}
}
ExampleStill with the same example as in Subsections, we know that the first subsection data starts at offset 0x4C (as described in the first subsection starting at offset 0x1FCD01) . So at this file offset, we have the subsection header of the first subsection data.
Then follows 3 entities.
This entity is followed by only one segment, at offset 0x8E of the file
The next entity starts at file offset 0xCA.
Mask = 0x7FFF ; NbBytesLeftToRead = 53 ; shiftValue = 0; PairIndex = 0 ; PositionInPair = 0 valueFromFile = read (4 bytes) = 0x37563E84 result = (valueFromFile >> shiftValue) & mask = 0x3E84 listOfValues[0] = 0x3E84 NbBytesLeftToRead = 49 ; shiftValue = 0x0F; PairIndex = 0 ; PositionInPair = 1 result = (valueFromFile >> shiftValue) & mask = 0x6EAC listOfValues[1] = 0x6EAC NbBytesLeftToRead = 49 ; shiftValue = 0x1E; PairIndex = 1 ; PositionInPair = 0 result = (valueFromFile >> shiftValue) & mask = 0x000 listOfValues[2] = 0x0000 rootMask + shiftValue = 0x2D > 0x20 so valueFromFile = read (4 bytes) = 0x1D741065 shiftValue = 0xFFFFFFFE NbBytesLeftToRead = 45 ; shiftValue = 0xFFFFFFFE; PairIndex = 1 ; PositionInPair = 0 result = (valueFromFile << (-shiftValue)) & mask = 0x4194 result += listOfValues[2] = 0x4194 + 0x0000 = 0x4194 listOfValues[2] = 0x4194 NbBytesLeftToRead = 45 ; shiftValue = 0x0D; PairIndex = 1 ; PositionInPair = 1 result = (valueFromFile >> shiftValue) & mask = 0x6BA0 listOfValues[3] = 0x6BA0 NbBytesLeftToRead = 45 ; shiftValue = 0x1C; PairIndex = 2 ; PositionInPair = 0 result = (valueFromFile >> shiftValue) & mask = 0x0001 listOfValues[4] = 0x0001 rootMask + shiftValue = 0x2BD > 0x20 so valueFromFile = read (4 bytes) = 0x9F5B443C shiftValue = 0xFFFFFFFC NbBytesLeftToRead = 41 ; shiftValue = 0xFFFFFFFC; PairIndex = 2 ; PositionInPair = 0 result = (valueFromFile << (-shiftValue)) & mask = 0x43C0 result += listOfValues[4] = 0x0x43C0 + 0x0001 = 0x43C1 listOfValues[4] = 0x43C1 etc Once the 53 bytes have been processed, the list contains the following 28 values, making up a list of 14 pairs. 3E84 6EAC 4194 6BA0 43C1 6B68 4467 6862 4905 6659 4B57 69D5 58A8 73B1 59E6 76C5 5952 7966 582E 79A9 5545 76A4 4C0C 7136 4B64 6DD6 3E84 6EAC To get the coordinates of the first point, we take the first pair {0x3E84, 0x6EAC} and use the convertToCoordinates method described at Subsection for section of type Terrain_Vector_DB.
doubledeltaLongFactor = ((-74.765625) - (- 75.0)) * 0.000030517578125; // = 0.000007152557373046875 double deltaLatFactor = (47.8125 - 47.63671875) * 0.000030517578125; // = 0.0000053644180297851563 double LongitudeDeg = (- 75.0) + (0x3E84 * deltaLongFactor); // = -74.885530471801758 double LatitudeDeg = 47.63671875 + (0x6EAC * deltaLatFactor); // = 47.788703441619873 Annexe AComputing the bounding coordinates from a DWORD value
public static List<double> GetBoundingCoordinates(uint boundingValue)
{
var list = new List<double>();
var shiftValue = 15;
var work = boundingValue;
var latitudeData = (uint)0;
var longitudeData = (uint)0;
while (work < 0x80000000 && shiftValue >= 0)
{
shiftValue--;
work *= 4;
}
work &= 0x7FFFFFFF; // Remove negative flag, if any
var powerOfTwo = shiftValue;
while (shiftValue >= 0)
{
if (work >= 0x80000000)
{
latitudeData += (uint)(1 << shiftValue);
}
if ((work & 0x40000000) != 0)
{
longitudeData += (uint)(1 << shiftValue);
}
work *= 4;
shiftValue--;
}
// factor = 1.0 / (2^i)
var factor = 1.0 / (1 << powerOfTwo);
// Calc bounding coordinates
var minLatitudeDeg = 90.0 - ((latitudeData + 1.0) * factor * 360.0);
var maxLatitudeDeg = 90.0 - (latitudeData * factor * 360.0);
var minLongitude = (longitudeData * factor * 480.0) - 180.0;
var maxLongitude = ((longitudeData + 1.0) * factor * 480.0) - 180.0;
list.Add(minLatitudeDeg);
list.Add(maxLatitudeDeg);
list.Add(minLongitude);
list.Add(maxLongitude);
return list;
}
Computing Longitude and Latitude from a DWORD valueLatitude and longitude are no longer represented as before. Each location on the earth is fixed in the LOD grid. Longitude and latitude are each represented by a 4 byte value (DWORD). The formula for obtaining the decimal values is as follows: (double) Lon = (DWORD) Lon * (360.0 / (3 * 0x10000000) – 180.0) (double) Lat = 90.0 - (DWORD) Lat * (180.0 / (2 * 0x10000000)) Pitch, bank and headingPitch, bank and heading are given as ANGLE16 in form of a DWORD. The formula for obtaining the decimal value is as follows: (double) Pitch = (DWORD) Pitch * 360.0 / 0x10000 ICAO Identifiers and region codesICAO Identifiers and region codes are coded in a special format. Each number and letter has a value from 0 .. 37:
The code is calculated by starting from left: the value of the first digit/letter is multiplied by 38, then the value of the next digit/letter to the right is added, the sum s multiplied by 38, and as long as there are more digits/letters this process is repeated. The region codes have only 2 digits/letters and the result is used as such; for the ICAO identifiers for airports, ILS, VOR, NDB and waypoints there are up to 5 digits/letters, and the result is shifted left by 5 positions, i.e. multiplied by 0x20. Bits 0 .. 4 of the resulting DWORD are frequently used for other purposes. The ICAO identifiers for primary and secondary ILS in a runway record are not shifted. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
