File indexing completed on 2024-05-12 04:42:34

0001 /*
0002     SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KPUBLICTRANSPORT_HAFASJOURNEYRESPONSE_P_H
0008 #define KPUBLICTRANSPORT_HAFASJOURNEYRESPONSE_P_H
0009 
0010 #include <QStringDecoder>
0011 
0012 #include <cstdint>
0013 
0014 namespace KPublicTransport {
0015 
0016 #pragma pack(push)
0017 #pragma pack(1)
0018 
0019 // occurs once at offset 0
0020 struct HafasJourneyResponseHeader
0021 {
0022     uint16_t version;
0023     uint16_t departureNameStr;
0024     uint16_t padding1;
0025     uint16_t departureType;
0026     uint32_t departureLongitude;
0027     uint32_t departureLatitude;
0028     uint16_t arrivalNameStr;
0029     uint16_t padding2;
0030     uint16_t arrivalType;
0031     uint32_t arrivalLongitude;
0032     uint32_t arrivalLatitude;
0033     // amount of journeys in the response
0034     uint16_t numJourneys;
0035     uint32_t serviceDaysTableOffset;
0036     // offset to HafasJourneyResponseStringTable
0037     uint32_t stringTableOffset;
0038     // base date for all times in the response, in days after 1980-01-01
0039     uint16_t date;
0040     uint8_t padding3[12];
0041     // offset to HafasJourneyResponseStation table
0042     uint32_t stationTableOffset;
0043     uint32_t commentTableOffset;
0044     uint8_t padding4[8];
0045     // offset to HafasJourneyResponseExtendedHeader
0046     uint32_t extendedHeaderOffset;
0047 };
0048 static_assert(sizeof(HafasJourneyResponseHeader) == 0x4a, "size of response header is wrong");
0049 static_assert(alignof(HafasJourneyResponseHeader) == 1, "broken alignment for binary response struct");
0050 
0051 // extended header, occurs once at offset HafasJourneyResponseHeader::extendedHeaderOffset
0052 struct HafasJourneyResponseExtendedHeader
0053 {
0054     // length of the extended header (should >= sizeof(HafasJourneyResponseExtendedHeader)
0055     uint32_t length;
0056     uint32_t padding1;
0057     uint16_t seqNum;
0058     uint16_t requestIdStr;
0059     // offset to HafasJourneyResponseDetailsHeader
0060     uint32_t detailsOffset;
0061     // error code for the journey query, 0 means success
0062     uint16_t errorCode;
0063     uint16_t padding2;
0064     uint32_t disruptionTableOffset;
0065     uint8_t padding3[8];
0066     // string table index for the name of the encoding used in the string table
0067     uint16_t encodingStr;
0068     uint16_t ldStr;
0069     // base offset for attributes table
0070     uint32_t attributesOffset;
0071     uint32_t padding4;
0072     // if > 0 this is an index into a uint16_t table with the journey attribute offsets
0073     uint32_t journeyAttributesIndexOffset;
0074 };
0075 static_assert(alignof(HafasJourneyResponseExtendedHeader) == 1, "broken alignment for binary response struct");
0076 
0077 // journey details header, occurs once at offset HafasJourneyResponseExtendedHeader::detailsOffset
0078 struct HafasJourneyResponseDetailsHeader
0079 {
0080     uint16_t version;
0081     uint16_t padding;
0082     uint16_t journeyDetailsIndexOffset;
0083     uint16_t sectionDetailsOffset;
0084     uint16_t sectionDetailsSize;
0085     uint16_t stopsSize;
0086     uint16_t stopsOffset;
0087 };
0088 static_assert(alignof(HafasJourneyResponseDetailsHeader) == 1, "broken alignment for binary response struct");
0089 
0090 // occurs HafasJourneyResponseHeader::numJourneys times, right after HafasJourneyResponseHeader at offset 0x4a
0091 struct HafasJourneyResponseJourney
0092 {
0093     uint16_t serviceDaysTableOffset;
0094     // offset for the HafasJourneyResponseSection table to start
0095     uint32_t sectionsOffset;
0096     // number of sections in this journey
0097     uint16_t numSections;
0098     uint16_t numChanges;
0099     uint16_t duration;
0100 };
0101 static_assert(sizeof(HafasJourneyResponseJourney) == 12, "size of journey structure is wrong");
0102 static_assert(alignof(HafasJourneyResponseJourney) == 1, "broken alignment for binary response struct");
0103 
0104 // occurs HafasJourneyResponseJourney::numSections times, after HafasJourneyResponseHeader + HafasJourneyResponseJourney::sectionsOffset
0105 struct HafasJourneyResponseSection
0106 {
0107     // hours * 100 + minutes
0108     uint16_t scheduledDepartureTime;
0109     // index into station table
0110     uint16_t departureStationIdx;
0111     uint16_t scheduledArrivalTime;
0112     uint16_t arrivalStationIdx;
0113     // section mode, see enum below
0114     uint16_t type;
0115     // index into string table
0116     uint16_t lineNameStr;
0117     // index into string table
0118     uint16_t scheduledDeparturePlatformStr;
0119     uint16_t scheduledArrivalPlatformStr;
0120     // section attribute table index
0121     uint16_t sectionAttributeIndex;
0122     uint16_t commentIdx;
0123 };
0124 static_assert(sizeof(HafasJourneyResponseSection) == 20, "size of section structure is wrong");
0125 static_assert(alignof(HafasJourneyResponseSection) == 1, "broken alignment for binary response struct");
0126 
0127 // values for HafasJourneyResponseSection::type
0128 namespace HafasJourneyResponseSectionMode {
0129 enum Mode {
0130     Walk = 1,
0131     PublicTransport = 2,
0132     Transfer1 = 3,
0133     Transfer2 = 4
0134 };
0135 }
0136 
0137 struct HafasJourneyResponseSectionDetail
0138 {
0139     // see above for format
0140     uint16_t expectedDepartureTime;
0141     uint16_t expectedArrivalTime;
0142     // indexes into string table
0143     uint16_t expectedDeparturePlatformStr;
0144     uint16_t expectedArrivalPlatformStr;
0145     uint16_t flags;
0146     uint16_t padding1;
0147     uint16_t stopIndex;
0148     uint16_t numStops;
0149 };
0150 static_assert(alignof(HafasJourneyResponseSectionDetail) == 1, "broken alignment for binary response struct");
0151 
0152 // occurs multiple times at offset HafasJourneyResponseHeader::stationTableOffset
0153 struct HafasJourneyResponseStation
0154 {
0155     // station name as index into the string table
0156     uint16_t nameStr;
0157     uint32_t id;
0158     // geo coordinates * 1000000
0159     int32_t longitude;
0160     int32_t latitude;
0161 };
0162 static_assert(sizeof(HafasJourneyResponseStation) == 14, "size of station structure is wrong");
0163 static_assert(alignof(HafasJourneyResponseStation) == 1, "broken alignment for binary response struct");
0164 
0165 // attribute table entries: pair if string table indexes
0166 // can be iterated until atEnd() returns @c true
0167 struct HafasJourneyResponseAttribute
0168 {
0169     uint16_t keyStr;
0170     uint16_t valueStr;
0171 
0172     inline bool atEnd() const { return keyStr == 0; }
0173 };
0174 static_assert(sizeof(HafasJourneyResponseAttribute) == 4, "size of attribute structure is wrong");
0175 static_assert(alignof(HafasJourneyResponseAttribute) == 1, "broken alignment for binary response struct");
0176 
0177 struct HafasJourneyResponseDisruption;
0178 // disruption table
0179 struct HafasJourneyResponseDisruptionTable
0180 {
0181     uint16_t padding1; // version?
0182     // followed by one uint16_t for each journey, containing the offset of the first HafasJourneyResponseDisruption entry for that journey
0183 
0184     /** First disruption entry for journey @p journeyIdx, or @c nullptr if none present. */
0185     inline const HafasJourneyResponseDisruption* firstDisruptionForJourney(uint16_t journeyIdx) const {
0186         const auto offset = *reinterpret_cast<const uint16_t*>(reinterpret_cast<const char*>(this) + sizeof(HafasJourneyResponseDisruptionTable) + journeyIdx * sizeof(uint16_t));
0187         return offset ? reinterpret_cast<const HafasJourneyResponseDisruption*>(reinterpret_cast<const char*>(this) + offset) : nullptr;
0188     }
0189 };
0190 static_assert(alignof(HafasJourneyResponseDisruptionTable) == 1, "broken alignment for binary response struct");
0191 
0192 // intermediate stops, located at HafasJourneyResponseDetailsHeader::stopsOffset
0193 struct HafasJourneyResponseStop
0194 {
0195     uint16_t scheduledDepartureTime;
0196     uint16_t scheduledArrivalTime;
0197     uint16_t scheduledDeparturePlatformStr;
0198     uint16_t scheduledArrivalPlatformStr;
0199     uint32_t unknown1;
0200     uint16_t expectedDepartureTime;
0201     uint16_t expectedArrivalTime;
0202     uint16_t expectedDeparturePlatformStr;
0203     uint16_t expectedArrivalPlatformStr;
0204     uint16_t flags; // 0x10 arrival cancelled, 0x20 departure cancelled
0205     uint16_t unknown2;
0206     uint16_t stationIdx;
0207 };
0208 
0209 // disruption table entry
0210 struct HafasJourneyResponseDisruption
0211 {
0212     uint16_t padding1;
0213     // index of the affected section
0214     uint16_t section;
0215     uint16_t bitmask;
0216     // indexes into string table
0217     uint16_t startStr;
0218     uint16_t endStr;
0219     uint16_t idStr;
0220     uint16_t titleStr;
0221     uint16_t messageStr;
0222     // offset from start of disruption table to the next element
0223     uint16_t nextOffset;
0224     // index into attribute table
0225     uint16_t disruptionAttributeIndex;
0226 
0227     /** Next disruption entry in the current disruption list. */
0228     inline const HafasJourneyResponseDisruption* next(const HafasJourneyResponseDisruptionTable *disruptionTable) const
0229     {
0230         return nextOffset ? reinterpret_cast<const HafasJourneyResponseDisruption*>(reinterpret_cast<const char*>(disruptionTable) + nextOffset) : nullptr;
0231     }
0232 };
0233 static_assert(alignof(HafasJourneyResponseDisruption) == 1, "broken alignment for binary response struct");
0234 
0235 #pragma pack(pop)
0236 
0237 // occurs once, at offset HafasJourneyResponseHeader::stringTableOffset
0238 class HafasJourneyResponseStringTable
0239 {
0240 public:
0241     explicit inline HafasJourneyResponseStringTable(const QByteArray &data, uint32_t stringTableOffset, uint16_t codecIdx) :
0242         m_data(data.constData() + stringTableOffset)
0243     {
0244         m_codec = QStringDecoder(QByteArray(m_data + codecIdx).constData());
0245     }
0246 
0247     inline QString lookup(uint16_t index)
0248     {
0249         // null terminated strings in the given codec
0250         return m_codec.decode(m_data + index);
0251     }
0252 
0253 private:
0254     const char *m_data;
0255     QStringDecoder m_codec;
0256 };
0257 
0258 namespace HafasJourneyResponse {
0259 /** Returns the attribute at @p attributeIdx. */
0260 inline const HafasJourneyResponseAttribute* attribute(const char *data, const HafasJourneyResponseExtendedHeader *extHeader, uint16_t attributeIdx)
0261 {
0262     return reinterpret_cast<const HafasJourneyResponseAttribute*>(data + extHeader->attributesOffset + attributeIdx * sizeof(HafasJourneyResponseAttribute));
0263 }
0264 
0265 /** Returns the disruption table. */
0266 inline const HafasJourneyResponseDisruptionTable* disruptionTable(const char *data, const HafasJourneyResponseExtendedHeader *extHeader)
0267 {
0268     return reinterpret_cast<const HafasJourneyResponseDisruptionTable*>(data + extHeader->disruptionTableOffset);
0269 }
0270 
0271 }
0272 
0273 }
0274 
0275 #endif // KPUBLICTRANSPORT_HAFASJOURNEYRESPONSE_P_H