You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ggv_bin.cc 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. Handle Geogrid-Viewer binary overlay file format (.ovl)
  3. Copyright (C) 2016 Ralf Horstmann <ralf@ackstorm.de>
  4. Copyright (C) 2016 Robert Lipe, robertlipe+source@gpsbabel.org
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  16. */
  17. #include "defs.h"
  18. #include <QtCore/QByteArray>
  19. #include <QtCore/QDataStream>
  20. #include <QtCore/QDebug>
  21. #include <QtCore/QFile>
  22. #include <QtCore/QtEndian>
  23. #define MYNAME "ggv_bin"
  24. static QString read_fname;
  25. /***************************************************************************
  26. * local helper functions *
  27. ***************************************************************************/
  28. static void
  29. ggv_bin_read_bytes(QDataStream& stream, QByteArray& buf, qint64 len, const char* descr = NULL)
  30. {
  31. buf.resize(len);
  32. if (stream.readRawData(buf.data(), len) != len || stream.status() != QDataStream::Ok)
  33. fatal(MYNAME ": Read error (%s)\n", descr ? descr : "");
  34. }
  35. static quint16
  36. ggv_bin_read16(QDataStream& stream, const char* descr = NULL)
  37. {
  38. quint16 res;
  39. stream >> res;
  40. if (stream.status() != QDataStream::Ok)
  41. fatal(MYNAME ": Read error (%s)\n", descr ? descr : "");
  42. if (global_opts.debug_level > 1)
  43. qDebug("ovl: %-15s %5u (0x%04x)", descr, res, res);
  44. return res;
  45. }
  46. static quint32
  47. ggv_bin_read32(QDataStream& stream, const char* descr = NULL)
  48. {
  49. quint32 res;
  50. stream >> res;
  51. if (stream.status() != QDataStream::Ok)
  52. fatal(MYNAME ": Read error (%s)\n", descr ? descr : "");
  53. if (global_opts.debug_level > 1) {
  54. if ((res & 0xFFFF0000) == 0)
  55. qDebug("ovl: %-15s %5u (0x%08x)", descr, res, res);
  56. else
  57. qDebug("ovl: %-15s (0x%08x)", descr, res);
  58. }
  59. return res;
  60. }
  61. static void
  62. ggv_bin_read_text16(QDataStream& stream, QByteArray& buf, const char* descr = NULL)
  63. {
  64. quint16 len = ggv_bin_read16(stream, descr);
  65. ggv_bin_read_bytes(stream, buf, len, descr);
  66. buf[len] = 0;
  67. if (global_opts.debug_level > 1)
  68. qDebug() << "ovl: text =" << QString::fromLatin1(buf.constData()).simplified();
  69. }
  70. static void
  71. ggv_bin_read_text32(QDataStream& stream, QByteArray& buf, const char* descr = NULL)
  72. {
  73. quint32 len = ggv_bin_read32(stream, descr);
  74. ggv_bin_read_bytes(stream, buf, len, descr);
  75. buf[len] = 0;
  76. if (global_opts.debug_level > 1)
  77. qDebug() << "ovl: text =" << QString::fromLatin1(buf.constData()).simplified();
  78. }
  79. static double
  80. ggv_bin_read_double(QDataStream& stream, const char* descr = NULL)
  81. {
  82. double res;
  83. stream >> res;
  84. if (stream.status() != QDataStream::Ok)
  85. fatal(MYNAME ": Read error (%s)\n", descr ? descr : "");
  86. return res;
  87. }
  88. /***************************************************************************
  89. * OVL Version 2.0 *
  90. ***************************************************************************/
  91. static void
  92. ggv_bin_read_v2(QDataStream& stream)
  93. {
  94. QByteArray buf;
  95. QString track_name;
  96. QString waypt_name;
  97. route_head* ggv_bin_track;
  98. Waypoint* wpt;
  99. double lon, lat;
  100. quint16 header_len;
  101. quint16 entry_type;
  102. quint16 entry_subtype;
  103. quint16 line_points;
  104. quint64 entry_pos;
  105. // header length is usually either 0x90 or 0x00
  106. header_len = ggv_bin_read16(stream, "map name len");
  107. if (header_len > 0) {
  108. ggv_bin_read_bytes(stream, buf, header_len, "map name");
  109. buf.remove(0,4);
  110. buf.append('\0');
  111. if (global_opts.debug_level > 1)
  112. qDebug() << "ovl: name =" << buf.constData();
  113. }
  114. while (!stream.atEnd()) {
  115. track_name.clear();
  116. if (global_opts.debug_level > 1)
  117. qDebug("------------------------------------ 0x%llx", stream.device()->pos());
  118. entry_pos = stream.device()->pos();
  119. entry_type = ggv_bin_read16(stream, "entry type");
  120. ggv_bin_read16(stream, "entry group");
  121. ggv_bin_read16(stream, "entry zoom");
  122. entry_subtype = ggv_bin_read16(stream, "entry subtype");
  123. switch (entry_subtype) {
  124. case 0x01:
  125. // no data following
  126. break;
  127. case 0x10:
  128. // text with 32 bit length field follows
  129. ggv_bin_read_text32(stream, buf, "text len");
  130. track_name = QString::fromLatin1(buf.constData()).simplified();
  131. break;
  132. default:
  133. fatal(MYNAME ": Unknown subtype (%hu)\n", entry_subtype);
  134. }
  135. switch (entry_type) {
  136. case 0x02:
  137. // text
  138. ggv_bin_read16(stream, "text color");
  139. ggv_bin_read16(stream, "text size");
  140. ggv_bin_read16(stream, "text trans");
  141. ggv_bin_read16(stream, "text font");
  142. ggv_bin_read16(stream, "text angle");
  143. lon = ggv_bin_read_double(stream, "text lon");
  144. lat = ggv_bin_read_double(stream, "text lat");
  145. ggv_bin_read_text16(stream, buf, "text label");
  146. waypt_name = QString::fromLatin1(buf.constData()).simplified();
  147. wpt = new Waypoint;
  148. wpt->longitude = lon;
  149. wpt->latitude = lat;
  150. wpt->description = waypt_name;
  151. waypt_add(wpt);
  152. break;
  153. case 0x03:
  154. // line
  155. case 0x04:
  156. // area
  157. ggv_bin_read16(stream, "line color");
  158. ggv_bin_read16(stream, "line width");
  159. ggv_bin_read16(stream, "line type");
  160. line_points = ggv_bin_read16(stream, "line points");
  161. ggv_bin_track = route_head_alloc();
  162. track_add_head(ggv_bin_track);
  163. if (! track_name.isEmpty())
  164. ggv_bin_track->rte_name = track_name;
  165. for (int i = 1; i <= line_points; i++) {
  166. lon = ggv_bin_read_double(stream, "line lon");
  167. lat = ggv_bin_read_double(stream, "line lat");
  168. wpt = new Waypoint;
  169. wpt->longitude = lon;
  170. wpt->latitude = lat;
  171. track_add_wpt(ggv_bin_track, wpt);
  172. }
  173. break;
  174. case 0x05:
  175. // rectangle
  176. case 0x06:
  177. // circle
  178. case 0x07:
  179. // triangle
  180. ggv_bin_read16(stream, "geom color");
  181. ggv_bin_read16(stream, "geom prop1");
  182. ggv_bin_read16(stream, "geom prop2");
  183. ggv_bin_read16(stream, "geom angle");
  184. ggv_bin_read16(stream, "geom stroke");
  185. ggv_bin_read16(stream, "geom area");
  186. ggv_bin_read_double(stream, "geom lon");
  187. ggv_bin_read_double(stream, "geom lat");
  188. break;
  189. case 0x09:
  190. ggv_bin_read16(stream, "bmp color");
  191. ggv_bin_read16(stream, "bmp prop1");
  192. ggv_bin_read16(stream, "bmp prop2");
  193. ggv_bin_read16(stream, "bmp prop3");
  194. ggv_bin_read_double(stream, "bmp lon");
  195. ggv_bin_read_double(stream, "bmp lat");
  196. ggv_bin_read_text32(stream, buf, "bmp data");
  197. break;
  198. default:
  199. fatal(MYNAME ": Unknown entry type (0x%hx, pos=0x%llx) \n", entry_type, entry_pos);
  200. }
  201. }
  202. }
  203. /***************************************************************************
  204. * OVL Version 3.0 and 4.0 *
  205. ***************************************************************************/
  206. static void
  207. ggv_bin_read_v34_header(QDataStream& stream, quint32& number_labels, quint32 &number_records)
  208. {
  209. QByteArray buf;
  210. quint16 header_len;
  211. ggv_bin_read_bytes(stream, buf, 8, "unknown");
  212. number_labels = ggv_bin_read32(stream, "num labels");
  213. number_records = ggv_bin_read32(stream, "num records");
  214. ggv_bin_read_text16(stream, buf, "text label");
  215. ggv_bin_read16(stream, "unknown");
  216. ggv_bin_read16(stream, "unknown");
  217. // 8 bytes ending with 1E 00, contains len of header block
  218. ggv_bin_read16(stream, "unknown");
  219. header_len = ggv_bin_read16(stream, "header len");
  220. ggv_bin_read16(stream, "unknown");
  221. ggv_bin_read16(stream, "unknown");
  222. if (header_len > 0) {
  223. ggv_bin_read_bytes(stream, buf, header_len, "map name");
  224. buf.remove(0,4);
  225. buf.append('\0');
  226. if (global_opts.debug_level > 1)
  227. qDebug() << "ovl: name =" << buf.constData();
  228. }
  229. }
  230. static void
  231. ggv_bin_read_v34_label(QDataStream& stream)
  232. {
  233. QByteArray buf;
  234. if (global_opts.debug_level > 1)
  235. qDebug("------------------------------------ 0x%llx", stream.device()->pos());
  236. ggv_bin_read_bytes(stream, buf, 0x08, "label header");
  237. ggv_bin_read_bytes(stream, buf, 0x14, "label number");
  238. ggv_bin_read_text16(stream, buf, "label text");
  239. ggv_bin_read16(stream, "label flag1");
  240. ggv_bin_read16(stream, "label flag2");
  241. }
  242. static QString
  243. ggv_bin_read_v34_common(QDataStream& stream)
  244. {
  245. QByteArray buf;
  246. QString res;
  247. quint16 type1;
  248. quint16 type2;
  249. ggv_bin_read16(stream, "entry group");
  250. ggv_bin_read16(stream, "entry prop2");
  251. ggv_bin_read16(stream, "entry prop3");
  252. ggv_bin_read16(stream, "entry prop4");
  253. ggv_bin_read16(stream, "entry prop5");
  254. ggv_bin_read16(stream, "entry prop6");
  255. ggv_bin_read16(stream, "entry prop7");
  256. ggv_bin_read16(stream, "entry prop8");
  257. ggv_bin_read16(stream, "entry zoom");
  258. ggv_bin_read16(stream, "entry prop10");
  259. ggv_bin_read_text16(stream, buf, "entry txt");
  260. res = QString::fromLatin1(buf.constData()).simplified();
  261. type1 = ggv_bin_read16(stream, "entry type1");
  262. if (type1 != 1) {
  263. ggv_bin_read_text32(stream, buf, "entry object");
  264. }
  265. type2 = ggv_bin_read16(stream, "entry type2");
  266. if (type2 != 1) {
  267. ggv_bin_read_text32(stream, buf, "entry object");
  268. }
  269. return res;
  270. }
  271. static void
  272. ggv_bin_read_v34_record(QDataStream& stream)
  273. {
  274. QByteArray buf;
  275. QString label;
  276. Waypoint *wpt;
  277. route_head* ggv_bin_track;
  278. quint16 entry_type;
  279. quint32 bmp_len;
  280. quint16 line_points;
  281. double lon, lat;
  282. if (global_opts.debug_level > 1)
  283. qDebug("------------------------------------ 0x%llx", stream.device()->pos());
  284. entry_type = ggv_bin_read16(stream, "entry type");
  285. label = ggv_bin_read_v34_common(stream);
  286. switch (entry_type) {
  287. case 0x02:
  288. // text
  289. ggv_bin_read16(stream, "text prop1");
  290. ggv_bin_read32(stream, "text prop2");
  291. ggv_bin_read16(stream, "text prop3");
  292. ggv_bin_read32(stream, "text prop4");
  293. ggv_bin_read16(stream, "text ltype");
  294. ggv_bin_read16(stream, "text angle");
  295. ggv_bin_read16(stream, "text size");
  296. ggv_bin_read16(stream, "text area");
  297. lon = ggv_bin_read_double(stream, "text lon");
  298. lat = ggv_bin_read_double(stream, "text lat");
  299. ggv_bin_read_double(stream, "text unk");
  300. ggv_bin_read_text16(stream, buf, "text label");
  301. wpt = new Waypoint;
  302. wpt->longitude = lon;
  303. wpt->latitude = lat;
  304. wpt->description = QString::fromLatin1(buf.constData()).simplified();
  305. waypt_add(wpt);
  306. break;
  307. case 0x03:
  308. case 0x04:
  309. // area
  310. case 0x17:
  311. // line
  312. ggv_bin_track = route_head_alloc();
  313. track_add_head(ggv_bin_track);
  314. if (! label.isEmpty())
  315. ggv_bin_track->rte_name = label;
  316. ggv_bin_read16(stream, "line prop1");
  317. ggv_bin_read32(stream, "line prop2");
  318. ggv_bin_read16(stream, "line prop3");
  319. ggv_bin_read32(stream, "line color");
  320. ggv_bin_read16(stream, "line size");
  321. ggv_bin_read16(stream, "line stroke");
  322. line_points = ggv_bin_read16(stream, "line points");
  323. if (entry_type == 0x04) {
  324. // found in example.ovl generated by Geogrid-Viewer 1.0
  325. ggv_bin_read16(stream, "line pad");
  326. }
  327. for (int i=1; i <= line_points; i++) {
  328. lon = ggv_bin_read_double(stream, "line lon");
  329. lat = ggv_bin_read_double(stream, "line lat");
  330. ggv_bin_read_double(stream, "line unk");
  331. wpt = new Waypoint;
  332. wpt->longitude = lon;
  333. wpt->latitude = lat;
  334. track_add_wpt(ggv_bin_track, wpt);
  335. }
  336. break;
  337. case 0x05:
  338. case 0x06:
  339. case 0x07:
  340. // circle
  341. ggv_bin_read16(stream, "circle prop1");
  342. ggv_bin_read32(stream, "circle prop2");
  343. ggv_bin_read16(stream, "circle prop3");
  344. ggv_bin_read32(stream, "circle color");
  345. ggv_bin_read32(stream, "circle prop5");
  346. ggv_bin_read32(stream, "circle prop6");
  347. ggv_bin_read16(stream, "circle ltype");
  348. ggv_bin_read16(stream, "circle angle");
  349. ggv_bin_read16(stream, "circle size");
  350. ggv_bin_read16(stream, "circle area");
  351. ggv_bin_read_double(stream, "circle lon");
  352. ggv_bin_read_double(stream, "circle lat");
  353. ggv_bin_read_double(stream, "circle unk");
  354. break;
  355. case 0x09:
  356. // bmp
  357. ggv_bin_read16(stream, "bmp prop1");
  358. ggv_bin_read32(stream, "bmp prop2");
  359. ggv_bin_read16(stream, "bmp prop3");
  360. ggv_bin_read32(stream, "bmp prop4");
  361. ggv_bin_read32(stream, "bmp prop5");
  362. ggv_bin_read32(stream, "bmp prop6");
  363. ggv_bin_read_double(stream, "bmp lon");
  364. ggv_bin_read_double(stream, "bmp lat");
  365. ggv_bin_read_double(stream, "bmp unk");
  366. bmp_len = ggv_bin_read32(stream, "bmp len");
  367. ggv_bin_read16(stream, "bmp prop");
  368. ggv_bin_read_bytes(stream, buf, bmp_len, "bmp data");
  369. break;
  370. default:
  371. fatal(MYNAME ": Unsupported type: %x\n", entry_type);
  372. }
  373. }
  374. static void
  375. ggv_bin_read_v34(QDataStream& stream)
  376. {
  377. QByteArray buf;
  378. QString track_name;
  379. quint32 label_count;
  380. quint32 record_count;
  381. while (!stream.atEnd()) {
  382. ggv_bin_read_v34_header(stream, label_count, record_count);
  383. if (label_count && !stream.atEnd()) {
  384. if (global_opts.debug_level > 1)
  385. qDebug("-----labels------------------------- 0x%llx", stream.device()->pos());
  386. for (unsigned int i = 0; i < label_count; i++)
  387. ggv_bin_read_v34_label(stream);
  388. }
  389. if (record_count && !stream.atEnd()) {
  390. if (global_opts.debug_level > 1)
  391. qDebug("-----records------------------------ 0x%llx", stream.device()->pos());
  392. for (unsigned int i = 0; i < record_count; i++)
  393. ggv_bin_read_v34_record(stream);
  394. }
  395. if (!stream.atEnd()) {
  396. if (global_opts.debug_level > 1)
  397. qDebug("------------------------------------ 0x%llx", stream.device()->pos());
  398. // we just skip over the next magic bytes without checking they
  399. // contain the correct string. This is consistent with what I
  400. // believe GGV does
  401. ggv_bin_read_bytes(stream, buf, 23, "magicbytes");
  402. if (global_opts.debug_level > 1)
  403. qDebug() << "ovl: header = " << buf.constData();
  404. }
  405. }
  406. if (global_opts.debug_level > 1) {
  407. qDebug("fpos: 0x%llx", stream.device()->pos());
  408. qDebug("size: 0x%llx", stream.device()->size());
  409. }
  410. }
  411. /***************************************************************************
  412. * global callbacks called by gpsbabel main process *
  413. ***************************************************************************/
  414. static void
  415. ggv_bin_read_file(QDataStream& stream)
  416. {
  417. QByteArray buf;
  418. ggv_bin_read_bytes(stream, buf, 0x17, "magic");
  419. buf[23] = 0;
  420. if (global_opts.debug_level > 1) {
  421. qDebug() << "ovl: header =" << buf.constData();
  422. }
  423. if (buf.startsWith("DOMGVCRD Ovlfile V2.0")) {
  424. ggv_bin_read_v2(stream);
  425. } else if (buf.startsWith("DOMGVCRD Ovlfile V3.0")) {
  426. ggv_bin_read_v34(stream);
  427. } else if (buf.startsWith("DOMGVCRD Ovlfile V4.0")) {
  428. ggv_bin_read_v34(stream);
  429. } else {
  430. fatal(MYNAME ": Unsupported file format\n");
  431. }
  432. }
  433. static void
  434. ggv_bin_read_init(const QString& fname)
  435. {
  436. read_fname = fname;
  437. }
  438. static void
  439. ggv_bin_read_deinit(void)
  440. {
  441. read_fname.clear();
  442. }
  443. static void
  444. ggv_bin_read(void)
  445. {
  446. QFile file(read_fname);
  447. if (!file.open(QIODevice::ReadOnly)) {
  448. fatal(MYNAME ": Error opening file %s\n", qPrintable(read_fname));
  449. }
  450. QDataStream stream(&file);
  451. stream.setFloatingPointPrecision(QDataStream::DoublePrecision);
  452. stream.setByteOrder(QDataStream::LittleEndian);
  453. ggv_bin_read_file(stream);
  454. file.close();
  455. }
  456. ff_vecs_t ggv_bin_vecs = {
  457. ff_type_file,
  458. {
  459. ff_cap_none, // waypoints
  460. ff_cap_read, // tracks
  461. ff_cap_none // routes
  462. },
  463. ggv_bin_read_init, // rd_init
  464. NULL, // wr_init
  465. ggv_bin_read_deinit, // rd_deinit
  466. NULL, // wr_deinit
  467. ggv_bin_read, // read
  468. NULL, // write
  469. NULL, // exit
  470. NULL, //args
  471. CET_CHARSET_ASCII, 0 //encode,fixed_encode
  472. //NULL //name dynamic/internal?
  473. };