diff --git a/src/fs/bgl/ap/approachleg.cpp b/src/fs/bgl/ap/approachleg.cpp index aa3ab910..f31b705c 100644 --- a/src/fs/bgl/ap/approachleg.cpp +++ b/src/fs/bgl/ap/approachleg.cpp @@ -217,6 +217,24 @@ QString ApproachLeg::altDescriptorToString(leg::AltDescriptor altDescr) return "INVALID"; } +QString ApproachLeg::speedDescriptorToString(leg::SpeedDescriptor speedDescr) +{ + switch(speedDescr) + { + case leg::UNKNOWN_SPEED: + case leg::AT: + return QString(); + + case leg::AT_OR_ABOVE: + return "+"; + + case leg::AT_OR_BELOW: + return "-"; + } + qWarning().nospace().noquote() << "Invalid approach altitude descriptor " << speedDescr; + return "INVALID"; +} + QString ApproachLeg::turnDirToString(leg::TurnDirection turnDir) { switch(turnDir) diff --git a/src/fs/bgl/ap/approachleg.h b/src/fs/bgl/ap/approachleg.h index 2d2728a0..8fe59a9a 100644 --- a/src/fs/bgl/ap/approachleg.h +++ b/src/fs/bgl/ap/approachleg.h @@ -136,8 +136,15 @@ enum TurnDirection BOTH = 3 }; -} +enum SpeedDescriptor +{ + UNKNOWN_SPEED = 0, + AT = 1, + AT_OR_ABOVE = 2, + AT_OR_BELOW = 3, +}; +} /* * Approach or transition leg. Not an actual record since it does not contain a header or size. */ @@ -296,6 +303,7 @@ class ApproachLeg static QString legTypeToString(atools::fs::bgl::leg::Type type, const QString& src, bool warn); static QString altDescriptorToString(atools::fs::bgl::leg::AltDescriptor altDescr); static QString turnDirToString(atools::fs::bgl::leg::TurnDirection turnDir); + static QString speedDescriptorToString(leg::SpeedDescriptor speedDescr); /* Only MSFS 2024 via SimConnect */ private: friend QDebug operator<<(QDebug out, const ApproachLeg& record); diff --git a/src/fs/bgl/ap/com.h b/src/fs/bgl/ap/com.h index 25877f8d..de466001 100644 --- a/src/fs/bgl/ap/com.h +++ b/src/fs/bgl/ap/com.h @@ -35,22 +35,22 @@ namespace bgl { namespace com { enum ComType { - APPROACH = 0x0008, - ASOS = 0x000D, + NONE = 0x0000, ATIS = 0x0001, - AWOS = 0x000C, - CENTER = 0x000A, - CLEARANCE = 0x0007, - CLEARANCE_PRE_TAXI = 0x000E, + MULTICOM = 0x0002, + UNICOM = 0x0003, CTAF = 0x0004, - DEPARTURE = 0x0009, - FSS = 0x000B, GROUND = 0x0005, - MULTICOM = 0x0002, - NONE = 0x0000, - REMOTE_CLEARANCE_DELIVERY = 0x000F, TOWER = 0x0006, - UNICOM = 0x0003, + CLEARANCE = 0x0007, + APPROACH = 0x0008, + DEPARTURE = 0x0009, + CENTER = 0x000A, + FSS = 0x000B, + AWOS = 0x000C, + ASOS = 0x000D, + CLEARANCE_PRE_TAXI = 0x000E, // MSFS: CPT + REMOTE_CLEARANCE_DELIVERY = 0x000F, // MSFS: GCO APPROACH_P3D_V5 = 0x0708, ASOS_P3D_V5 = 0x070D, diff --git a/src/fs/bgl/ap/parking.cpp b/src/fs/bgl/ap/parking.cpp index c8b9afcf..d85f51d6 100644 --- a/src/fs/bgl/ap/parking.cpp +++ b/src/fs/bgl/ap/parking.cpp @@ -75,6 +75,9 @@ QString Parking::parkingTypeToStr(ap::ParkingType type) case ap::GATE_EXTRA: return "GE"; + + case ap::MSFS_2024_UNKNOWN: + return "UNKN"; } qWarning().nospace().noquote() << "Invalid parking type " << type; return "INVALID"; @@ -361,6 +364,8 @@ bool Parking::isGate() const return true; else if(type == ap::GATE_HEAVY) return true; + else if(type == ap::GATE_EXTRA) + return true; return false; } @@ -375,6 +380,8 @@ bool Parking::isGaRamp() const return true; else if(type == ap::RAMP_GA_LARGE) return true; + else if(type == ap::RAMP_GA_EXTRA) + return true; return false; } diff --git a/src/fs/bgl/ap/parking.h b/src/fs/bgl/ap/parking.h index 735341ed..f92aff5e 100644 --- a/src/fs/bgl/ap/parking.h +++ b/src/fs/bgl/ap/parking.h @@ -40,23 +40,24 @@ namespace ap { enum ParkingType { - UNKNOWN = 0x0, - RAMP_GA = 0x1, - RAMP_GA_SMALL = 0x2, - RAMP_GA_MEDIUM = 0x3, - RAMP_GA_LARGE = 0x4, - RAMP_CARGO = 0x5, - RAMP_MIL_CARGO = 0x6, - RAMP_MIL_COMBAT = 0x7, - GATE_SMALL = 0x8, - GATE_MEDIUM = 0x9, - GATE_HEAVY = 0xa, - DOCK_GA = 0xb, - FUEL = 0xc, - VEHICLES = 0xd, - - RAMP_GA_EXTRA = 0xe, - GATE_EXTRA = 0xf + UNKNOWN = 0x00, + RAMP_GA = 0x01, + RAMP_GA_SMALL = 0x02, + RAMP_GA_MEDIUM = 0x03, + RAMP_GA_LARGE = 0x04, + RAMP_CARGO = 0x05, + RAMP_MIL_CARGO = 0x06, + RAMP_MIL_COMBAT = 0x07, + GATE_SMALL = 0x08, + GATE_MEDIUM = 0x09, + GATE_HEAVY = 0x0a, + DOCK_GA = 0x0b, + FUEL = 0x0c, + VEHICLES = 0x0d, + + RAMP_GA_EXTRA = 0x0e, + GATE_EXTRA = 0x0f, + MSFS_2024_UNKNOWN = 0x10 // Unknown parking type. Most likely a gate }; enum PushBack diff --git a/src/fs/bgl/ap/start.cpp b/src/fs/bgl/ap/start.cpp index 58bb96f9..b931a006 100644 --- a/src/fs/bgl/ap/start.cpp +++ b/src/fs/bgl/ap/start.cpp @@ -29,14 +29,20 @@ QString Start::startTypeToStr(start::StartType type) { switch(type) { - case atools::fs::bgl::start::RUNWAY: + case start::RUNWAY: return "R"; - case atools::fs::bgl::start::WATER: + case start::WATER: return "W"; - case atools::fs::bgl::start::HELIPAD: + case start::HELIPAD: return "H"; + + case start::TRACK: + return "T"; + + case start::UNKNOWN: + return "UNKNOWN"; } qWarning().nospace().noquote() << "Invalid START type " << type; return "INVALID"; @@ -51,7 +57,7 @@ Start::Start(const NavDatabaseOptions *options, BinaryStream *stream) runwayDesignator = flags & 0x0f; type = static_cast((flags >> 4) & 0xf); position = BglPosition(stream, true, 1000.f); - heading = stream->readFloat(); // TODO wiki heading is float degrees + heading = stream->readFloat(); // Heading is float degrees } Start::~Start() diff --git a/src/fs/bgl/ap/start.h b/src/fs/bgl/ap/start.h index 722e6779..ce6d16b0 100644 --- a/src/fs/bgl/ap/start.h +++ b/src/fs/bgl/ap/start.h @@ -36,9 +36,11 @@ namespace bgl { namespace start { enum StartType { + UNKNOWN = 0, RUNWAY = 1, WATER = 2, - HELIPAD = 3 // TOOD fix in wiki + HELIPAD = 3, + TRACK = 4 /* MSFS 2024 */ }; } // namespace start diff --git a/src/fs/bgl/ap/taxipath.cpp b/src/fs/bgl/ap/taxipath.cpp index 9d36132b..51b52db1 100644 --- a/src/fs/bgl/ap/taxipath.cpp +++ b/src/fs/bgl/ap/taxipath.cpp @@ -84,25 +84,25 @@ QString TaxiPath::pathTypeToString(taxipath::Type type) { switch(type) { - case atools::fs::bgl::taxipath::VEHICLE: - return "V"; - - case atools::fs::bgl::taxipath::UNKNOWN: + case taxipath::VEHICLE: + case taxipath::ROAD: + case taxipath::PAINTEDLINE: + case taxipath::UNKNOWN: return "UNKNOWN"; - case atools::fs::bgl::taxipath::TAXI: + case taxipath::TAXI: return "T"; - case atools::fs::bgl::taxipath::RUNWAY: + case taxipath::RUNWAY: return "R"; - case atools::fs::bgl::taxipath::PARKING: + case taxipath::PARKING: return "P"; - case atools::fs::bgl::taxipath::PATH: + case taxipath::PATH: return "PT"; - case atools::fs::bgl::taxipath::CLOSED: + case taxipath::CLOSED: return "C"; } qWarning().nospace().noquote() << "Invalid taxi path type " << type; @@ -113,16 +113,16 @@ QString TaxiPath::edgeTypeToString(taxipath::EdgeType type) { switch(type) { - case atools::fs::bgl::taxipath::NONE: + case taxipath::NONE: return "NONE"; - case atools::fs::bgl::taxipath::SOLID: + case taxipath::SOLID: return "SOLID"; - case atools::fs::bgl::taxipath::DASHED: + case taxipath::DASHED: return "DASHED"; - case atools::fs::bgl::taxipath::SOLID_DASHED: + case taxipath::SOLID_DASHED: return "SOLID_DASHED"; } qWarning().nospace().noquote() << "Invalid taxi path edge type " << type; diff --git a/src/fs/bgl/ap/taxipath.h b/src/fs/bgl/ap/taxipath.h index 40e019ad..e3e2d9e4 100644 --- a/src/fs/bgl/ap/taxipath.h +++ b/src/fs/bgl/ap/taxipath.h @@ -43,7 +43,9 @@ enum Type PARKING = 3, PATH = 4, CLOSED = 5, - VEHICLE = 6 // TODO add wiki + VEHICLE = 6, + ROAD = 7, /* MSFS 2024 */ + PAINTEDLINE = 8 /* MSFS 2024 */ }; enum EdgeType diff --git a/src/fs/bgl/bglfile.cpp b/src/fs/bgl/bglfile.cpp index 4768e618..95991f89 100644 --- a/src/fs/bgl/bglfile.cpp +++ b/src/fs/bgl/bglfile.cpp @@ -162,7 +162,7 @@ void BglFile::handleBoundaries(BinaryStream *bs) Record rec(options, bs); rec::RecordType type = rec.getId(); - if(type == rec::BOUNDARY) + if(type == rec::BOUNDARY || type == rec::BOUNDARY_MSFS2024) { rec.seekToStart(); const Record *r = createRecord(bs, &boundaries); @@ -263,6 +263,7 @@ void BglFile::readRecords(BinaryStream *bs, const atools::fs::scenery::SceneryAr bool msfsNavigraphNavdata = area.isMsfsNavigraphNavdata(); createFlags.setFlag(bgl::AIRPORT_MSFS_NAVIGRAPH_NAVDATA, msfsNavigraphNavdata); createFlags.setFlag(bgl::AIRPORT_MSFS_DUMMY, area.isNavdata()); + FsPaths::SimulatorType sim = options->getSimulatorType(); // There should be no duplicate airport idents in the file. Otherwise bail out of reading this file. QHash airportIdentCount; @@ -293,7 +294,8 @@ void BglFile::readRecords(BinaryStream *bs, const atools::fs::scenery::SceneryAr switch(type) { case section::AIRPORT: - if(options->isIncludedNavDbObject(type::AIRPORT)) + // Do not read airports from MSFS 2024. These are fetched via SimConnect. + if(sim != FsPaths::MSFS_2024 && options->isIncludedNavDbObject(type::AIRPORT)) { // Will return null if ICAO is excluded in configuration // Read airport and all subrecords, like runways, com, approaches, waypoints and so on @@ -331,38 +333,59 @@ void BglFile::readRecords(BinaryStream *bs, const atools::fs::scenery::SceneryAr break; case section::AIRPORT_ALT: - qWarning() << "Found alternate airport ID"; - if(options->isIncludedNavDbObject(type::AIRPORT)) + qWarning() << Q_FUNC_INFO << "Found alternate airport ID"; + if(sim != FsPaths::MSFS_2024 && options->isIncludedNavDbObject(type::AIRPORT)) rec = createRecord(bs, &airports, bgl::NO_CREATE_FLAGS); break; case section::NAME_LIST: - rec = createRecord(bs, &namelists); + // Do not read airports/namelists from MSFS 2024. These are fetched via SimConnect. + if(sim != FsPaths::MSFS_2024) + rec = createRecord(bs, &namelists); break; case section::P3D_TACAN: - rec = createRecord(bs, &tacans); + // TACAN secion type overlaps with a MSFS 2024 section type + if(sim != FsPaths::MSFS_2024) + rec = createRecord(bs, &tacans); break; case section::ILS_VOR: + // Read VOR, VORDME, DME. Also TACAN for MSFS 2024 + // Do not read from MSFS 2020 Navigraph extension if(!msfsNavigraphNavdata) + { rec = handleIlsVor(bs); + if(options->isVerbose() && rec != nullptr) + qDebug() << Q_FUNC_INFO << "ILS_VOR" << hex << rec->getId(); + } break; case section::NDB: + // Do not read from MSFS 2020 Navigraph extension if(options->isIncludedNavDbObject(type::NDB) && !msfsNavigraphNavdata) + { rec = createRecord(bs, &ndbs); + if(options->isVerbose() && rec != nullptr) + qDebug() << Q_FUNC_INFO << "NDB" << hex << rec->getId(); + } break; case section::MARKER: + // Do not read from MSFS 2020 Navigraph extension if(options->isIncludedNavDbObject(type::MARKER) && !msfsNavigraphNavdata) rec = createRecord(bs, &marker); break; case section::WAYPOINT: + // Do not read from MSFS 2020 Navigraph extension if(options->isIncludedNavDbObject(type::WAYPOINT) && !msfsNavigraphNavdata) + { // Read waypoints and airways rec = createRecord(bs, &waypoints); + if(options->isVerbose() && rec != nullptr) + qDebug() << Q_FUNC_INFO << "WAYPOINT" << hex << rec->getId(); + } break; // MSFS sections not found yet diff --git a/src/fs/bgl/boundary.cpp b/src/fs/bgl/boundary.cpp index d465d836..7860e6d7 100644 --- a/src/fs/bgl/boundary.cpp +++ b/src/fs/bgl/boundary.cpp @@ -144,7 +144,7 @@ Boundary::Boundary() Boundary::Boundary(const NavDatabaseOptions *options, BinaryStream *stream) : Record(options, stream) { - if(id != rec::BOUNDARY) + if(id != rec::BOUNDARY && id != rec::BOUNDARY_MSFS2024) { qWarning() << "Not a boundary record" << hex << "0x" << id << dec; excluded = true; @@ -158,7 +158,6 @@ Boundary::Boundary(const NavDatabaseOptions *options, BinaryStream *stream) } int flags = stream->readUByte(); - // TODO fix in wiki minAltType = static_cast(flags & 0xf); maxAltType = static_cast((flags >> 4) & 0xf); @@ -176,8 +175,11 @@ Boundary::Boundary(const NavDatabaseOptions *options, BinaryStream *stream) minPosition = BglPosition(stream, true, 1000.f); maxPosition = BglPosition(stream, true, 1000.f); - atools::io::Encoding encoding = options->getSimulatorType() == - atools::fs::FsPaths::MSFS ? atools::io::UTF8 : atools::io::LATIN1; + atools::io::Encoding encoding = options->getSimulatorType() == atools::fs::FsPaths::MSFS || id != rec::BOUNDARY_MSFS2024 ? + atools::io::UTF8 : atools::io::LATIN1; + + if(id == rec::BOUNDARY_MSFS2024) + stream->skip(20); // Skip unknown data int numFreq = 0; while(stream->tellg() < startOffset + size) @@ -197,9 +199,11 @@ Boundary::Boundary(const NavDatabaseOptions *options, BinaryStream *stream) comFrequency = stream->readInt() / 1000; comName = stream->readString(r.getSize() - 12, encoding); break; + case rec::BOUNDARY_NAME: name = stream->readString(r.getSize() - Record::SIZE, atools::io::LATIN1); break; + case rec::BOUNDARY_LINES: { // Read geometry diff --git a/src/fs/bgl/converter.cpp b/src/fs/bgl/converter.cpp index 5c20ee53..96b15e92 100644 --- a/src/fs/bgl/converter.cpp +++ b/src/fs/bgl/converter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* Copyright 2015-2020 Alexander Barthel alex@littlenavmap.org +* Copyright 2015-2024 Alexander Barthel alex@littlenavmap.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,25 +25,23 @@ namespace fs { namespace bgl { namespace converter { -static const char *RUNWAY_DESIGNATORS[] = {"", "L", "R", "C", "W", "A", "B"}; - -QString intToIcao(unsigned int icao, bool noBitShift) +QString intToIcaoInternal(quint64 icao, int numChars, int bitShift) { QString icaoStr; - unsigned int value = icao; + quint64 value = icao; // The ICAO identifiers for primary and secondary ILS in a runway record are not shifted. - if(!noBitShift) - value = value >> 5; + value = value >> bitShift; if(value == 0) return QString(); - QVarLengthArray codedArr({0, 0, 0, 0, 0}); - unsigned int coded = 0; + // Max of 8 characters for MSFS 2024 + QVarLengthArray codedArr({0, 0, 0, 0, 0, 0, 0, 0}); + quint64 coded = 0; // First extract the coded/compressed values int idx = 0; - if(value > 37) + if(value > 37) // More than "Z" { while(value > 37) { @@ -64,24 +62,37 @@ QString intToIcao(unsigned int icao, bool noBitShift) } } else + // More or equal than "0" and lower or equal than "Z" codedArr[idx++] = value; // Convert the decompressed bytes to characters - for(int i = 0; i < 5; i++) + for(int i = 0; i < numChars; i++) { - coded = codedArr.at(i); - if(coded == 0) + unsigned int codedChar = static_cast(codedArr.at(i)); + if(codedChar == 0) break; - if(coded > 1 && coded < 12) - icaoStr.insert(0, '0' + (coded - 2)); + if(codedChar > 1 && codedChar < 12) + icaoStr.insert(0, '0' + (codedChar - 2)); else - icaoStr.insert(0, 'A' + (coded - 12)); + icaoStr.insert(0, 'A' + (codedChar - 12)); } + return icaoStr; } +QString intToIcao(unsigned int icao, bool noBitShift) +{ + return intToIcaoInternal(icao, 5 /* numChars */, noBitShift ? 0 : 5 /* bitShift */); +} + +QString intToIcaoLong(quint64 icao, bool noBitShift) +{ + return intToIcaoInternal(icao, 8 /* numChars */, noBitShift ? 0 : 6 /* bitShift */); +} + QString designatorStr(int designator) { + static const QVarLengthArray RUNWAY_DESIGNATORS({"", "L", "R", "C", "W", "A", "B"}); if(designator >= 0 && designator <= 6) return RUNWAY_DESIGNATORS[designator]; @@ -152,8 +163,8 @@ time_t filetime(unsigned int lowDateTime, unsigned int highDateTime) static const unsigned long long FILETIME_EPOCH_DIFF = 11644473600LL; static const unsigned long long FILETIME_SECOND = 10000000LL; - unsigned long long filetime = ((static_cast(highDateTime)) << 32) - + static_cast(lowDateTime); + unsigned long long filetime = ((static_cast(highDateTime)) << 32) + + static_cast(lowDateTime); filetime /= FILETIME_SECOND; filetime -= FILETIME_EPOCH_DIFF; return static_cast(filetime); diff --git a/src/fs/bgl/converter.h b/src/fs/bgl/converter.h index b5dccddc..ae9f825d 100644 --- a/src/fs/bgl/converter.h +++ b/src/fs/bgl/converter.h @@ -1,5 +1,5 @@ /***************************************************************************** -* Copyright 2015-2020 Alexander Barthel alex@littlenavmap.org +* Copyright 2015-2024 Alexander Barthel alex@littlenavmap.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,18 +48,27 @@ inline float intToLatY(int latY) time_t filetime(unsigned int lowDateTime, unsigned int highDateTime); /* - * Convert the BGL ICAO format to string - * @param noBitShift if true do not shift 5 bits to the right + * Convert the BGL ICAO format to string for FSX, P3D and MSFS 2020 + * Max of five characters into four bytes. + * @param noBitShift if true do not shift 5 bits to the right before decoding */ QString intToIcao(unsigned int icao, bool noBitShift = false); /* - * Convert BGL runway designator to a string like "L", "C", "R" or "W" + * Convert the BGL ICAO format to string using MSFS 2024 eight character format. + * Uses a different bit shift of 6. + * Max of eight characters into six or eight bytes. + * @param noBitShift if true do not shift 6 bits to the right before decoding + */ +QString intToIcaoLong(quint64 icao, bool noBitShift = false); + +/* + * Convert BGL runway designator number to a string like "L", "C", "R" or "W" */ QString designatorStr(int designator); /* - * Create a full runway name from number and designator. + * Create a full runway name from number and designator. Number is always two digits except special codes like "N" or "E". * @return Runway name like "12", "24C" or "NE" */ QString runwayToStr(int runwayNumber, int designator); diff --git a/src/fs/bgl/nav/airwaysegment.cpp b/src/fs/bgl/nav/airwaysegment.cpp index 771bcfd2..f448bce0 100644 --- a/src/fs/bgl/nav/airwaysegment.cpp +++ b/src/fs/bgl/nav/airwaysegment.cpp @@ -51,9 +51,11 @@ AirwaySegment::AirwaySegment(const atools::fs::NavDatabaseOptions *options, Bina type = static_cast(stream->readUByte()); name = stream->readString(8, atools::io::LATIN1); + // Current waypoint mid = AirwayWaypoint(waypoint); - next = AirwayWaypoint(options, stream); - previous = AirwayWaypoint(options, stream); + + next = AirwayWaypoint(options, stream, waypoint); + previous = AirwayWaypoint(options, stream, waypoint); } AirwaySegment::~AirwaySegment() diff --git a/src/fs/bgl/nav/airwaywaypoint.cpp b/src/fs/bgl/nav/airwaywaypoint.cpp index 7e9ac6af..c162f23e 100644 --- a/src/fs/bgl/nav/airwaywaypoint.cpp +++ b/src/fs/bgl/nav/airwaywaypoint.cpp @@ -16,9 +16,11 @@ *****************************************************************************/ #include "fs/bgl/nav/airwaywaypoint.h" + #include "fs/bgl/converter.h" -#include "io/binarystream.h" #include "fs/bgl/nav/waypoint.h" +#include "fs/bgl/recordtypes.h" +#include "io/binarystream.h" namespace atools { namespace fs { @@ -42,6 +44,7 @@ AirwayWaypoint::AirwayWaypoint(const Waypoint& waypoint) case atools::fs::bgl::nav::FAF: case atools::fs::bgl::nav::RNAV: case atools::fs::bgl::nav::VFR: + // Airway waypoint has a limited number of types type = nav::AIRWAY_WP_OTHER; break; @@ -55,17 +58,33 @@ AirwayWaypoint::AirwayWaypoint(const Waypoint& waypoint) } } -AirwayWaypoint::AirwayWaypoint(const atools::fs::NavDatabaseOptions *options, atools::io::BinaryStream *stream) +AirwayWaypoint::AirwayWaypoint(const atools::fs::NavDatabaseOptions *options, atools::io::BinaryStream *stream, const Waypoint& waypoint) : BglBase(options, stream) { - unsigned int nextFlags = stream->readUInt(); - unsigned int nextIdFlags = stream->readUInt(); - minimumAltitude = stream->readFloat(); - - type = static_cast(nextFlags & 0x7); - ident = converter::intToIcao((nextFlags >> 5) & 0x7ffffff, true); - region = converter::intToIcao(nextIdFlags & 0x7ff, true); - airportIdent = converter::intToIcao((nextIdFlags >> 11) & 0xfffff, true); + if(waypoint.getId() == rec::WAYPOINT_MSFS2024) + { + ident = converter::intToIcaoLong(stream->readULong()); + region = converter::intToIcao(stream->readUInt() & 0x7ff, true); + + stream->skip(4); // float of maximum altitude? Alyway 0 + minimumAltitude = stream->readFloat(); + + // Type not given in MSFS 2024 + type = nav::AIRWAY_WP_OTHER; + } + else + { + // FSX, P3D and MSFS 2020 + unsigned int nextFlags = stream->readUInt(); + type = static_cast(nextFlags & 0x7); + ident = converter::intToIcao((nextFlags >> 5) & 0x7ffffff, true); + + unsigned int nextIdFlags = stream->readUInt(); + region = converter::intToIcao(nextIdFlags & 0x7ff, true); + airportIdent = converter::intToIcao((nextIdFlags >> 11) & 0xfffff, true); + + minimumAltitude = stream->readFloat(); + } } AirwayWaypoint::~AirwayWaypoint() diff --git a/src/fs/bgl/nav/airwaywaypoint.h b/src/fs/bgl/nav/airwaywaypoint.h index 2ba9eb88..6ee22b2b 100644 --- a/src/fs/bgl/nav/airwaywaypoint.h +++ b/src/fs/bgl/nav/airwaywaypoint.h @@ -50,7 +50,7 @@ class AirwayWaypoint : { public: AirwayWaypoint(const Waypoint& waypoint); - AirwayWaypoint(const NavDatabaseOptions *options, atools::io::BinaryStream *stream); + AirwayWaypoint(const NavDatabaseOptions *options, atools::io::BinaryStream *stream, const atools::fs::bgl::Waypoint& waypoint); virtual ~AirwayWaypoint() override; AirwayWaypoint() diff --git a/src/fs/bgl/nav/ils.cpp b/src/fs/bgl/nav/ils.cpp index 397b9344..7f607514 100644 --- a/src/fs/bgl/nav/ils.cpp +++ b/src/fs/bgl/nav/ils.cpp @@ -52,7 +52,6 @@ Ils::Ils(const NavDatabaseOptions *options, BinaryStream *stream) int flags = stream->readUByte(); backcourse = (flags & FLAGS_BC) == FLAGS_BC; - // TODO compare values with record presence // dmeOnlyOrIls = (flags & FLAGS_DME_ONLY) == FLAGS_DME_ONLY; // hasGlideslope = (flags & FLAGS_GS) == FLAGS_GS; // hasDme = (flags & FLAGS_DME) == FLAGS_DME; @@ -62,19 +61,23 @@ Ils::Ils(const NavDatabaseOptions *options, BinaryStream *stream) position = BglPosition(stream, true, 1000.f); frequency = stream->readInt() / 1000; range = stream->readFloat(); - magVar = converter::adjustMagvar(stream->readFloat()); + float mv = stream->readFloat(); + magVar = converter::adjustMagvar(mv); // ILS ident - ident = converter::intToIcao(stream->readUInt()); + ident = id == rec::ILS_VOR_MSFS2024 ? converter::intToIcaoLong(stream->readULong()) : converter::intToIcao(stream->readUInt()); unsigned int regionFlags = stream->readUInt(); // Two letter region code - region = converter::intToIcao(regionFlags & 0x7ff, true); // TODO wiki region is never set + region = converter::intToIcao(regionFlags & 0x7ff, true); // Region is never set // Read airport ICAO ident airportIdent = converter::intToIcao((regionFlags >> 11) & 0x1fffff, true); - atools::io::Encoding encoding = options->getSimulatorType() == - atools::fs::FsPaths::MSFS ? atools::io::UTF8 : atools::io::LATIN1; + atools::io::Encoding encoding = options->getSimulatorType() == FsPaths::MSFS || id == rec::ILS_VOR_MSFS2024 ? + atools::io::UTF8 : atools::io::LATIN1; + + if(id == rec::ILS_VOR_MSFS2024) + stream->skip(4); // Skip unknown data // Read all subrecords of ILS while(stream->tellg() < startOffset + size) @@ -89,19 +92,25 @@ Ils::Ils(const NavDatabaseOptions *options, BinaryStream *stream) case rec::ILS_VOR_NAME: name = stream->readString(r.getSize() - Record::SIZE, encoding); break; + case rec::LOCALIZER: - // This is actually not optional for an ILS + case rec::LOCALIZER_MSFS2024: + // Required for ILS r.seekToStart(); - localizer = new Localizer(options, stream); + localizer = new Localizer(options, stream, magVar); break; + case rec::GLIDESLOPE: r.seekToStart(); glideslope = new Glideslope(options, stream); break; + case rec::DME: + case rec::DME_MSFS2024: r.seekToStart(); dme = new Dme(options, stream); break; + default: qWarning().nospace().noquote() << Q_FUNC_INFO << " Unexpected record type in ILS record 0x" << hex << t << dec << " for ident " << ident; diff --git a/src/fs/bgl/nav/localizer.cpp b/src/fs/bgl/nav/localizer.cpp index ede96d8c..5882db30 100644 --- a/src/fs/bgl/nav/localizer.cpp +++ b/src/fs/bgl/nav/localizer.cpp @@ -17,9 +17,10 @@ #include "fs/bgl/nav/localizer.h" -#include "io/binarystream.h" #include "fs/bgl/converter.h" +#include "fs/bgl/recordtypes.h" #include "fs/navdatabaseoptions.h" +#include "io/binarystream.h" namespace atools { namespace fs { @@ -27,12 +28,18 @@ namespace bgl { using atools::io::BinaryStream; -Localizer::Localizer(const NavDatabaseOptions *options, BinaryStream *stream) +Localizer::Localizer(const NavDatabaseOptions *options, BinaryStream *stream, float magVar) : Record(options, stream) { - runwayNumber = stream->readUByte(); - runwayDesignator = stream->readUByte(); + runwayNumber = stream->readUByte(); // Not set + runwayDesignator = stream->readUByte(); // Not set heading = stream->readFloat(); + + if(id == rec::LOCALIZER_MSFS2024) + // MSFS 2024 gives heading in magnetic instead of true + heading += magVar; + + // Limit width width = std::min(stream->readFloat(), 20.f); } diff --git a/src/fs/bgl/nav/localizer.h b/src/fs/bgl/nav/localizer.h index 498f217c..7cd721c7 100644 --- a/src/fs/bgl/nav/localizer.h +++ b/src/fs/bgl/nav/localizer.h @@ -31,7 +31,7 @@ class Localizer : public atools::fs::bgl::Record { public: - Localizer(const atools::fs::NavDatabaseOptions *options, atools::io::BinaryStream *stream); + Localizer(const atools::fs::NavDatabaseOptions *options, atools::io::BinaryStream *stream, float magVar); virtual ~Localizer() override; /* diff --git a/src/fs/bgl/nav/ndb.cpp b/src/fs/bgl/nav/ndb.cpp index 1c3219c2..49ae7ffd 100644 --- a/src/fs/bgl/nav/ndb.cpp +++ b/src/fs/bgl/nav/ndb.cpp @@ -60,14 +60,17 @@ Ndb::Ndb(const NavDatabaseOptions *options, BinaryStream *stream) position = BglPosition(stream, true, 1000.f); range = stream->readFloat(); magVar = converter::adjustMagvar(stream->readFloat()); - ident = converter::intToIcao(stream->readUInt()); + ident = id == rec::NDB_MSFS2024 ? converter::intToIcaoLong(stream->readULong()) : converter::intToIcao(stream->readUInt()); unsigned int regionFlags = stream->readUInt(); region = converter::intToIcao(regionFlags & 0x7ff, true); airportIdent = converter::intToIcao((regionFlags >> 11) & 0x1fffff, true); - atools::io::Encoding encoding = options->getSimulatorType() == - atools::fs::FsPaths::MSFS ? atools::io::UTF8 : atools::io::LATIN1; + if(id == rec::NDB_MSFS2024) + stream->skip(4); // Skip unknown + + atools::io::Encoding encoding = options->getSimulatorType() == FsPaths::MSFS || id == rec::NDB_MSFS2024 ? + atools::io::UTF8 : atools::io::LATIN1; // Read only name subrecord if(stream->tellg() < startOffset + size) @@ -84,6 +87,7 @@ Ndb::Ndb(const NavDatabaseOptions *options, BinaryStream *stream) case rec::NDB_NAME: name = stream->readString(r.getSize() - Record::SIZE, encoding); break; + default: qWarning().nospace().noquote() << Q_FUNC_INFO << " Unexpected record type in NDB record 0x" << hex << t << dec << " for ident " << ident; diff --git a/src/fs/bgl/nav/tacan.cpp b/src/fs/bgl/nav/tacan.cpp index ec5de8a4..4bf0b593 100644 --- a/src/fs/bgl/nav/tacan.cpp +++ b/src/fs/bgl/nav/tacan.cpp @@ -50,8 +50,8 @@ Tacan::Tacan(const NavDatabaseOptions *options, BinaryStream *stream) region = converter::intToIcao(regionFlags & 0x7ff, true); airportIdent = converter::intToIcao((regionFlags >> 11) & 0x1fffff, true); - atools::io::Encoding encoding = options->getSimulatorType() == - atools::fs::FsPaths::MSFS ? atools::io::UTF8 : atools::io::LATIN1; + atools::io::Encoding encoding = options->getSimulatorType() == FsPaths::MSFS || id == rec::ILS_VOR_MSFS2024 ? + atools::io::UTF8 : atools::io::LATIN1; while(stream->tellg() < startOffset + size) { @@ -65,11 +65,15 @@ Tacan::Tacan(const NavDatabaseOptions *options, BinaryStream *stream) case rec::ILS_VOR_NAME: name = stream->readString(r.getSize() - Record::SIZE, encoding); break; + case rec::DME: + case rec::DME_MSFS2024: r.seekToStart(); dme = new Dme(options, stream); break; + case rec::LOCALIZER: + case rec::LOCALIZER_MSFS2024: case rec::GLIDESLOPE: break; default: diff --git a/src/fs/bgl/nav/tacan.h b/src/fs/bgl/nav/tacan.h index 23803761..ca0e4006 100644 --- a/src/fs/bgl/nav/tacan.h +++ b/src/fs/bgl/nav/tacan.h @@ -27,7 +27,7 @@ namespace bgl { class Dme; /* - * TACAN + * TACAN. Not used for MSFS 2024. */ class Tacan : public atools::fs::bgl::NavBase diff --git a/src/fs/bgl/nav/vor.cpp b/src/fs/bgl/nav/vor.cpp index 9218b395..19d4ed7e 100644 --- a/src/fs/bgl/nav/vor.cpp +++ b/src/fs/bgl/nav/vor.cpp @@ -36,17 +36,12 @@ using Qt::dec; enum VorFlags { - // bit 0: if 0 then DME only, otherwise 1 for ILS - // bit 2: backcourse (0 = false, 1 = true) - // bit 3: glideslope present - // bit 4: DME present - // bit 5: NAV true - - FLAGS_DME_ONLY = 1 << 0, - FLAGS_BC = 1 << 1, - FLAGS_GS = 1 << 2, - FLAGS_DME = 1 << 3, - FLAGS_NAV = 1 << 4 + FLAGS_DME_ONLY = 1 << 0, // if 0 then DME only, otherwise 1 for ILS + FLAGS_BC = 1 << 1, // backcourse (0 = false, 1 = true) + FLAGS_GS = 1 << 2, // glideslope present + FLAGS_DME = 1 << 3, // DME present + FLAGS_NAV = 1 << 4, // NAV true + FLAGS_TACAN_MSFS = 1 << 1 /* 2020 and 2024 */ }; Vor::Vor(const NavDatabaseOptions *options, BinaryStream *stream) @@ -54,26 +49,33 @@ Vor::Vor(const NavDatabaseOptions *options, BinaryStream *stream) { type = static_cast(stream->readUByte()); int flags = stream->readUByte(); - - dmeOnly = (flags & FLAGS_DME_ONLY) == 0; - // TODO compare flags with record presence + tacan = flags & FLAGS_TACAN_MSFS; + dmeOnly = (flags & FLAGS_DME_ONLY) == 0 && !tacan; // hasDme = (flags & FLAGS_DME) == FLAGS_DME; // hasNav = (flags & FLAGS_NAV) == FLAGS_NAV; + // Flag fields found in MSFS 2024 + // "TCN" "ZZ" 0x33 0b110011 0x0 dmeOnly false dummy 0x0 + // "VDM" "ZZ" 0x31 0b110001 0x0 dmeOnly false dummy 0x0 + // "VOR" "ZZ" 0x1 0b000001 0x0 dmeOnly false dummy 0x0 + // "DME" "ZZ" 0x10 0b010000 0x0 dmeOnly true dummy 0x0 + position = BglPosition(stream, true, 1000.f); frequency = stream->readInt() / 1000; range = stream->readFloat(); magVar = converter::adjustMagvar(stream->readFloat()); - - ident = converter::intToIcao(stream->readUInt()); + ident = id == rec::ILS_VOR_MSFS2024 ? converter::intToIcaoLong(stream->readULong()) : converter::intToIcao(stream->readUInt()); unsigned int regionFlags = stream->readUInt(); region = converter::intToIcao(regionFlags & 0x7ff, true); - // TODO report wiki error ap ident is never set + // Airport ident is never set airportIdent = converter::intToIcao((regionFlags >> 11) & 0x1fffff, true); - atools::io::Encoding encoding = options->getSimulatorType() == - atools::fs::FsPaths::MSFS ? atools::io::UTF8 : atools::io::LATIN1; + atools::io::Encoding encoding = options->getSimulatorType() == FsPaths::MSFS || id == rec::ILS_VOR_MSFS2024 ? + atools::io::UTF8 : atools::io::LATIN1; + + if(id == rec::ILS_VOR_MSFS2024) + stream->skip(4); // Skip unknown data while(stream->tellg() < startOffset + size) { @@ -87,16 +89,21 @@ Vor::Vor(const NavDatabaseOptions *options, BinaryStream *stream) case rec::ILS_VOR_NAME: name = stream->readString(r.getSize() - Record::SIZE, encoding); break; + case rec::DME: + case rec::DME_MSFS2024: r.seekToStart(); dme = new Dme(options, stream); break; - case atools::fs::bgl::rec::LOCALIZER: - case atools::fs::bgl::rec::GLIDESLOPE: + + // Only ILS records - should not appear here + case rec::LOCALIZER: + case rec::LOCALIZER_MSFS2024: + case rec::GLIDESLOPE: + default: + qWarning().nospace().noquote() << Q_FUNC_INFO << " Unexpected record type in VOR record 0x" << hex << t << dec + << " for ident " << ident; break; - // default: - // qWarning().nospace().noquote() << Q_FUNC_INFO << " Unexpected record type in VOR record 0x" << hex << t << dec << - // " for ident " << ident; } r.seekToEnd(); } diff --git a/src/fs/bgl/nav/vor.h b/src/fs/bgl/nav/vor.h index ef6d5425..e3c80d29 100644 --- a/src/fs/bgl/nav/vor.h +++ b/src/fs/bgl/nav/vor.h @@ -28,7 +28,8 @@ namespace bgl { class Dme; /* - * VHF omnidirectional range - VOR/DME/VORDME record + * VOR/DME/VORDME record + * Also reads MSFS 2024 TACAN */ class Vor : public atools::fs::bgl::NavBase @@ -64,11 +65,17 @@ class Vor : return type; } + /* true if MSFS 2024 TACAN */ + bool isTacan() const + { + return tacan; + } + private: friend QDebug operator<<(QDebug out, const atools::fs::bgl::Vor& record); atools::fs::bgl::nav::IlsVorType type; - bool dmeOnly; + bool dmeOnly, tacan; atools::fs::bgl::Dme *dme = nullptr; }; diff --git a/src/fs/bgl/nav/waypoint.cpp b/src/fs/bgl/nav/waypoint.cpp index b3ea8b76..27b7c2f7 100644 --- a/src/fs/bgl/nav/waypoint.cpp +++ b/src/fs/bgl/nav/waypoint.cpp @@ -18,6 +18,7 @@ #include "fs/bgl/nav/waypoint.h" #include "fs/bgl/converter.h" #include "fs/bgl/nav/airwaysegment.h" +#include "fs/bgl/recordtypes.h" #include "io/binarystream.h" #include "fs/navdatabaseoptions.h" @@ -93,8 +94,7 @@ Waypoint::Waypoint(const NavDatabaseOptions *options, BinaryStream *stream) int numAirways = stream->readUByte(); position = BglPosition(stream); magVar = converter::adjustMagvar(stream->readFloat()); - unsigned int identInt = stream->readUInt(); - ident = converter::intToIcao(identInt); + ident = id == rec::WAYPOINT_MSFS2024 ? converter::intToIcaoLong(stream->readULong()) : converter::intToIcao(stream->readUInt()); unsigned int regionFlags = stream->readUInt(); region = converter::intToIcao(regionFlags & 0x7ff, true); @@ -106,7 +106,10 @@ Waypoint::Waypoint(const NavDatabaseOptions *options, BinaryStream *stream) if(ident.isEmpty() && type != nav::UNNAMED && !isDisabled() && options->getSimulatorType() != atools::fs::FsPaths::MSFS) qWarning().nospace().noquote() << "Waypoint at " << position << " region " << region << " has no ident"; - // Read airways if desired by configuration + if(id == rec::WAYPOINT_MSFS2024) + stream->skip(4); // Skip unknown data + + // Read airways for(int i = 0; i < numAirways; i++) { // Read always to avoid messing up current file position diff --git a/src/fs/bgl/recordtypes.cpp b/src/fs/bgl/recordtypes.cpp index a53c4c25..5b2c0631 100644 --- a/src/fs/bgl/recordtypes.cpp +++ b/src/fs/bgl/recordtypes.cpp @@ -52,6 +52,9 @@ QString recordTypeStr(rec::RecordType type) case rec::BOUNDARY: return "BOUNDARY"; + case rec::BOUNDARY_MSFS2024: + return "BOUNDARY_MSFS2024"; + case rec::GEOPOL: return "GEOPOL"; @@ -66,6 +69,15 @@ QString recordTypeStr(rec::RecordType type) case rec::WAYPOINT_ICAO_INDEX: return "WAYPOINT_ICAO_INDEX"; + + case ILS_VOR_MSFS2024: + return "ILS_VOR_MSFS2024"; + + case WAYPOINT_MSFS2024: + return "WAYPOINT_MSFS2024"; + + case NDB_MSFS2024: + return "NDB_MSFS2024"; } qWarning().nospace().noquote() << "Invalid record type " << type; return "INVALID"; @@ -458,12 +470,18 @@ QString ilsvorRecordTypeStr(rec::IlsVorRecordType type) case rec::LOCALIZER: return "LOCALIZER"; + case LOCALIZER_MSFS2024: + return "LOCALIZER_MSFS2024"; + case rec::GLIDESLOPE: return "GLIDESLOPE"; case rec::DME: return "DME"; + case DME_MSFS2024: + return "DME_MSFS2024"; + case rec::ILS_VOR_NAME: return "ILS_VOR_NAME"; } diff --git a/src/fs/bgl/recordtypes.h b/src/fs/bgl/recordtypes.h index 8a0c36b1..fdd19668 100644 --- a/src/fs/bgl/recordtypes.h +++ b/src/fs/bgl/recordtypes.h @@ -26,7 +26,9 @@ namespace bgl { namespace rec { -/* Top level record types */ +/* Top level record types. +Not used in BglFile which relies on section types. +Reading classes use these types to detect BGL version / simulator. */ enum RecordType { AIRPORT = 0x003c, @@ -41,7 +43,14 @@ enum RecordType NAMELIST = 0x0027, VOR_ILS_ICAO_INDEX = 0x0028, NDB_ICAO_INDEX = 0x0029, - WAYPOINT_ICAO_INDEX = 0x002A + WAYPOINT_ICAO_INDEX = 0x002A, + + /* MSFS 2024: New record types for top-level records. + * Have structure changes and this id value is checked in the classes Ils, Vor, Ndb and Boundary. */ + ILS_VOR_MSFS2024 = 0x0105, + WAYPOINT_MSFS2024 = 0x0108, + NDB_MSFS2024 = 0x0106, + BOUNDARY_MSFS2024 = 0x0107 }; QString recordTypeStr(RecordType type); @@ -199,7 +208,11 @@ enum IlsVorRecordType LOCALIZER = 0x0014, GLIDESLOPE = 0x0015, DME = 0x0016, - ILS_VOR_NAME = 0x0019 + ILS_VOR_NAME = 0x0019, + + /* MSFS 2024: New record types for ILS and VOR sub-records. No changes in structure */ + DME_MSFS2024 = 0x0114, + LOCALIZER_MSFS2024 = 0x0115 }; QString ilsvorRecordTypeStr(IlsVorRecordType type);