From 4b893ca726bda760088d07b2d31b0099b433e1a4 Mon Sep 17 00:00:00 2001 From: Kilian Hofmann Date: Mon, 3 Jan 2022 04:56:27 +0100 Subject: [PATCH] Simulator database for X-Plane --- .clang-format | 2 +- file/include/config.h | 47 +++++ file/include/gate.h | 56 ++++++ file/include/runway.h | 114 ++++++++++++ file/include/simulatorDatabase.h | 191 +++++++++++++++++++ file/include/stringExtensions.h | 53 ++++++ file/include/util.h | 306 +++++++++++++++++++++++++++++++ websocket/include/types.h | 4 +- websocket/include/websocket.h | 22 +-- websocket/websocket.cpp | 207 ++------------------- xplugin/CMakeLists.txt | 6 + xplugin/include/main.h | 15 +- xplugin/include/makeRwysXP.h | 30 +++ xplugin/main.cpp | 50 ++++- xplugin/makeRwysXP.cpp | 177 ++++++++++++++++++ xplugin/xPluginWin.cpp | 4 +- 16 files changed, 1054 insertions(+), 230 deletions(-) create mode 100644 file/include/config.h create mode 100644 file/include/gate.h create mode 100644 file/include/runway.h create mode 100644 file/include/simulatorDatabase.h create mode 100644 file/include/stringExtensions.h create mode 100644 file/include/util.h create mode 100644 xplugin/include/makeRwysXP.h create mode 100644 xplugin/makeRwysXP.cpp diff --git a/.clang-format b/.clang-format index edb326d..51d2aa3 100644 --- a/.clang-format +++ b/.clang-format @@ -29,7 +29,7 @@ IndentCaseLabels: true IndentFunctionDeclarationAfterType: true IndentWidth: 4 MaxEmptyLinesToKeep: 2 -NamespaceIndentation: None +NamespaceIndentation: All ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true ObjCBlockIndentWidth: 4 diff --git a/file/include/config.h b/file/include/config.h new file mode 100644 index 0000000..cbcecfd --- /dev/null +++ b/file/include/config.h @@ -0,0 +1,47 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_CONFIG_H +#define GERMANAIRLINESVA_GACONNECTOR_CONFIG_H + +#include "stringExtensions.h" + +#include + +#include +#include +#include +#include + +namespace config +{ + static inline std::map + readConfig(const std::string &file) + { + std::ifstream config(file); + std::map settings; + + std::string line; + while (std::getline(config, line)) { + std::vector fields = split(line, '='); + if (fields.size() >= 2) { + trim(fields[0]); + trim(fields[1]); + settings[fields[0]] = fields[1]; + } + } + + config.close(); + return settings; + } + + + static inline void + writeConfig(const std::map &config, + const std::string &file) + { + std::ofstream cfg(file); + for (const std::pair &entry : config) { + cfg << entry.first << '=' << entry.second << '\n'; + } + cfg.close(); + } +} // namespace config +#endif diff --git a/file/include/gate.h b/file/include/gate.h new file mode 100644 index 0000000..ed53ed8 --- /dev/null +++ b/file/include/gate.h @@ -0,0 +1,56 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_GATE_H +#define GERMANAIRLINESVA_GACONNECTOR_GATE_H + +#include +#include +#include +#include +#include +#include + +/* + * Representation of X-Plane gate + * Heading in degrees (0...360) + * + * Length in bytes: 18 + length of designator + * Designator must be null terminated + * + * UINT8 | CHAR[] | DOUBLE | DOUBLE | UINT8 + * ------+------------+--------+--------+------ + * LEN | DESIGNATOR | LAT | LON | WIDTH + */ +class Gate +{ +private: + std::string designator; + double latitude; + double longitude; + std::vector file; + +public: + Gate(const std::string &designator, double latitude, double longitude) + { + + this->designator = designator; + this->latitude = latitude; + this->longitude = longitude; + + file = std::vector(18 + this->designator.length(), 0); + std::uint8_t *bufPtr = file.data(); + memset(bufPtr, + static_cast(this->designator.length()), + sizeof(std::uint8_t)); + bufPtr++; // Designator length + memcpy(bufPtr, this->designator.c_str(), this->designator.length()); + bufPtr += + this->designator.length() + 1; // Designator plus null termination + memcpy(bufPtr, &this->latitude, sizeof(this->latitude)); + bufPtr += 8; // Latitude + memcpy(bufPtr, &this->longitude, sizeof(this->longitude)); + } + + std::uint8_t *getBinaryData() { return this->file.data(); } + std::size_t getBinaryLength() { return this->file.size(); } +}; + +#endif \ No newline at end of file diff --git a/file/include/runway.h b/file/include/runway.h new file mode 100644 index 0000000..79e7ed9 --- /dev/null +++ b/file/include/runway.h @@ -0,0 +1,114 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_RUNWAY_H +#define GERMANAIRLINESVA_GACONNECTOR_RUNWAY_H + +#include +#include +#include +#include +#include + +#include "util.h" + +/* + * Representation of one X-Plane runway with supplementary information + * Heading in degrees (0...360) + * Width and length in feet + * + * Length in bytes: 23 + length of designator + * Designator must be null terminated + * + * UINT8 | CHAR[] | DOUBLE | DOUBLE | UINT8 | UINT16 | UINT16 + * ------+------------+--------+--------+-------+--------+------- + * LEN | DESIGNATOR | LAT | LON | WIDTH | LENGTH | TRUHDG + */ +class Runway +{ +private: + std::string designator; + double latitudeStart; + double longitudeStart; + std::uint8_t width; + std::uint16_t length; + std::uint16_t trueHeading; + std::vector file; + +public: + Runway(std::string designator, + double latitudeStart, + double longitudeStart, + double latitudeEnd, + double longitudeEnd, + double width) + { + this->designator = std::move(designator); + this->latitudeStart = latitudeStart; + this->longitudeStart = longitudeStart; + this->width = (std::uint8_t)std::round(util::to_feet(width)); + double dist = util::distanceEarth(latitudeStart, + longitudeStart, + latitudeEnd, + longitudeEnd); + this->length = (std::uint16_t)std::round(util::to_feet(dist)); + this->trueHeading = + (std::uint16_t)std::round(util::bearing(latitudeStart, + longitudeStart, + latitudeEnd, + longitudeEnd)); + + file = std::vector(23 + this->designator.length(), 0); + std::uint8_t *bufPtr = file.data(); + memset(bufPtr, + static_cast(this->designator.length()), + sizeof(std::uint8_t)); + bufPtr++; + memcpy(bufPtr, this->designator.c_str(), this->designator.length()); + bufPtr += this->designator.length() + 1; + memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart)); + bufPtr += sizeof(this->latitudeStart); + memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart)); + bufPtr += sizeof(this->longitudeStart); + memcpy(bufPtr, &this->width, sizeof(this->width)); + bufPtr += sizeof(this->width); + memcpy(bufPtr, &this->length, sizeof(this->length)); + bufPtr += sizeof(this->length); + memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading)); + } + + Runway(std::string designator, + double latitudeStart, + double longitudeStart, + std::uint8_t width, + std::uint16_t length, + std::uint16_t trueHeading) + { + this->designator = std::move(designator); + this->latitudeStart = latitudeStart; + this->longitudeStart = longitudeStart; + this->width = width; + this->length = length; + this->trueHeading = trueHeading; + + file = std::vector(23 + this->designator.length(), 0); + std::uint8_t *bufPtr = file.data(); + memset(bufPtr, + static_cast(this->designator.length()), + sizeof(std::uint8_t)); + bufPtr++; + memcpy(bufPtr, this->designator.c_str(), this->designator.length()); + bufPtr += this->designator.length() + 1; + memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart)); + bufPtr += sizeof(this->latitudeStart); + memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart)); + bufPtr += sizeof(this->longitudeStart); + memcpy(bufPtr, &this->width, sizeof(this->width)); + bufPtr += sizeof(this->width); + memcpy(bufPtr, &this->length, sizeof(this->length)); + bufPtr += sizeof(this->length); + memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading)); + } + + std::uint8_t *getBinaryData() { return this->file.data(); } + std::size_t getBinaryLength() { return this->file.size(); } +}; + +#endif \ No newline at end of file diff --git a/file/include/simulatorDatabase.h b/file/include/simulatorDatabase.h new file mode 100644 index 0000000..f109668 --- /dev/null +++ b/file/include/simulatorDatabase.h @@ -0,0 +1,191 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_SIMULATORDATABASE_H +#define GERMANAIRLINESVA_GACONNECTOR_SIMULATORDATABASE_H + +#include "gate.h" +#include "runway.h" +#include "stringExtensions.h" + +#define CURRENT_VERSION 1 + +#include +#include +#include +#include +#include +#include +#include + +/* + * Header + * + * CHAR[5] | UINT8 + * --------+-------- + * VGAS | VERSION + */ +/* + * Airport + * + * UINT8 | CHAR[] | UINT16 | GATE[] | UINT8 | RUNWAY[] + * --------+--------+----------+--------+---------+--------- + * STRLEN | ICAO | NUMGATES | GATES | NUMRWYS | RUNWAYS + */ + +namespace simulatorDatabase +{ + static inline void toFile( + std::map, std::vector>> + &airports, + const std::string &file) + { + std::uint8_t null = 0; + std::ofstream out(file, std::fstream::binary); + + // File Header + std::uint8_t header[] = {'V', 'G', 'A', 'S', 0, CURRENT_VERSION}; + out.write(reinterpret_cast(header), 6); + // Num Airports + std::uint16_t numAirports = airports.size(); + out.write(reinterpret_cast(&numAirports), + sizeof(numAirports)); + // Airport + for (const std::pair, std::vector>> + &airport : airports) { + std::string icao = airport.first; + std::vector gates = airport.second.first; + std::vector runways = airport.second.second; + // ICAO + std::uint8_t icaoLength = icao.length(); + out.write(reinterpret_cast(&icaoLength), + sizeof(icaoLength)); + out.write(icao.c_str(), icaoLength); + out.write(reinterpret_cast(&null), sizeof(null)); + // Gates + std::uint16_t numGates = gates.size(); + out.write(reinterpret_cast(&numGates), + sizeof(numGates)); + for (Gate &gate : gates) { + out.write(reinterpret_cast(gate.getBinaryData()), + (std::streamsize)gate.getBinaryLength()); + } + // Runways + std::uint8_t numRunways = runways.size(); + out.write(reinterpret_cast(&numRunways), + sizeof(numRunways)); + for (Runway &runway : runways) { + out.write( + reinterpret_cast(runway.getBinaryData()), + (std::streamsize)runway.getBinaryLength()); + } + } + out.close(); + } + + static inline std::map, std::vector>> + readVersion1(std::ifstream &in) + { + std::map, std::vector>> + airports; + + std::uint16_t numAirports; + in.read(reinterpret_cast(&numAirports), sizeof(numAirports)); + + for (int i = 0; i < numAirports; i++) { + // ICAO + std::uint8_t icaoLength; + in.read(reinterpret_cast(&icaoLength), sizeof(icaoLength)); + char *icao = + static_cast(calloc(icaoLength + 1, sizeof(char))); + in.read(icao, icaoLength + 1); + // Gates + std::uint16_t numGates; + in.read(reinterpret_cast(&numGates), sizeof(numGates)); + for (int j = 0; j < numGates; j++) { + // ICAO + std::uint8_t designatorLength; + in.read(reinterpret_cast(&designatorLength), + sizeof(designatorLength)); + char *designator = static_cast( + calloc(designatorLength + 1, sizeof(char))); + in.read(designator, designatorLength + 1); + // Latitude + double latitude; + in.read(reinterpret_cast(&latitude), sizeof(latitude)); + // Latitude + double longitude; + in.read(reinterpret_cast(&longitude), + sizeof(longitude)); + + airports[icao].first.emplace_back(designator, + latitude, + longitude); + } + // Runways + std::uint8_t numRunways; + in.read(reinterpret_cast(&numRunways), sizeof(numRunways)); + for (int j = 0; j < numRunways; j++) { + // ICAO + std::uint8_t designatorLength; + in.read(reinterpret_cast(&designatorLength), + sizeof(designatorLength)); + char *designator = static_cast( + calloc(designatorLength + 1, sizeof(char))); + in.read(designator, designatorLength + 1); + // Latitude + double latitude; + in.read(reinterpret_cast(&latitude), sizeof(latitude)); + // Latitude + double longitude; + in.read(reinterpret_cast(&longitude), + sizeof(longitude)); + // Width + std::uint8_t width; + in.read(reinterpret_cast(&width), sizeof(width)); + // Length + std::uint16_t length; + in.read(reinterpret_cast(&length), sizeof(length)); + // True Heading + std::uint16_t trueHeading; + in.read(reinterpret_cast(&trueHeading), + sizeof(trueHeading)); + + airports[icao].second.emplace_back(designator, + latitude, + longitude, + width, + length, + trueHeading); + } + } + + in.close(); + + return airports; + } + + static inline std::map, std::vector>> + fromFile(const std::string &file) + { + std::map, std::vector>> + airports; + std::ifstream in(file); + + // File Header + char ident[5]; + in.read(ident, 5); + if (strcmp(ident, "VGAS") != 0) { + throw std::invalid_argument("Wrong file"); + } + std::uint8_t version; + in.read(reinterpret_cast(&version), 1); + + if (version == 1) { + return readVersion1(in); + } + return airports; + } +} // namespace simulatorDatabase + +#endif diff --git a/file/include/stringExtensions.h b/file/include/stringExtensions.h new file mode 100644 index 0000000..73cd052 --- /dev/null +++ b/file/include/stringExtensions.h @@ -0,0 +1,53 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_STRINGEXTENSIONS_H +#define GERMANAIRLINESVA_GACONNECTOR_STRINGEXTENSIONS_H + +#include +#include +#include +#include + +// trim from start (in place) +static inline void ltrim(std::string &s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +} + +// trim from end (in place) +static inline void rtrim(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), + s.rend(), + [](unsigned char ch) { return !std::isspace(ch); }) + .base(), + s.end()); +} + +static inline std::string rtrim_copy(std::string s) +{ + rtrim(s); + return s; +} + +// trim from both ends (in place) +static inline void trim(std::string &s) +{ + ltrim(s); + rtrim(s); +} + +static inline std::vector split(const std::string &s, char delim) +{ + std::vector result; + std::stringstream ss(s); + std::string item; + + while (getline(ss, item, delim)) { + result.push_back(item); + } + + return result; +} + +#endif \ No newline at end of file diff --git a/file/include/util.h b/file/include/util.h new file mode 100644 index 0000000..a355fb1 --- /dev/null +++ b/file/include/util.h @@ -0,0 +1,306 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_UTIL_H +#define GERMANAIRLINESVA_GACONNECTOR_UTIL_H + +#ifdef IBM +#define WIN32_LEAN_AND_MEAN +#endif + +#define BUFSIZE 1024 +#define MD5LEN 16 +#define EARTH_M 6371000 + +#ifdef IBM +// clang-format off +#include +#include +// clang-format on +#endif +#ifdef APL +#include +#include +#endif +#ifdef LIN +#include +#include +#endif + +#if defined APL || defined LIN +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +namespace util +{ + static inline double to_feet(double value) { return value * 3.280839895; } + + static inline double to_degrees(double value) { return value * 180 / M_PI; } + + static inline double to_radians(double value) { return value * M_PI / 180; } + + static inline double normalize(double value) + { + return fmod(value + 360, 360); + } + + static inline double bearing(double fromLatitude, + double fromLongitude, + double toLatitude, + double toLongitude) + { + double y = sin(to_radians(toLongitude) - to_radians(fromLongitude)) * + cos(to_radians(toLatitude)); + double x = cos(to_radians(fromLatitude)) * sin(to_radians(toLatitude)) - + sin(to_radians(fromLatitude)) * cos(to_radians(toLatitude)) * + cos(to_radians(toLongitude) - to_radians(fromLongitude)); + + return normalize(to_degrees(atan2(y, x))); + } + + static inline double distanceEarth(double fromLatitude, + double fromLongitude, + double toLatitude, + double toLongitude) + { + double lat1r, lon1r, lat2r, lon2r, u, v; + lat1r = to_radians(fromLatitude); + lon1r = to_radians(fromLongitude); + lat2r = to_radians(toLatitude); + lon2r = to_radians(toLongitude); + u = sin((lat2r - lat1r) / 2); + v = sin((lon2r - lon1r) / 2); + return 2.0 * EARTH_M * + asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v)); + } + + template + static inline std::vector + select_T(const std::vector &inVec, + std::function predicate) + { + std::vector result; + copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate); + return result; + } + +#if defined APL || defined LIN + static unsigned long get_size_by_fd(int fd) + { + struct stat buf { + }; + if (fstat(fd, &buf) < 0) + return 0; + return buf.st_size; + } +#endif + + static void to_hex(const char *hash, char *buffer) + { + for (int i = 0; i < MD5LEN; i++) { + if (buffer != nullptr) { + sprintf(&buffer[2 * i], "%02x", hash[i] & 0xff); + } + } + } + +#ifdef IBM + static inline int + generateMD5(const char *filepath, + char *lastHash, + const std::function toLog) + { + BOOL bResult = FALSE; + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + HANDLE hFile; + BYTE rgbFile[BUFSIZE] = {0}; + DWORD cbRead = 0; + BYTE rgbHash[MD5LEN] = {0}; + DWORD cbHash = 0; + + // Logic to check usage goes here. + hFile = CreateFile(filepath, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + nullptr); + + // Get handle to the crypto provider + if (!CryptAcquireContext(&hProv, + nullptr, + nullptr, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + std::stringstream debug_msg; + debug_msg << "CryptAcquireContext returned with error " + << GetLastError(); + toLog(debug_msg.str()); + + CloseHandle(hFile); + return 1; + } + + if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { + std::stringstream debug_msg; + debug_msg << "CryptCreateHash returned with error " + << GetLastError(); + toLog(debug_msg.str()); + + CloseHandle(hFile); + CryptReleaseContext(hProv, 0); + return 1; + } + + while ( + (bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, nullptr))) { + if (0 == cbRead) { + break; + } + + if (!CryptHashData(hHash, rgbFile, cbRead, 0)) { + std::stringstream debug_msg; + debug_msg << "CryptHashData returned with error " + << GetLastError(); + toLog(debug_msg.str()); + + CryptReleaseContext(hProv, 0); + CryptDestroyHash(hHash); + CloseHandle(hFile); + return 1; + } + } + + if (!bResult) { + std::stringstream debug_msg; + debug_msg << "ReadFile returned with error " << GetLastError(); + toLog(debug_msg.str()); + + CryptReleaseContext(hProv, 0); + CryptDestroyHash(hHash); + CloseHandle(hFile); + return 1; + } + + cbHash = MD5LEN; + if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) { + to_hex((char *)rgbHash, lastHash); + } else { + std::stringstream debug_msg; + debug_msg << "CryptGetHashParam returned with error " + << GetLastError(); + toLog(debug_msg.str()); + } + + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + CloseHandle(hFile); + + return 0; + } +#endif +#ifdef APL +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" + static inline int + generateMD5(const char *filepath, + char *lastHash, + const std::function &toLog) + { + int file_descript; + unsigned long file_size; + char *file_buffer; + unsigned char result[MD5LEN]; + + file_descript = open(filepath, O_RDONLY); + if (file_descript < 0) + return 1; + + file_size = get_size_by_fd(file_descript); + + file_buffer = + (char *)mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0); + + CC_MD5_CTX context; + CC_MD5_Init(&context); + CC_MD5_Update(&context, file_buffer, (CC_LONG)file_size); + CC_MD5_Final(result, &context); + + munmap(file_buffer, file_size); + close(file_descript); + + to_hex((char *)result, lastHash); + return 0; + } +#pragma clang diagnostic pop +#endif +#ifdef LIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" + static inline int + generateMD5(const char *filepath, + char *buffer, + const std::function &toLog) + { + int file_descriptor; + unsigned long file_size; + char *file_buffer; + unsigned char result[MD5LEN]; + + file_descriptor = open(filepath, O_RDONLY); + if (file_descriptor < 0) + return 1; + + file_size = get_size_by_fd(file_descriptor); + if (file_size == 0) + return 1; + + file_buffer = (char *) + mmap(nullptr, file_size, PROT_READ, MAP_SHARED, file_descriptor, 0); + + MD5((unsigned char *)file_buffer, file_size, result); + + munmap(file_buffer, file_size); + close(file_descriptor); + + to_hex((char *)result, buffer); + return 0; + } +#pragma clang diagnostic pop + +#endif + + static inline void setThreadName(const std::string &name) + { +#ifdef APL + // + // Apple reserves 16 bytes for its thread names + // Notice that the Apple version of pthread_setname_np + // does not take a pthread_t argument + // + pthread_setname_np(name.substr(0, 63).c_str()); +#endif +#ifdef LIN + // + // Linux only reserves 16 bytes for its thread names + // See prctl and PR_SET_NAME property in + // http://man7.org/linux/man-pages/man2/prctl.2.html + // + pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); +#endif +#ifdef IBM + SetThreadName(-1, name.c_str()); +#endif + } + +} // namespace util + +#endif diff --git a/websocket/include/types.h b/websocket/include/types.h index 71c0419..8f707ed 100644 --- a/websocket/include/types.h +++ b/websocket/include/types.h @@ -1,5 +1,5 @@ -#ifndef GERMANAIRLINESVA_GACONNECTOR_TYPES_H -#define GERMANAIRLINESVA_GACONNECTOR_TYPES_H +#ifndef GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_TYPES_H +#define GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_TYPES_H #include diff --git a/websocket/include/websocket.h b/websocket/include/websocket.h index 48d6f71..e83f12d 100644 --- a/websocket/include/websocket.h +++ b/websocket/include/websocket.h @@ -10,21 +10,7 @@ #include #include "types.h" - -#ifdef IBM -// clang-format off -#include -#include -// clang-format on -#endif -#ifdef APL -#include -#include -#endif -#ifdef LIN -#include -#include -#endif +#include "util.h" #include #include @@ -51,12 +37,6 @@ public: ix::WebSocket &ws, const ix::WebSocketMessagePtr &msg); void sendData(data d); - int generateMD5(const char *filepath); }; -#if defined APL || defined LIN -unsigned long get_size_by_fd(int fd); -#endif -void to_hex(const char *hash, char *buffer); - #endif diff --git a/websocket/websocket.cpp b/websocket/websocket.cpp index 5feafbc..7576219 100644 --- a/websocket/websocket.cpp +++ b/websocket/websocket.cpp @@ -97,197 +97,28 @@ void websocket::onClientMessageCallback( void websocket::sendData(data d) { - char *hash = (char *)calloc(2 * MD5LEN + 1, sizeof(char)); - if (hash != nullptr) { - if (strcmp(d.path, lastPath) != 0) { - strcpy(lastPath, d.path); - if (generateMD5(d.path)) { - strcpy(lastHash, "NOT SET"); - } - } - - nlohmann::json json = { - {"altitude", d.alt}, - {"vs", d.vs}, - {"ias", d.ias}, - {"magHdg", d.magHeading}, - {"truHdg", d.truHdg}, - {"totFuel", d.totFuelKg}, - {"fuelFlow", d.ff}, - {"hash", lastHash}, - }; - - { - std::lock_guard lock(wsLock); - if (webSocket != nullptr) { - webSocket->send(json.dump(), false); - } - free(hash); - } - } -} - -#ifdef IBM -int websocket::generateMD5(const char *filepath) -{ - BOOL bResult = FALSE; - HCRYPTPROV hProv = 0; - HCRYPTHASH hHash = 0; - HANDLE hFile = nullptr; - BYTE rgbFile[BUFSIZE] = {0}; - DWORD cbRead = 0; - BYTE rgbHash[MD5LEN] = {0}; - DWORD cbHash = 0; - - // Logic to check usage goes here. - hFile = CreateFile(filepath, - GENERIC_READ, - FILE_SHARE_READ, - nullptr, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - nullptr); - - // Get handle to the crypto provider - if (!CryptAcquireContext(&hProv, - nullptr, - nullptr, - PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - std::stringstream debug_msg; - debug_msg << "CryptAcquireContext returned with error " - << GetLastError(); - toLog(debug_msg.str()); - - CloseHandle(hFile); - return 1; - } - - if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { - std::stringstream debug_msg; - debug_msg << "CryptCreateHash returned with error " << GetLastError(); - toLog(debug_msg.str()); - - CloseHandle(hFile); - CryptReleaseContext(hProv, 0); - return 1; - } - - while ((bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, nullptr))) { - if (0 == cbRead) { - break; - } - - if (!CryptHashData(hHash, rgbFile, cbRead, 0)) { - std::stringstream debug_msg; - debug_msg << "CryptHashData returned with error " << GetLastError(); - toLog(debug_msg.str()); - - CryptReleaseContext(hProv, 0); - CryptDestroyHash(hHash); - CloseHandle(hFile); - return 1; + if (strcmp(d.path, lastPath) != 0) { + strcpy(lastPath, d.path); + if (util::generateMD5(d.path, lastHash, toLog)) { + strcpy(lastHash, "NOT SET"); } } - if (!bResult) { - std::stringstream debug_msg; - debug_msg << "ReadFile returned with error " << GetLastError(); - toLog(debug_msg.str()); - - CryptReleaseContext(hProv, 0); - CryptDestroyHash(hHash); - CloseHandle(hFile); - return 1; - } - - cbHash = MD5LEN; - if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) { - to_hex((char *)rgbHash, lastHash); - } else { - std::stringstream debug_msg; - debug_msg << "CryptGetHashParam returned with error " << GetLastError(); - toLog(debug_msg.str()); - } - - CryptDestroyHash(hHash); - CryptReleaseContext(hProv, 0); - CloseHandle(hFile); - - return 0; -} -#endif -#ifdef APL -int websocket::generateMD5(const char *filepath) -{ - int file_descript; - unsigned long file_size; - char *file_buffer; - unsigned char result[MD5LEN]; - - file_descript = open(filepath, O_RDONLY); - if (file_descript < 0) - return 1; - - file_size = get_size_by_fd(file_descript); - - file_buffer = - (char *)mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0); - - CC_MD5_CTX context; - CC_MD5_Init(&context); - CC_MD5_Update(&context, file_buffer, (CC_LONG)file_size); - CC_MD5_Final(result, &context); - - munmap(file_buffer, file_size); - close(file_descript); - - to_hex((char *)result, lastHash); - return 0; -} -#endif -#ifdef LIN -int websocket::generateMD5(const char *filepath) -{ - int file_descriptor; - unsigned long file_size; - char *file_buffer; - unsigned char result[MD5LEN]; - - file_descriptor = open(filepath, O_RDONLY); - if (file_descriptor < 0) - return 1; - - file_size = get_size_by_fd(file_descriptor); - - file_buffer = (char *) - mmap(nullptr, file_size, PROT_READ, MAP_SHARED, file_descriptor, 0); - - MD5((unsigned char *)file_buffer, file_size, result); - - munmap(file_buffer, file_size); - close(file_descriptor); - - to_hex((char *)result, lastHash); - return 0; -} -#endif - -#if defined APL || defined LIN -unsigned long get_size_by_fd(int fd) -{ - struct stat buf { + nlohmann::json json = { + {"altitude", d.alt}, + {"vs", d.vs}, + {"ias", d.ias}, + {"magHdg", d.magHeading}, + {"truHdg", d.truHdg}, + {"totFuel", d.totFuelKg}, + {"fuelFlow", d.ff}, + {"hash", lastHash}, }; - if (fstat(fd, &buf) < 0) - exit(-1); - return buf.st_size; -} -#endif -void to_hex(const char *hash, char *buffer) -{ - for (int i = 0; i < MD5LEN; i++) { - if (buffer != nullptr) { - sprintf(&buffer[2 * i], "%02x", hash[i] & 0xff); + + { + std::lock_guard lock(wsLock); + if (webSocket != nullptr) { + webSocket->send(json.dump(), false); } } -} \ No newline at end of file +} diff --git a/xplugin/CMakeLists.txt b/xplugin/CMakeLists.txt index 102e64b..279bf97 100644 --- a/xplugin/CMakeLists.txt +++ b/xplugin/CMakeLists.txt @@ -7,10 +7,13 @@ file(GLOB ixwebsocket CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/ixwebsocket/*.cpp) file(GLOB websocket CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/websocket/*.cpp) +file(GLOB file CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/file/*.cpp) add_library(germanairlinesva_xplugin SHARED ${ixwebsocket} ${websocket} + ${file} + makeRwysXP.cpp main.cpp ) @@ -19,6 +22,7 @@ target_include_directories(germanairlinesva_xplugin PRIVATE ${CMAKE_SOURCE_DIR}/websocket/include ${CMAKE_SOURCE_DIR}/XPSDK/CHeaders ${CMAKE_SOURCE_DIR}/nlohmann + ${CMAKE_SOURCE_DIR}/file/include ) set_target_properties(germanairlinesva_xplugin PROPERTIES @@ -28,9 +32,11 @@ set_target_properties(germanairlinesva_xplugin PROPERTIES target_compile_definitions(germanairlinesva_xplugin PRIVATE XPLM200 XPLM210 + _USE_MATH_DEFINES ) target_compile_options(germanairlinesva_xplugin PRIVATE -Wall + -Wextra -pedantic -fvisibility=hidden ) diff --git a/xplugin/include/main.h b/xplugin/include/main.h index 096b67a..7079f0e 100644 --- a/xplugin/include/main.h +++ b/xplugin/include/main.h @@ -1,13 +1,5 @@ -#ifndef GERMANAIRLINESVA_GACONNECTOR_MAIN_H -#define GERMANAIRLINESVA_GACONNECTOR_MAIN_H - -#ifdef IBM -#define WIN32_LEAN_AND_MEAN -#define _USE_MATH_DEFINES -#endif - -#define BUFSIZE 1024 -#define MD5LEN 16 +#ifndef GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAIN_H +#define GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAIN_H #include "XPLM/XPLMDataAccess.h" #include "XPLM/XPLMGraphics.h" @@ -16,6 +8,9 @@ #include "XPLM/XPLMProcessing.h" #include "XPLM/XPLMUtilities.h" +#include "config.h" +#include "makeRwysXP.h" +#include "simulatorDatabase.h" #include "websocket.h" #include diff --git a/xplugin/include/makeRwysXP.h b/xplugin/include/makeRwysXP.h new file mode 100644 index 0000000..8cecce3 --- /dev/null +++ b/xplugin/include/makeRwysXP.h @@ -0,0 +1,30 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAKERWYSXP_H +#define GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAKERWYSXP_H + +#include +#include +#include +#include + +#include "gate.h" +#include "runway.h" +#include "stringExtensions.h" +#include "util.h" + +int scan(const char *defaultFile, + const char *sceneryPack, + const char *logFile, + std::map, std::vector>> &airports); + +void makeAirport( + const std::string &kind, + std::ifstream *infile, + std::map, std::vector>> + *airports, + std::ofstream *logfile); +void makeGate15(std::vector *gates, std::vector fields); +void makeRunway(std::vector *runways, std::vector fields); +void makeGate1300(std::vector *gates, std::vector fields); + +#endif \ No newline at end of file diff --git a/xplugin/main.cpp b/xplugin/main.cpp index ddd374f..6d5dab1 100644 --- a/xplugin/main.cpp +++ b/xplugin/main.cpp @@ -9,6 +9,8 @@ std::queue> &messageQueue() std::thread serverThread; std::atomic wantsExit; +std::map configuration; + websocket *connector; /* Datarefs */ @@ -54,9 +56,9 @@ PLUGIN_API int XPluginStart(char *outName, char *outSig, char *outDesc) /* First we must fill in the passed-in buffers to describe our * plugin to the plugin-system. */ - strcpy(outName, "WebSocketTestXPlane"); - strcpy(outSig, "de.german-airlines.WebSocketTestXPlane"); - strcpy(outDesc, "WebSocketTestXPlane"); + strcpy(outName, "GAConnector"); + strcpy(outSig, "de.german-airlines.GAConnector"); + strcpy(outDesc, "GAConnector"); /* Flight Loop */ XPLMRegisterFlightLoopCallback(flightLoop, -1, nullptr); @@ -111,11 +113,41 @@ PLUGIN_API int XPluginStart(char *outName, char *outSig, char *outDesc) toLog(e.what()); return 0; } + toLog("WebSocket started"); + + configuration = + config::readConfig("Resources/plugins/GAConnector/config.cfg"); + toLog("Config loaded"); + + char hash[2 * MD5LEN + 1] = ""; + if (util::generateMD5("Custom Scenery/scenery_packs.ini", hash, toLog) == + 0) { + std::map, std::vector>> + airports; + + if (strcmp(configuration["scenery"].c_str(), hash) != 0) { + scan("Resources/default scenery/default apt dat/Earth nav " + "data/apt.dat", + "Custom Scenery/scenery_packs.ini", + "Resources/plugins/GAConnector/log.txt", + airports); + simulatorDatabase::toFile(airports, + "Resources/plugins/GAConnector/sim.bin"); + + configuration["scenery"] = hash; + config::writeConfig(configuration, + "Resources/plugins/GAConnector/config.cfg"); + toLog("Sim Database updated"); + } else { + airports = simulatorDatabase::fromFile( + "Resources/plugins/GAConnector/sim.bin"); + toLog("Sim Database loaded"); + } + } // Thread for sending data to web socket serverThread = std::thread(&serverWorker); - - toLog("WebSocketTestXPlane initialized and ready"); + toLog("Worker started"); return 1; } @@ -134,11 +166,16 @@ PLUGIN_API void XPluginDisable(void) {} PLUGIN_API int XPluginEnable(void) { return 1; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFromWho, long inMessage, void *inParam) { } +#pragma clang diagnostic pop +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" float flightLoop(float elapsedMe, float elapsedSim, int counter, void *refcon) { const std::lock_guard lock(mutex); @@ -177,9 +214,12 @@ float flightLoop(float elapsedMe, float elapsedSim, int counter, void *refcon) return -1; } +#pragma clang diagnostic pop void serverWorker() { + util::setThreadName("GAWorker"); + while (!wantsExit) { data copy; { diff --git a/xplugin/makeRwysXP.cpp b/xplugin/makeRwysXP.cpp new file mode 100644 index 0000000..1c21d22 --- /dev/null +++ b/xplugin/makeRwysXP.cpp @@ -0,0 +1,177 @@ +#include "include/makeRwysXP.h" + +int scan(const char *defaultFile, + const char *sceneryPack, + const char *logFile, + std::map, std::vector>> &airports) +{ + std::ifstream base(defaultFile); + if (!base.good()) { + return 1; + } + std::ifstream custom(sceneryPack); + if (!custom.good()) { + base.close(); + return 2; + } + std::ofstream logfile(logFile, std::ios::out | std::ios::trunc); + if (!logfile.good()) { + base.close(); + custom.close(); + return 3; + } + + // Default + logfile << " " << defaultFile << std::endl; + makeAirport("DEFAULT", &base, &airports, &logfile); + base.close(); + + std::string line; + size_t pos; + std::vector packs; + while (std::getline(custom, line)) { + if ((pos = line.find("SCENERY_PACK")) != std::string::npos) { + std::string path = + rtrim_copy(line.substr(pos + 13)) + "Earth nav data/apt.dat"; + packs.push_back(path); + } + } + std::reverse(packs.begin(), packs.end()); + + for (std::string const &path : packs) { + std::ifstream pack(path); + if (pack.good()) { + logfile << " " << path << std::endl; + makeAirport("CUSTOM", &pack, &airports, &logfile); + pack.close(); + } else { + pack.close(); + logfile << "" + << "Could not find " << path << std::endl; + } + } + + custom.close(); + logfile.close(); + return 0; +} + +void makeAirport( + const std::string &kind, + std::ifstream *infile, + std::map, std::vector>> + *airports, + std::ofstream *logfile) +{ + std::string line; + std::string *currentIcao = nullptr; + std::vector tmpGates; + std::vector tmpRunways; + + int apCount = 0; + int validCount = 0; + + while (std::getline(*infile, line)) { + std::vector fields = split(line, ' '); + fields = util::select_T(fields, [](const std::string &s) { + return s.length() > 0; + }); + + if (fields.empty()) + continue; + if (fields[0] == "1") { + // Write to file if ICAO is valid, and we have gates and runways + if (currentIcao != nullptr && !tmpRunways.empty() && + !tmpGates.empty()) { + (*airports)[*currentIcao] = {tmpGates, tmpRunways}; + validCount += 1; + *logfile << "\t " << *currentIcao << " committed" + << std::endl; + } else if (currentIcao != nullptr) { + *logfile << "\t " << *currentIcao + << " had no gates or runways" << std::endl; + } + tmpGates = std::vector(); + tmpRunways = std::vector(); + currentIcao = new std::string(fields[4]); + apCount += 1; + *logfile << "\t<" << kind << "> " << line << std::endl; + } else if (currentIcao != nullptr && fields[0] == "15") { + makeGate15(&tmpGates, fields); + *logfile << "\t\t " << line << std::endl; + } else if (fields[0] == "16" || fields[0] == "17") { + // Write to file if ICAO is valid, and we have gates and runways + if (currentIcao != nullptr && !tmpRunways.empty() && + !tmpGates.empty()) { + (*airports)[*currentIcao] = {tmpGates, tmpRunways}; + validCount += 1; + *logfile << "\t " << *currentIcao << " committed" + << std::endl; + } else if (currentIcao != nullptr) { + *logfile << "\t " << *currentIcao + << " had no gates or runways" << std::endl; + } + tmpGates = std::vector(); + tmpRunways = std::vector(); + currentIcao = nullptr; + *logfile << "\t<" << kind << " SKIPPED> " << line << std::endl; + } else if (currentIcao != nullptr && fields[0] == "100") { + makeRunway(&tmpRunways, fields); + *logfile << "\t\t " << line << std::endl; + } else if (currentIcao != nullptr && fields[0] == "1300") { + makeGate1300(&tmpGates, fields); + *logfile << "\t\t " << line << std::endl; + } + } + + if (currentIcao != nullptr && !tmpRunways.empty() && !tmpGates.empty()) { + (*airports)[*currentIcao] = {tmpGates, tmpRunways}; + validCount += 1; + *logfile << "\t " << *currentIcao << " committed" << std::endl; + } + *logfile << " " << apCount << " airports found, of which " + << validCount << " are valid" << std::endl; +} + +void makeGate15(std::vector *gates, std::vector fields) +{ + std::string gateName; + for (size_t j = 4; j < fields.size() - 1; j++) { + gateName += fields[j] + " "; + } + gateName += fields.back(); + gateName = std::regex_replace(gateName, std::regex{","}, "0"); + + gates->push_back( + Gate{gateName, std::stod(fields[1]), std::stod(fields[2])}); +} + +void makeRunway(std::vector *runways, std::vector fields) +{ + runways->push_back(Runway{fields[8], + std::stod(fields[9]), + std::stod(fields[10]), + std::stod(fields[18]), + std::stod(fields[19]), + std::stod(fields[1])}); + runways->push_back(Runway{fields[17], + std::stod(fields[18]), + std::stod(fields[19]), + std::stod(fields[9]), + std::stod(fields[10]), + std::stod(fields[1])}); +} + +void makeGate1300(std::vector *gates, std::vector fields) +{ + std::string gateName; + for (size_t j = 6; j < fields.size() - 1; j++) { + gateName += fields[j] + " "; + } + gateName += fields.back(); + gateName = std::regex_replace(gateName, std::regex{","}, "0"); + + gates->push_back( + Gate{gateName, std::stod(fields[1]), std::stod(fields[2])}); +} \ No newline at end of file diff --git a/xplugin/xPluginWin.cpp b/xplugin/xPluginWin.cpp index d70ed5c..3fdb60c 100644 --- a/xplugin/xPluginWin.cpp +++ b/xplugin/xPluginWin.cpp @@ -1,7 +1,6 @@ -#include +#include #include -#pragma warning(suppress : 26440) BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) @@ -11,7 +10,6 @@ BOOL APIENTRY DllMain(HANDLE hModule, case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: - break; default: break; }