Difference between revisions of "BGL File Format"
(→GEOPOL) |
(→Sections) |
||
Line 170: | Line 170: | ||
* NdbIcaoIndex = 0x29 | * NdbIcaoIndex = 0x29 | ||
* WaypointIcaoIndex = 0x2A | * WaypointIcaoIndex = 0x2A | ||
− | * ModelData = 02B | + | * [[#MODEL DATA|ModelData]] = 02B |
* AirportSummary = 0x2C | * AirportSummary = 0x2C | ||
* Exclusion = 0x2E | * Exclusion = 0x2E |
Revision as of 08:16, 1 September 2014
Contents
- 1 Introduction
- 2 BGL Common Format
- 3 AIRPORT
- 4 ILS/VOR
- 5 NDB
- 6 SCENERYOBJECT
- 7 MARKER
- 8 BOUNDARY
- 9 GEOPOL
- 10 MODEL DATA
- 11 TERRAIN_VECTOR_DB
- 12 Annexe A
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.
- Some reverse engineering work on the TmfViewer and BglComp applications (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) |
0x30 | |
DWORD | Region Ident (Special Format) |
0x34 | |
BYTE | Unknown |
0x35 | |
BYTE | Traffic Scalar |
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 | |
LONG | Elevation (in meters) |
0x20 | |
FLOAT | Length (in meters) |
0x24 | |
FLOAT | Width (in meters) |
0x28 | |
FLOAT | Heading (Deg) |
0x2C | |
FLOAT | Pattern Altitude |
0x30 | |
WORD | Marking flags: BIT 0: edges |
0x32 | |
BYTE | Light flags: BIT 0-1: edge (00 none, 01 low, 10 medium, 11 high) |
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 | System
|
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 |
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 |
0x0C | |
STRINGZ | Name |
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
this feature)
|
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) |
0x20 | |
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 (0x001B) |
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 | |
DWORD | Longitude |
0x10 | |
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 | after this follows a normal scenery object record starting with an ID of 0x0b.
This record refers to an appropriate scenery object like {BFCDF52B-9142-415C-8318-03C1B92CA9D9} |
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.
ILS/VOR
The 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:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x0013) |
0x02 | |
DWORD | Size |
0x06 | |
BYTE | Type. The following numbers have been found:
|
0x07 | |
BYTE | Flags. The following bits have been recognized:
|
0x08 | |
DWORD | Longitude |
0x0C | |
DWORD | Latitude |
0x10 | |
DWORD | Elevation |
0x14 | |
DWORD | Frequency |
0x18 | |
FLOAT | Range in meters |
0x1C | |
FLOAT | Magnetic Variation |
0x20 | |
DWORD | ICAO ident (Special Format) |
0x24 | |
DWORD | bit 0-10 regionId
bit 11-31 airportId (for ILS) |
The following subrecords can follow: (For ILS)
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id Localizer (0x0014) |
0x02 | |
DWORD | Size |
0x06 | |
WORD | Unknown |
0x08 | |
FLOAT | Heading |
0x0C | |
FLOAT | Width |
For (ILS)
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id GlideSlope(0x0015) |
0x02 | |
DWORD | Size (0x1C) |
0x06 | |
WORD | Unknown |
0x08 | |
DWORD | Longitude |
0x0C | |
DWORD | Latitude |
0x10 | |
DWORD | Elevation |
0x14 | |
FLOAT | Range |
0x18 | |
FLOAT | Pitch |
For (ILS/VOR)
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id DME(0x0016) |
0x02 | |
DWORD | Size (0x18) |
0x06 | |
WORD | Unknown |
0x08 | |
DWORD | Longitude |
0x0C | |
DWORD | Latitude |
0x10 | |
DWORD | Elevation |
0x14 | |
FLOAT | Range |
After these subsections, a name subsection is added:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x0019) |
0x02 | |
DWORD | Size |
0x06 | STRING | Name (max. 48 characters) |
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.
NDB
The 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:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x0017) |
0x02 | |
DWORD | Size: variable |
0x06 | |
WORD | Type
|
0x08 | |
DWORD | Frequency |
0x0C | |
DWORD | Longitude |
0x10 | |
DWORD | Latitude |
0x14 | |
DWORD | Elevation |
0x18 | |
FLOAT | Range |
0x1C | |
FLOAT | Magnetic Variation |
0x20 | |
DWORD | ICAO ident (Special Format) |
0x24 | |
DWORD | bit 0-10: region
bit 11-31: ICAO id of airport, if it was defined with an airport (terminal NDB) |
The name subsection has the following structure
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x0019) |
0x02 | |
DWORD | Size |
0x06 | STRING | Name |
SCENERYOBJECT
LibraryObject
The record has a fixed length of 0x40 bytes with the following structure:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x000B) |
0x02 | |
DWORD | Size (0x0040) |
0x04 | |
DWORD | Longitude |
0x08 | |
DWORD | Latitude |
0x0C | |
DWORD | Altitude |
0x10 | |
WORD | Flag: 1 = isAboveAGL |
0x12 | |
WORD | Pitch |
0x14 | |
WORD | Bank |
0x16 | |
WORD | Heading |
0x18 | |
WORD | imageComplexity
|
0x1A | |
WORD | Unknown |
0x1C | |
GUID | Instance Id |
0x2C | |
GUID | Name |
0x3C | |
FLOAT | Scale |
If an AttachedObject exists, there are 3 other records following:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x1002) |
0x02 | |
DWORD | Size (0x0004) |
and then the 2nd record | |||
0x00 | |
WORD | ID depending on the kind of attached object.
It is possible to attach beacons, effects and other library objects |
0x02 | |
WORD | Size |
0x04 | |
WORD | Offset of attach point string |
0x06 | |
WORD | Pitch |
0x08 | |
WORD | Bank |
0x0A | |
WORD | Heading |
0x0C | |
FLOAT | BiasX |
0x10 | |
FLOAT | BiasY |
0x14 | |
FLOAT | BiasZ |
0x18 | |
GUID | Instance Id |
0x28 | |
WORD | Probability |
0x2A | |
WORD | Randomness |
The following part of the record depends on the type of attached object and corresponds to the code of this type of object | |||
and then the 3rd record | |||
0x00 | |
WORD | Id (0x1003) |
0x02 | |
WORD | Size (?) (0x004) |
In theory, there can be several attachments with one library object (if an adequate number of attachment points exists)
Effect
The record has a fixed part of 108 byte and a variable part. The fixed part has the following structure:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x000D) |
0x02 | |
WORD | Size: variable |
0x04 | |
DWORD | Longitude |
0x08 | |
DWORD | Latitude |
0x0C | |
DWORD | Altitude |
0x10 | |
WORD | flag: 1 = isAboveAGL |
0x12 | |
WORD | Pitch |
0x14 | |
WORD | Bank |
0x16 | |
WORD | Heading |
0x18 | |
WORD | imageComplexity
|
0x1A | |
WORD | Unknown |
0x1C | |
GUID | Instance Id |
0x2C | |
FLOAT | Scale |
0x30 | |
WORD | type: 0x00a0 generic building |
0x32 | |
WORD | Size fo record |
0x34 | |
WORD | subtype. The following numbers have been identified:
|
for all rectangular buildings:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x36 | |
WORD | SizeX (0) |
0x38 | |
WORD | SizeZ (1) |
0x3A | |
WORD | bottomTexture (2) |
0x3C | |
WORD | sizeBottomY (3) |
0x3E | |
WORD | textureIndexBottomX (4) |
0x40 | |
WORD | textureIndexBottomZ (5) |
0x42 | |
WORD | windowTexture(6) |
0x44 | |
WORD | sizeWindowY(7) |
0x46 | |
WORD | textureIndexWindowX(8) |
0x48 | |
WORD | textureIndexWindowY(9) |
0x4A | |
WORD | textureIndexWindowZ(10) |
0x4C | |
WORD | topTexture (11) |
0x4E | |
WORD | sizeTopY (12) |
0x50 | |
WORD | textureIndexTopX (13) |
0x52 | |
WORD | textureIndexTopZ (14) |
0x54 | |
WORD | roofTexture (15) |
0x56 | |
WORD | textureIndexRoofX (16) |
0x58 | |
WORD | textureIndexRoofX (17) |
end for rectangular buildings with rooftype FLAT.
for rectangular buildings with roofType RIDGE or SLANTED:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x5A | |
WORD | sizeRoofY (18) |
0x5C | |
WORD | textureIndexGableY (19) |
0x5E | |
WORD | gableTexture (20) |
0x60 | |
WORD | textureIndexGableZ (21) |
for roofType SLANTED only:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x62 | |
WORD | faceTexture (22) |
0x64 | |
WORD | textureIndexFaceX (23) |
0x66 | |
WORD | textureIndexFaceX (24) |
for rectangular buildings with roofType PEAKED:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x5A | |
WORD | sizeRoofY(18) |
0x5C | |
WORD | textureIndexRoofY (19) |
for multisided buildings:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x38 | |
WORD | sizeX (1) |
0x3A | |
WORD | sizeZ (2) |
0x3C | |
WORD | bottomTexture (3) |
0x3E | |
WORD | sizeBottomY (4) |
0x40 | |
WORD | textureIndexBottomX (5) |
0x42 | |
WORD | windowTexture (6) |
0x44 | |
WORD | sizeWindowY (7) |
0x46 | |
WORD | textureIndexWindowX (8) |
0x48 | |
WORD | textureIndexWindowY (9) |
0x4A | |
WORD | topTexture (10) |
0x4C | |
WORD | sizeTopY (11) |
0x4E | |
WORD | textureIndexTopX (12) |
0x50 | |
WORD | roofTexture (13) |
0x52 | |
WORD | sizeRoofY (14) |
0x54 | |
WORD | textureIndexRoofX (15) |
0x56 | |
WORD | textureIndexRoofY (16) |
Note: textureIndexRoofY is required by the compiler, but it has no effect on the bgl file ! |
for pyramidal buildings:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x36 | |
WORD | sizeX (0) |
0x38 | |
WORD | sizeZ (1) |
0x3A | |
WORD | sizeTopX (2) |
0x3C | |
WORD | sizeTopZ (3) |
0x3E | |
WORD | bottomTexture (4) |
0x40 | |
WORD | sizeBottomY (5) |
0x42 | |
WORD | textureIndexBottomX (6) |
0x44 | |
WORD | textureIndexBottomZ (7) |
0x46 | |
WORD | windowTexture (8) |
0x48 | |
WORD | sizeWindowY (9) |
0x4A | |
WORD | textureIndexWindowX (10) |
0x4C | |
WORD | textureIndexWindowY (11) |
0x4E | |
WORD | textureIndexWindowZ (12) |
0x50 | |
WORD | topTexture (13) |
0x52 | |
WORD | sizeTopY (14) |
0x54 | |
WORD | textureIndexTopX (15) |
0x56 | |
WORD | textureIndexTopZ (16) |
0x58 | |
WORD | roofTexture (17) |
0x5A | |
WORD | textureIndexRoofX (18) |
0x5C | |
WORD | textureIndexRoofZ (19) |
Windsock
Record with fixed length of 46 bytes.
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x0C) |
0x02 | |
WORD | Size (0x3E) |
0x04 | |
DWORD | Longitude |
0x08 | |
DWORD | Latitude |
0x0C | |
LONG | Altitude |
0x10 | |
WORD | flags (unused) |
0x12 | |
WORD | Pitch |
0x14 | |
WORD | Bank |
0x16 | |
WORD | Heading |
0x18 | |
WORD | imageComplexity |
0x1A | |
WORD | Unknown |
0x1C | |
GUID | Instance Id |
0x2C | |
FLOAT | poleheight |
0x30 | |
FLOAT | sockLength |
0x34 | |
BYTE | PoleColor: blue |
0x35 | |
BYTE | PoleColor: green |
0x36 | |
BYTE | PoleColor: red |
0x37 | |
BYTE | PoleColor ? (0xFF) |
0x38 | |
BYTE[4] | SockColor |
0x3C | |
WORD | flag: lighted (TRUE = 0x0001) |
Extrusion Bridge
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x12) |
0x02 | |
WORD | Size |
0x04 | |
DWORD | Longitude |
0x08 | |
LONG | Latitude |
0x0C | |
DWORD | Altitude |
0x10 | |
WORD | Flags |
0x12 | |
WORD | Pitch |
0x14 | |
WORD | Bank |
0x16 | |
WORD | Heading |
0x18 | |
WORD | imageComplexity |
0x1A | |
WORD | Unknown |
0x1C | |
GUID | Instance Id |
0x2C | |
GUID | Profile |
0x3C | |
GUID | Material Set |
0x4C | |
DWORD[3] | altitude sample location 1 |
0x58 | |
DWORD[3] | altitude sample location 2 |
0x64 | |
FLOAT | road width |
0x68 | |
FLOAT | Probability |
0x6C | |
BYTE | Suppress |
0x6D | |
BYTE | Placement Count |
0x6E | |
WORD | Point count |
and then for each polyline object placement | |||
|
GUID | placement id | |
and then for each polyline point | |||
|
DWORD | Longitude | |
|
DWORD | Latitude | |
|
LONG | Elevation |
Trigger
The record consists of a fixed part and a variable part. The fixed part is 34 byte long and has the following structure:
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x10) |
0x02 | |
WORD | Size: variable |
0x04 | |
DWORD | Longitude |
0x08 | |
DWORD | Latitude |
0x0C | |
DWORD | Altitude |
0x10 | |
WORD | AltitudeIsAGL (0x0001 = TRUE) |
0x12 | |
WORD | Pitch |
0x14 | |
WORD | Bank |
0x16 | |
WORD | Heading |
0x18 | |
WORD | imageComplexity |
0x1A | |
WORD | Unknown |
0x1C | |
GUID | Instance Id |
0x2C | |
WORD | Type
|
0x2E | |
FLOAT | triggerHeight |
in case of WEATHER the variable part has the following structure | |||
0x32 | |
WORD | Type
|
0x34 | |
FLOAT | Heading |
0x38 | |
FLOAT | Scalar |
0x3C | |
DWORD | Number of vertices |
and then for each vertex: | |||
|
FLOAT | BiasX | |
|
FLOAT | BiasY | |
in case of FUEL_REPAIR the variable part has the following structure | |||
0x32 | |
DWORD | Fuel type and availability
8bit 24-25: type JET5 *bit 26-29 : unused *bit 30 : piston type 8bit 31 : jet type for all except last two :
when type=UNKNOWN and availability = YES then type=100 and type = JETA both are set to availability=YES |
0x036 | |
DWORD | Number of vertices |
and then for each vertex | |||
0x036 | |
FLOAT | BiasX |
0x036 | |
FLOAT | BiasZ |
MARKER
The Marker record has a fixed length of 28 bytes with the following structure:
NOTE: The structure given by Winfried appears to be wrong in the first three fields
- ID 2 bytes WORD
- Size 4 bytes DWORD
- Heading 1 byte BYTE
The structure below is taken from the source code of BGLXMl.
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x18) |
0x02 | |
WORD | Size (0x0000001C) |
0x04 | |
BYTE | Unknown |
0x05 | |
WORD | Heading |
0x07 | |
BYTE | Type
|
0x08 | |
DWORD | Longitude |
0x0C | |
DWORD | Latitude |
0x10 | |
DWORD | Altitude |
0x14 | |
DWORD | Ident (Special Format) |
0x18 | |
WORD | Region (Special Format) |
0x1A | |
WORD | Unknown (0x0000) |
BOUNDARY
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x20) |
0x00 | |
DWORD | Size: variable |
0x06 | |
BYTE | Type
|
0x07 | |
BYTE | BIT 0-3: maximumAltitudeType
BIT 4-7: minimumAltitudeType
|
0x08 | |
DWORD | Minimum Longitude of area covered |
0x12 | |
DWORD | Minimum Latitude of area covered |
0x16 | |
DWORD | Minimum Altitude * 1000 |
0x20 | |
DWORD | Maximum Latitude of area covered |
0x24 | |
DWORD | Maximum Latitude of area covered |
0x28 | |
DWORD | Maximum Altitude |
0x32 | |
WORD | Type field of name record (0x19) |
0x34 | |
DWORD | Size of name record |
0x36 | |
STRING | Name |
on this follows a record describing the drawing of the lines
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x21) |
0x02 | |
DWORD | size: variable |
0x06 | |
WORD | Number of points to follow |
for each point 10 bytes | |||
0x00 | |
WORD | Type of point
NB: in case of circle, the entries for minimumAltitude and maximumAltitude override
the values in start if both are given. |
0x02 | |
DWORD | Latitude of point (in case of circle: unknown, = 0x0000) |
0x06 | |
DWORD | Longitude of point (in case of circle: FLOAT: radius) |
GEOPOL
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id (0x23) |
0x02 | |
DWORD | Size: Variable |
0x06 | |
WORD | Bit 0-13: number of vertices number of vertices
Bit 14-15: type (0x40 = BOUNDARY, 0x80 = COASTLINE) |
0x08 | |
DWORD | Minimum Longitude |
0x0C | |
DWORD | Minimum Latitude |
0x10 | |
DWORD | Maximum Longitude |
0x14 | |
DWORD | Maximum Latitude |
variable part: for each vertex | |||
0x02 | |
DWORD | Longitude |
0x04 | |
DWORD | Latitude |
MODEL DATA
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD |
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 section 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); }
Subsection Header
Each subsection starts with the following structure:
Relative Offset | Number of bytes | Description |
---|---|---|
0x00 | 4 - DWORD | 6 I did not find a cvx file with a value other than 6. |
0x04 | 4 - DWORD | Bounding coordinates of a squared area covered by this subsection. See Computing the bounding coordinates from a DWORD value. |
0x08 | 4 - DWORD | 0 I did not find a cvx file with a value other than 0. |
0x0C | 4 - DWORD | Number of entities. An entity is a list of segments. (a segment is a list of points). |
0x10 | 4 - DWORD | Number of bytes in the signatures buffer. The signatures buffer contains some GUIDs identifying the segments of this subsection. Some GUID values (like the Texture) are not used by the TmfViewer application.
{359C73E8-06BE-4FB2-ABCB-EC942F7761D0} Airport Bounds {91CB4A9B-9398-48E6-81DA-70AEA3295914} Parks {EA0C44F7-01DE-4D10-97EB-FB5510EB7B72} Water Polygons (GPS) {956A42AD-EC8A-41BE-B7CB-C68B5FF1727E} Water Polygons {AC39CDCB-DB78-4628-9A7C-051DA7AC864A} Exclusions {0CBC8FAD-DF73-40A1-AD2B-FE62F8004F6F} Shorelines {714BF912-F9DF-467E-80AE-28EB27374DBD} Streams {C7ACE4AE-871D-4938-8BDC-BB29C4BBF4E3} Utilities {33239EB4-D2B8-46F5-98AB-47B3D0922E2A} Railways {560FA8E6-723D-407D-B730-AE08039102A5} Roads {54B91ED8-BC02-41B7-8C3B-2B8449FF85EC} Freeway Traffic Roads |
0x14 | 4 - DWORD | Maximum allowed number of signature offsets. Signature offsets (see Entity Structure below) are offset into the signature buffer and point to the GUID identifying the corresponding segment. |
0x18 | 4 - DWORD | Maximum number of pair values per segment for this subsection. = 2 * maximum number of points per segment |
0x1C | 4 - DWORD | 0 I did not find a cvx file with a value other than 0. |
0x20 | N | Signatures buffer containing <N> bytes, where N is defined at relative offset 0x10 |
Entity Structure
Then 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:
Relative Offset | Number of bytes | Description |
---|---|---|
0x00 | 4 - DWORD | Number of segments in the entity. |
0x04 | 4 - DWORD | Segment type:
|
0x08 | 2 - WORD | Number of signatures offsets (Must be < 0x64). |
0x0A | 4 - DWORD | N = Number of signatures offsets (see above). Will contain the offsets into the signature buffer. |
Segment Structure
Then 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:
Relative Offset | Number of bytes | Description |
---|---|---|
0x00 | 4 - DWORD | Number of points (geographical coordinates) in this segment. |
0x04 | 1 - BYTE | AddFlag : Determines how many additional bytes to read after the data buffer has been read. |
0x05 | 1 - BYTE | Method used to build the pairs list from the data buffer. Possible values are 1,2 or 3
|
0x06 | Data buffer – Size may vary depending on the method used. See below. | |
- | N | N depends on the AddFlag defined at relative offset 0x04.
|
Method 1
By far the most complex method. I have some code for it. But I still have to figure out the big picture.
The data buffer for this method has the following structure:
Relative offset | Number of bytes | Description |
---|---|---|
0x00 | 4 - DWORD | First Longitude Data - First value in the list of pairs. |
0x04 | 4 - DWORD | First Latitude Data - First value in the list of pairs. |
0x08 | 4 - DWORD | LongitudeData Increment |
0x0C | 4 - DWORD | LatitudeData Increment |
0x10 | 4 - DWORD | Number of bytes |
0x14 | N | N is the number of bytes defined above at relative offset 0x10. These N bytes are the raw data used to build the final list of pairs. |
Method 2
The data buffer for this method has the following structure:
Relative offset | Number of bytes | Description |
---|---|---|
0x00 | 1 - BYTE | Root Mask |
0x01 | N | N is computed using the value of the Root Mask. N = (RootMask * NbPoints * 2 + 7) >> 3 where
|
Algorithm
The 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; } }
Example
Still 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.
Offset | Value | Description |
---|---|---|
0x4C | 06 00 00 00 |
6 |
0x50 | 00 FA 81 00 |
Bounding coordinates:
|
0x54 | 00 00 00 00 |
0 |
0x58 | 03 00 00 00 |
Number of entities = 3 |
0x5C | 14 00 00 00 |
Number of bytes in the signatures buffer = 20 |
0x60 | 03 00 00 00 |
Maximum allowed number of signature offsets = 3 |
0x64 | 1C 00 00 00 |
Maximum number of pair values per segment = 28 |
0x68 | 00 00 00 00 |
0 |
0x6C | Signatures buffer containing 20 bytes.F7 44 0C EA DE 01 10 4D 97 EB FB 55 10 EB 7B 72 00 00 00 00 That is GUID {EA0C44F7-01DE-4D10-97EB-FB5510EB7B72} = Water Polygons (GPS), followed by 4 zeroes. |
Then follows 3 entities.
The first entity (at offset 0x80 of the file) is
Offset | Value | Description |
---|---|---|
0x80 | 01 00 00 00 |
Number of segments in the entity = 1 |
0x84 | 03 00 00 00 |
Segment type = 3 - Polygons |
0x88 | 01 00 |
Number of signatures offsets = 1 |
0x8A | 00 00 00 00 |
Signatures offset = 0: points to the only one GUID |
This entity is followed by only one segment, at offset 0x8E of the file
Offset | Value | Description |
---|---|---|
0x8E | 0E 00 00 00 |
Number of points = 14 |
0x92 | 00 |
AddFlag = 0 |
0x93 | 02 |
2 – Method2 |
0x94 | 0F |
Root Mask = 0x0F So N = (15 * 14* 2 + 7) >> 3 = 53 53 bytes to follow. |
0x95 | 53 bytes:84 3E 56 37 65 10 74 1D 3C 44 5B 9F 11 C5 D0 05 C9 2C F3 D5 B2 3A 8D 8A 8D 9D 9B 67 8B ED 52 59 B3 BC 0B 36 35 5F 54 25 B5 33 30 6D E2 64 4B EB 36 A1 8F D5 0D |
The next entity starts at file offset 0xCA.
So the algorithm for method 2 goes like this:
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.
The first value in the pair is longitude-related. The second value in the pair is latitude-related.
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 A
Computing 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 value
Latitude 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 heading
Pitch, 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 codes
ICAO Identifiers and region codes are coded in a special format. Each number and letter has a value from 0 .. 37:
blank | 00 |
Digits 0-9 | 02-11 |
Letters A-Z | 12-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.