1 /*
2 	Garmin GPS Database Reader/Writer
3 
4 	Copyright (C) 2005-2008 Olaf Klein, o.b.klein@gpsbabel.org
5 	Mainly based on mapsource.c,
6 	Copyright (C) 2005 Robert Lipe, robertlipe+source@gpsbabel.org
7 
8 
9 	This program is free software; you can redistribute it and/or modify
10 	it under the terms of the GNU General Public License as published by
11 	the Free Software Foundation; either version 2 of the License, or
12 	(at your option) any later version.
13 
14 	This program is distributed in the hope that it will be useful,
15 	but WITHOUT ANY WARRANTY; without even the implied warranty of
16 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 	GNU General Public License for more details.
18 
19 	You should have received a copy of the GNU General Public License
20 	along with this program; if not, write to the Free Software
21 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 
23   A format description obtained from reverse-engineering is at
24   https://www.memotech.franken.de/FileFormats/Garmin_MPS_GDB_and_GFI_Format.pdf
25 */
26 
27 #include <cmath>                   // for fabs
28 #include <cstdio>                  // for printf, snprintf, sscanf, SEEK_SET, NULL
29 #include <cstdlib>                 // for atoi, strtol, NULL
30 #include <cstring>                 // for memset, strcmp, strstr, strchr, strlen, strncpy
31 #include <ctime>                   // for strftime
32 #include <iterator>                // for next
33 
34 #include <QtCore/QByteArray>       // for QByteArray
35 #include <QtCore/QList>            // for QList
36 #include <QtCore/QString>          // for QString, operator!=, operator==
37 #include <QtCore/QVector>          // for QVector
38 #include <QtCore/Qt>               // for CaseInsensitive
39 #include <QtCore/QtGlobal>         // for qPrintable, Q_UNUSED, foreach
40 
41 #include "defs.h"
42 
43 #include "cet_util.h"              // for cet_convert_init
44 #include "garmin_fs.h"             // for garmin_fs_t, garmin_fs_flags_t, garmin_ilink_t, GMSD_GET, GMSD_SETSTR, GMSD_SET, garmin_fs_alloc, GMSD_FIND, GMSD_HAS
45 #include "garmin_tables.h"         // for gt_get_icao_country, gt_waypt_class_map_point, gt_color_index_by_rgb, gt_color_value, gt_waypt_classes_e, gt_find_desc_from_icon_number, gt_find_icon_number_from_desc, gt_gdb_display_mode_symbol, gt_waypt_class_user_waypoint, GDB, gt_display_mode_symbol
46 #include "gbfile.h"                // for gbfputint32, gbfgetint32, gbfread, gbfwrite, gbfgetc, gbfputc, gbfgetdbl, gbfgetcstr, gbfile, gbfclose, gbfputcstr, gbfcopyfrom, gbfrewind, gbfseek, gbftell, gbfopen_le, gbfgetcstr_old, gbfgetint16, gbfputdbl, gbfputint16
47 #include "grtcirc.h"               // for RAD, gcdist, radtometers
48 #include "jeeps/gpsmath.h"         // for GPS_Math_Deg_To_Semi, GPS_Math_Semi_To_Deg
49 #include "src/core/datetime.h"     // for DateTime
50 
51 
52 #define MYNAME "gdb"
53 
54 #define GDB_VER_1		1
55 #define GDB_VER_2		2
56 #define GDB_VER_3		3
57 
58 #define GDB_VER_UTF8		GDB_VER_3
59 #define GDB_VER_MIN		GDB_VER_1
60 #define GDB_VER_MAX		GDB_VER_3
61 
62 #define GDB_DEF_CLASS		gt_waypt_class_user_waypoint
63 #define GDB_DEF_HIDDEN_CLASS	gt_waypt_class_map_point
64 #define GDB_DEF_ICON		18
65 
66 #define GDB_NAME_BUFFERLEN	1024
67 
68 #define GDB_DBG_WPT		1
69 #define GDB_DBG_RTE		2
70 #define GDB_DBG_TRK		4
71 
72 #define GDB_DBG_WPTe		8
73 #define GDB_DBG_RTEe		16
74 #define GDB_DBG_TRKe		32
75 
76 #define GDB_DEBUG		(GDB_DBG_WPTe) /* | GDB_DBG_RTE) */
77 #undef GDB_DEBUG
78 // #define GDB_DEBUG 0xff
79 
80 #define DBG(a,b)		if ((GDB_DEBUG & (a)) && (b))
81 
82 /*******************************************************************************/
83 
84 /* static char gdb_release[] = "$Revision: 1.74 $"; */
85 static char gdb_release_date[] = "$Date: 2011-04-14 01:30:01 $";
86 
87 static gbfile* fin, *fout, *ftmp;
88 static int gdb_ver, gdb_category;
89 static bool gdb_roadbook;
90 static bool gdb_hide_wpt;
91 static bool gdb_hide_rpt;
92 
93 static QList<Waypoint*> wayptq_in, wayptq_out, wayptq_in_hidden;
94 static short_handle short_h;
95 
96 static char* gdb_opt_category;
97 static char* gdb_opt_ver;
98 static char* gdb_opt_via;
99 static char* gdb_opt_roadbook;
100 static char* gdb_opt_bitcategory;
101 static char* gdb_opt_drop_hidden_wpt;
102 
103 static int waypt_flag;
104 static int route_flag;
105 
106 static int waypt_ct;	/* informational: total number of waypoints in/out */
107 static int waypth_ct;	/* informational: total number of hidden waypoints in/out */
108 static int rtept_ct;	/* informational: total number of route points in/out */
109 static int trkpt_ct;	/* informational: total number of track points in/out */
110 static int rte_ct;	/* informational: total number of routes in/out */
111 static int trk_ct;	/* informational: total number of tracks in/out */
112 
113 /*******************************************************************************/
114 
115 #define ELEMENTS(a) a->rte_waypt_ct
116 #define NOT_EMPTY(a) (a && *a)
117 
118 static void
gdb_flush_waypt_queue(QList<Waypoint * > * Q)119 gdb_flush_waypt_queue(QList<Waypoint*>* Q)
120 {
121 
122   while (!Q->isEmpty()) {
123     const Waypoint* wpt = Q->takeFirst();
124     if (wpt->extra_data) {
125       // FIXME
126       // wpt->extra_data may be holding a pointer to a QString, courtesy
127       // the grossness at the end of write_waypt_cb().  If that leaks,
128       // (and I think it will) find some way to do the approximate equivalent
129       // of:
130       // delete static_cast<QString*>(wpt->extra_data);
131     }
132     delete wpt;
133   }
134 }
135 
136 #if GDB_DEBUG
137 static void
disp_summary(const gbfile * f)138 disp_summary(const gbfile* f)
139 {
140   int i, len;
141 
142   len = strlen(f->name);
143 
144   warning(MYNAME ": =====================");
145   for (i = 0; i < len; i++) {
146     warning("=");
147   }
148   warning("\n" MYNAME ": %s summary for \"%s\"\n",
149           (f->mode == 'r') ? "Reader" : "Writer", f->name);
150 
151   warning(MYNAME ": ---------------------");
152   for (i = 0; i < len; i++) {
153     warning("-");
154   }
155 
156   warning("\n" MYNAME ": %d waypoint(s)\n", waypt_ct - waypth_ct);
157   warning(MYNAME ": %d hidden waypoint(s)\n", waypth_ct);
158   warning(MYNAME ": %d route(s) with total %d point(s)\n", rte_ct, rtept_ct);
159   warning(MYNAME ": %d track(s) with total %d point(s)\n", trk_ct, trkpt_ct);
160   warning(MYNAME ": ---------------------");
161 
162   for (i = 0; i < len; i++) {
163     warning("-");
164   }
165   warning("\n");
166 }
167 #else
168 #define disp_summary(a)
169 #endif
170 
171 /*******************************************************************************/
172 /* TOOLS AND MACROS FOR THE READER */
173 /*-----------------------------------------------------------------------------*/
174 
175 #define FREAD_C gbfgetc(fin)
176 #define FREAD(a,b) gbfread(a,(b),1,fin)
177 #define FREAD_i32 gbfgetint32(fin)
178 #define FREAD_i16 gbfgetint16(fin)
179 #define FREAD_DBL gbfgetdbl(fin)
180 #define FREAD_LATLON GPS_Math_Semi_To_Deg(gbfgetint32(fin))
181 
182 #define FREAD_STR() gbfgetnativecstr(fin)
183 
184 // This is all very messy.  Some versions of GDB store strings as
185 // 8859-1 strings and others as UTF8.  This wrapper tries to hide
186 // all that while (while keeping the character sets correct) and
187 // not pushing that decision  down into gbfread.  This module is
188 // still pretty messy and the points as to which fields are encode
189 // which ways in which versions are not at all clear, leading to
190 // encoding issues on read and leaks because of the differences
191 // in calling conventions on who owns/destroys the result.
192 
193 #define FREAD_CSTR_AS_QSTR gbfgetcstr(fin)
194 
195 static char* gdb_fread_cstr(gbfile* fin);
196 
fread_cstr()197 static QString fread_cstr()
198 {
199   QString rv;
200   char* s = gdb_fread_cstr(fin);
201   if (gdb_ver >= GDB_VER_UTF8) {
202     rv = QString::fromUtf8(s);
203   } else {
204     rv = QString::fromLatin1(s);
205   }
206 
207   xfree(s);
208 
209   return rv;
210 }
211 
212 static char*
gdb_fread_cstr(gbfile * file_in)213 gdb_fread_cstr(gbfile* file_in)
214 {
215   char* result = gbfgetcstr_old(file_in);
216 
217   if (result && (*result == '\0')) {
218     xfree(result);
219     result = nullptr;
220   }
221 
222   return result;
223 }
224 
225 static QString
gdb_fread_strlist()226 gdb_fread_strlist()
227 {
228   QString res;
229 
230   int count = FREAD_i32;
231 
232   while (count > 0) {
233     QString str = fread_cstr();
234     if (!str.isEmpty()) {
235       res = str;
236     }
237     count--;
238   }
239 
240   QString qres = res;
241   return qres;
242 }
243 
244 static Waypoint*
gdb_find_wayptq(const QList<Waypoint * > * Q,const Waypoint * wpt,const char exact)245 gdb_find_wayptq(const QList<Waypoint*>* Q, const Waypoint* wpt, const char exact)
246 {
247   QString name = wpt->shortname;
248   foreach (Waypoint* tmp, *Q) {
249     if (name.compare(tmp->shortname,Qt::CaseInsensitive) == 0) {
250       if (! exact) {
251         return tmp;
252       }
253 
254       if ((tmp->latitude == wpt->latitude) &&
255           (tmp->longitude == wpt->longitude)) {
256         return tmp;
257       }
258     }
259   }
260   return nullptr;
261 }
262 
263 static Waypoint*
gdb_reader_find_waypt(const Waypoint * wpt,const char exact)264 gdb_reader_find_waypt(const Waypoint* wpt, const char exact)
265 {
266   Waypoint* res = gdb_find_wayptq(&wayptq_in, wpt, exact);
267   if (res == nullptr) {
268     res = gdb_find_wayptq(&wayptq_in_hidden, wpt, exact);
269   }
270   return res;
271 }
272 
273 static Waypoint*
gdb_add_route_waypt(route_head * rte,Waypoint * ref,const int wpt_class)274 gdb_add_route_waypt(route_head* rte, Waypoint* ref, const int wpt_class)
275 {
276   Waypoint* tmp = gdb_reader_find_waypt(ref, 1);
277   if (tmp == nullptr) {
278     tmp = find_waypt_by_name(ref->shortname);
279     if (tmp == nullptr) {
280       route_add_wpt(rte, ref);
281       return ref;
282     }
283 
284     /* At this point we have found a waypoint with same name,
285        but probably from another data stream. Check coordinates!
286     */
287     double dist = radtometers(gcdist(
288                                 RAD(ref->latitude), RAD(ref->longitude),
289                                 RAD(tmp->latitude), RAD(tmp->longitude)));
290 
291     if (fabs(dist) > 100) {
292       warning(MYNAME ": Route point mismatch!\n");
293       warning(MYNAME ": \"%s\" from waypoints differs to \"%s\"\n",
294               qPrintable(tmp->shortname), qPrintable(ref->shortname));
295       fatal(MYNAME ": from route table by more than %0.1f meters!\n",
296             dist);
297 
298     }
299   }
300   Waypoint* res = nullptr;
301   int turn_point = (gdb_roadbook && (wpt_class > gt_waypt_class_map_point) && !tmp->description.isEmpty());
302   if (turn_point || !gdb_hide_rpt || (wpt_class < gt_waypt_class_map_point)) {
303     res = new Waypoint(*tmp);
304     route_add_wpt(rte, res);
305   }
306   delete ref;
307   return res;
308 }
309 
gdb_to_ISO8601_duration(unsigned int seconds)310 static QString gdb_to_ISO8601_duration(unsigned int seconds)
311 {
312   if (seconds == 0u) {
313     return QString("PT0S");
314   }
315   unsigned int days = seconds / 86400u;
316   QString out = "P";
317   if (days != 0) {
318     out.append(QString("D%1").arg(days));
319     seconds -= 86400u * days;
320   }
321   out.append(QString("T"));
322   unsigned int hours = seconds / 3600u;
323   if (hours != 0) {
324     out.append(QString("%1H").arg(hours));
325     seconds -= 3600u * hours;
326   }
327   unsigned int minutes = seconds / 60u;
328   if (minutes != 0) {
329     out.append(QString("%1M").arg(minutes));
330     seconds -= 60u * minutes;
331   }
332   if (seconds != 0) {
333     out.append(QString("%1S").arg(seconds));
334   }
335   return out;
336 }
337 
338 /*******************************************************************************/
339 /* TOOLS AND MACROS FOR THE WRITER */
340 /*-----------------------------------------------------------------------------*/
FWRITE_CSTR(const QString & a)341 static void FWRITE_CSTR(const QString& a)
342 {
343   if (a.isEmpty()) {
344     gbfputc(0, fout);
345     return;
346   }
347   if (gdb_ver >= GDB_VER_UTF8) {
348     gbfputcstr(a.toUtf8().constData(), fout);
349   } else {
350     gbfputcstr(a.toLatin1().constData(), fout);
351   }
352 }
353 
354 #define FWRITE_i16(a) gbfputint16((a),fout)
355 #define FWRITE_i32(a) gbfputint32((a),fout)
356 #define FWRITE(a, b) gbfwrite(a,(b),1,fout)
357 #define FWRITE_C(a) gbfputc((a),fout)
358 #define FWRITE_DBL(a,b) gdb_write_dbl((a),(b))
359 #define FWRITE_TIME(a) gdb_write_time((a))
360 #define FWRITE_CSTR_LIST(a) gdb_write_cstr_list((a))
361 #define FWRITE_LATLON(a) gbfputint32(GPS_Math_Deg_To_Semi((a)),fout)
362 
363 static void
gdb_write_cstr_list(const char * str)364 gdb_write_cstr_list(const char* str)
365 {
366   if NOT_EMPTY(str) {
367     gbfputint32(1, fout);
368     gbfputcstr(str, fout);
369   } else {
370     gbfputint32(0, fout);
371   }
372 }
373 
374 static void
gdb_write_cstr_list(const QString & str)375 gdb_write_cstr_list(const QString& str)
376 {
377   gdb_write_cstr_list(CSTRc(str));
378 }
379 
380 static void
gdb_write_dbl(const double value,const double def)381 gdb_write_dbl(const double value, const double def)
382 {
383   if (value == def) {
384     gbfputc(0, fout);
385   } else {
386     gbfputc(1, fout);
387     gbfputdbl(value, fout);
388   }
389 }
390 
391 static void
gdb_write_time(const int time)392 gdb_write_time(const int time)
393 {
394   if (time > 0) {
395     gbfputc(1, fout);
396     gbfputint32(time, fout);
397   } else {
398     gbfputc(0, fout);
399   }
400 }
401 
402 /*******************************************************************************/
403 /* GDB "Garmin Database" READER CODE */
404 /*-----------------------------------------------------------------------------*/
405 
406 static void
read_file_header()407 read_file_header()
408 {
409   char buf[128];
410 
411   /*
412   	We are beginning with a simple binary read.
413   */
414   FREAD(buf, 6);
415   /*
416   	A "gbfgetcstr" (FREAD_CSTR) works too, but if we get a wrong file as input,
417   	the file validation my be comes too late. For example a XML base file normally
418   	has no binary zeros inside and produce, if big enough, a buffer overflow.
419   	The following message "local buffer overflow detected..." could be
420   	misinterpreted.
421   */
422   is_fatal(strcmp(buf, "MsRcf") != 0, MYNAME ": Invalid file \"%s\"!", fin->name);
423 
424   int reclen = FREAD_i32;
425   Q_UNUSED(reclen);
426   QByteArray drec = FREAD_STR();
427   is_fatal(drec.at(0) != 'D', MYNAME ": Invalid file \"%s\"!", fin->name);
428 
429   gdb_ver = drec.at(1) - 'k' + 1;
430   is_fatal((gdb_ver < GDB_VER_MIN) || (gdb_ver > GDB_VER_MAX),
431            MYNAME ": Unknown or/and unsupported GDB version (%d.0)!", gdb_ver);
432 
433   if (global_opts.verbose_status > 0) {
434     printf(MYNAME ": Reading Garmin GPS Database version %d.0\n", gdb_ver);
435   }
436 
437   reclen = FREAD_i32;
438   is_fatal((reclen + 1 > int(sizeof(buf))),
439            MYNAME ": Invalid record length\n");
440   (void) FREAD(buf, reclen + 1);
441   if (global_opts.verbose_status > 0) {
442     const char* name = buf+2;
443     if (strstr(name, "SQA") == nullptr) {
444       name = "MapSource";
445     } else if (strstr(name, "neaderhi") == nullptr) {
446       name = "MapSource BETA";
447     }
448     warning(MYNAME ": File created with \"%s\"\n", name);
449   }
450 
451   QByteArray applicationField = FREAD_STR();
452   is_fatal(!((applicationField == "MapSource") || (applicationField == "BaseCamp")), MYNAME ": Not a recognized signature in header");
453 }
454 
455 /*-----------------------------------------------------------------------------*/
456 
457 static Waypoint*
read_waypoint(gt_waypt_classes_e * waypt_class_out)458 read_waypoint(gt_waypt_classes_e* waypt_class_out)
459 {
460   char buf[128];		/* used for temporary stuff */
461   gt_waypt_classes_e wpt_class;
462   Waypoint* res;
463   garmin_fs_t* gmsd;
464 #ifdef GMSD_EXPERIMENTAL
465   char subclass[22];
466 #endif
467   waypt_ct++;
468   res = new Waypoint;
469 
470   gmsd = garmin_fs_alloc(-1);
471   res->fs.FsChainAdd(gmsd);
472   res->shortname = fread_cstr();
473   wpt_class = (gt_waypt_classes_e) FREAD_i32;
474   garmin_fs_t::set_wpt_class(gmsd, wpt_class);
475   if (wpt_class != 0) {
476     waypth_ct++;
477   }
478 
479   garmin_fs_t::set_cc(gmsd, fread_cstr());
480 
481 #ifdef GMSD_EXPERIMENTAL
482   FREAD(subclass, sizeof(subclass));
483   if (gmsd && (wpt_class >= gt_waypt_class_map_point)) {
484     memcpy(gmsd->subclass, subclass, sizeof(gmsd->subclass));
485     gmsd->flags.subclass = 1;
486   }
487 #else
488   FREAD(buf, 22);
489 #endif
490   res->latitude = FREAD_LATLON;
491   res->longitude = FREAD_LATLON;
492 
493   if (FREAD_C == 1) {
494     double alt = FREAD_DBL;
495     if (alt < 1.0e24) {
496       res->altitude = alt;
497 #if GDB_DEBUG
498       DBG(GDB_DBG_WPTe, 1)
499       printf(MYNAME "-wpt \"%s\" (%d): Altitude = %.1f\n",
500              qPrintable(res->shortname), wpt_class, alt);
501 #endif
502     }
503   }
504 #if GDB_DEBUG
505   DBG(GDB_DBG_WPT, 1)
506   printf(MYNAME "-wpt \"%s\": coordinates = %c%0.6f %c%0.6f\n",
507          qPrintable(res->shortname),
508          res->latitude < 0 ? 'S' : 'N', res->latitude,
509          res->longitude < 0 ? 'W' : 'E', res->longitude);
510 #endif
511   res->notes = fread_cstr();
512 #if GDB_DEBUG
513   DBG(GDB_DBG_WPTe, !res->notes.isNull())
514   printf(MYNAME "-wpt \"%s\" (%d): notes = %s\n",
515          qPrintable(res->shortname), wpt_class,
516          qPrintable(QString(res->notes).replace("\r\n", ", ")));
517 #endif
518   if (FREAD_C == 1) {
519     WAYPT_SET(res, proximity, FREAD_DBL);
520 #if GDB_DEBUG
521     DBG(GDB_DBG_WPTe, 1)
522     printf(MYNAME "-wpt \"%s\" (%d): Proximity = %.1f\n",
523            qPrintable(res->shortname), wpt_class, res->proximity / 1000);
524 #endif
525   }
526   int display = FREAD_i32;
527 #if GDB_DEBUG
528   DBG(GDB_DBG_WPTe, 1)
529   printf(MYNAME "-wpt \"%s\" (%d): display = %d\n",
530          qPrintable(res->shortname), wpt_class, display);
531 #endif
532   switch (display) {			/* display value */
533   case gt_gdb_display_mode_symbol:
534     display = gt_display_mode_symbol;
535     break;
536   case gt_gdb_display_mode_symbol_and_comment:
537     display = gt_display_mode_symbol_and_comment;
538     break;
539   default:
540     display = gt_display_mode_symbol_and_name;
541     break;
542   }
543   garmin_fs_t::set_display(gmsd, display);
544 
545   FREAD_i32;				/* color !not implemented! */
546   int icon = FREAD_i32;
547   garmin_fs_t::set_icon(gmsd, icon);
548   garmin_fs_t::set_city(gmsd, fread_cstr());
549   garmin_fs_t::set_state(gmsd, fread_cstr());
550   garmin_fs_t::set_facility(gmsd, fread_cstr());
551 
552   FREAD(buf, 1);
553 
554   if (FREAD_C == 1) {
555     WAYPT_SET(res, depth, FREAD_DBL);
556 #if GDB_DEBUG
557     DBG(GDB_DBG_WPTe, 1)
558     printf(MYNAME "-wpt \"%s\" (%d): Depth = %.1f\n",
559            qPrintable(res->shortname), wpt_class, res->depth);
560 #endif
561   }
562 
563   /* VERSION DEPENDENT CODE */
564 
565   if (gdb_ver <= GDB_VER_2) {
566 
567     FREAD(buf, 2);				/* ?????????????????????????????????? */
568     waypt_flag = FREAD_C;
569     if (waypt_flag == 0) {
570       FREAD(buf, 3);
571     } else {
572       FREAD(buf, 2);
573     }
574 
575 #if GDB_DEBUG
576     QString temp = FREAD_CSTR_AS_QSTR;				/* undocumented & unused string */
577     DBG(GDB_DBG_WPTe, !temp.isEmpty())
578     printf(MYNAME "-wpt \"%s\" (%d): Unknown string = %s\n",
579            qPrintable(res->shortname), wpt_class, qPrintable(temp));
580 #else
581     (void) FREAD_CSTR_AS_QSTR;				/* undocumented & unused string */
582 #endif
583 
584     QString linky = FREAD_CSTR_AS_QSTR;
585     UrlLink l(linky);
586     if (!linky.isEmpty()) {
587       res->AddUrlLink(l);
588     }
589     if (wpt_class != 0) {
590       res->description = l.url_;
591     }
592   } else { // if (gdb_ver >= GDB_VER_3)
593 
594     waypt_flag = 0;
595 
596     garmin_fs_t::set_addr(gmsd, fread_cstr());
597 
598     FREAD(buf, 1);
599     unsigned int duration = gbfgetuint32(fin);
600 
601     res->description = FREAD_CSTR_AS_QSTR;	/* instruction */
602     if (wpt_class == gt_waypt_class_map_intersection || wpt_class == gt_waypt_class_map_line) {
603       garmin_fs_t::set_duration(gmsd, duration);
604       res->notes = QString("[%1]").arg(gdb_to_ISO8601_duration(duration));
605 #if GDB_DEBUG
606       DBG(GDB_DBG_WPTe, 1)
607       printf(MYNAME "-wpt \"%s\" (%d): duration = %u\n",
608              qPrintable(res->shortname), wpt_class, duration);
609 #endif
610     }
611     int url_ct = FREAD_i32;
612     for (int i = url_ct; (i); i--) {
613       QString str = FREAD_CSTR_AS_QSTR;
614       if (!str.isEmpty()) {
615         waypt_add_url(res, str, nullptr);
616 #if GDB_DEBUG
617         DBG(GDB_DBG_WPTe, 1)
618         printf(MYNAME "-wpt \"%s\" (%d): url(%d) = %s\n",
619                qPrintable(res->shortname), wpt_class, url_ct - i, qPrintable(str));
620 #endif
621       }
622     }
623   }
624 
625 #if GDB_DEBUG
626   DBG(GDB_DBG_WPTe, !res->description.isNull())
627   printf(MYNAME "-wpt \"%s\" (%d): description = %s\n",
628          qPrintable(res->shortname), wpt_class, qPrintable(res->description));
629   DBG(GDB_DBG_WPTe, res->urls.HasUrlLink())
630   printf(MYNAME "-wpt \"%s\" (%d): url = %s\n",
631          qPrintable(res->shortname), wpt_class, qPrintable(res->urls.GetUrlLink().url_));
632 #endif
633   int category = FREAD_i16;
634   if (category != 0) {
635     garmin_fs_t::set_category(gmsd, category);
636   }
637 #if GDB_DEBUG
638   DBG(GDB_DBG_WPTe, category)
639   printf(MYNAME "-wpt \"%s\" (%d): category = %d\n",
640          qPrintable(res->shortname), wpt_class, category);
641 #endif
642 
643   if (FREAD_C == 1) {
644     WAYPT_SET(res, temperature, FREAD_DBL);
645 #if GDB_DEBUG
646     DBG(GDB_DBG_WPTe, 1)
647     printf(MYNAME "-wpt \"%s\" (%d): temperature = %.1f\n",
648            qPrintable(res->shortname), wpt_class, res->temperature);
649 #endif
650   }
651 
652   /* VERSION DEPENDENT CODE */
653   if (gdb_ver <= GDB_VER_2) {
654     if (waypt_flag != 0) {
655       FREAD(buf, 1);
656     }
657   }
658   if (FREAD_C == 1) {
659     res->SetCreationTime(FREAD_i32);
660   }
661 
662   /* VERSION DEPENDENT CODE */
663   if (gdb_ver >= GDB_VER_3) {
664     if (FREAD_i32 == 1) {
665       garmin_fs_t::set_phone_nr(gmsd, fread_cstr());
666       (void) FREAD_STR();		/* ?? fax / mobile ?? */
667     }
668     garmin_fs_t::set_country(gmsd, fread_cstr());
669     garmin_fs_t::set_postal_code(gmsd, fread_cstr());
670   }
671 
672   res->icon_descr = gt_find_desc_from_icon_number(icon, GDB);
673 
674 #if GDB_DEBUG
675   DBG(GDB_DBG_WPTe, icon != GDB_DEF_ICON)
676   printf(MYNAME "-wpt \"%s\" (%d): icon = \"%s\" (MapSource symbol %d)\n",
677          qPrintable(res->shortname), wpt_class, qPrintable(res->icon_descr), icon);
678 #endif
679   QString str;
680   if (!(str = garmin_fs_t::get_cc(gmsd, nullptr)).isEmpty()) {
681     if (!(garmin_fs_t::has_country(gmsd))) {
682       garmin_fs_t::set_country(gmsd, gt_get_icao_country(str));
683     }
684   }
685   if (gdb_roadbook && (wpt_class > gt_waypt_class_map_point) && !res->description.isEmpty()) {
686     wpt_class = gt_waypt_class_user_waypoint;
687     garmin_fs_t::set_wpt_class(gmsd, wpt_class);
688 #ifdef GMSD_EXPERIMENTAL
689     garmin_fs_t::unset_subclass(gmsd);
690 #endif
691   }
692   *waypt_class_out = wpt_class;
693   return res;
694 }
695 
696 /*-----------------------------------------------------------------------------*/
697 
698 static route_head*
read_route()699 read_route()
700 {
701   rte_ct++;
702   int warnings = 0;
703 
704   auto* rte = new route_head;
705   rte->rte_name = fread_cstr();
706 
707   char tbuf[128];
708   FREAD(tbuf, 1);			/* display/autoname - 1 byte */
709 
710   if (FREAD_C == 0) {		/* max. data flag */
711     /* maxlat = */ (void) FREAD_i32;
712     /* maxlon = */
713     (void) FREAD_i32;
714     if (FREAD_C == 1) { /* maxalt = */
715       FREAD_DBL;
716     }
717     /* minlat = */ (void) FREAD_i32;
718     /* minlon = */
719     (void) FREAD_i32;
720     if (FREAD_C == 1) { /* minalt = */
721       FREAD_DBL;
722     }
723   }
724 
725   int points = FREAD_i32;
726 
727 #if GDB_DEBUG
728   DBG(GDB_DBG_RTE, 1)
729   printf(MYNAME "-rte \"%s\": loading route with %d point(s)...\n",
730          qPrintable(rte->rte_name), points);
731 #endif
732 
733   for (int i = 0; i < points; i++) {
734     char buf[128];
735 
736     auto* wpt = new Waypoint;
737     rtept_ct++;
738 
739     wpt->shortname = fread_cstr();	/* shortname */
740     int wpt_class = FREAD_i32;		/* waypoint class */
741     (void) FREAD_STR();			/* country code */
742     FREAD(buf, 18 + 4);		/* subclass part 1-3 / unknown */
743 
744     if (FREAD_C != 0) {
745       FREAD(buf, 8);		/* aviation data (?); only seen with class "1" (Airport) */
746       /* VERSION DEPENDENT CODE */
747       if (gdb_ver >= GDB_VER_3) {
748         FREAD(buf, 8);  /* a second block since V3 */
749       }
750     }
751 
752     FREAD(buf, 18);			/* unknown 18 bytes; but first should be 0x01 or 0x03 */
753     /* seen also 0 with VER3 */
754     if ((buf[0] != 0x00) && (buf[0] != 0x01) && (buf[0] != 0x03)) {
755       warnings++;
756       if (warnings > 3) {
757         fatal(MYNAME "-rte_pt \"%s\": too many warnings!\n", qPrintable(wpt->shortname));
758       }
759       warning(MYNAME "-rte_pt \"%s\" (class %d): possible error in route.\n", qPrintable(wpt->shortname), wpt_class);
760       warning(MYNAME "-rte_pt (dump):");
761       for (int idx = 0; idx < 18; idx++) {
762         warning(" %02x", (unsigned char)buf[idx]);
763       }
764       warning("\n");
765     }
766 
767     int links = FREAD_i32;
768     garmin_ilink_t* il_anchor = nullptr;
769     garmin_ilink_t* il_root = nullptr;
770 #if GDB_DEBUG
771     DBG(GDB_DBG_RTE, links)
772     printf(MYNAME "-rte_pt \"%s\" (%d): %d interlink step(s)\n",
773            qPrintable(wpt->shortname), wpt_class, links);
774 #endif
775     for (int j = 0; j < links; j++) {
776       auto* il_step = (garmin_ilink_t*) xmalloc(sizeof(garmin_ilink_t));
777 
778       il_step->ref_count = 1;
779 
780       il_step->lat = FREAD_LATLON;
781       il_step->lon = FREAD_LATLON;
782       if (FREAD_C == 1) {
783         il_step->alt = FREAD_DBL;
784       } else {
785         il_step->alt = unknown_alt;
786       }
787 
788       if (j == 0) {
789         wpt->latitude = il_step->lat;
790         wpt->longitude = il_step->lon;
791         wpt->altitude = il_step->alt;
792       }
793 
794       il_step->next = nullptr;
795       if (il_anchor == nullptr) {
796         il_root = il_step;
797       } else {
798         il_anchor->next = il_step;
799       }
800       il_anchor = il_step;
801 
802 #if GDB_DEBUG
803       DBG(GDB_DBG_RTEe, 1) {
804         printf(MYNAME "-rte_il \"%s\" (%d of %d): %c%0.6f %c%0.6f\n",
805                qPrintable(wpt->shortname), j + 1, links,
806                il_step->lat < 0 ? 'S' : 'N', il_step->lat,
807                il_step->lon < 0 ? 'W' : 'E', il_step->lon);
808       }
809 #endif
810     }
811 
812     bounds bounds;
813     waypt_init_bounds(&bounds);
814 
815     if (FREAD_C == 0) {		/* interlink bounds */
816       bounds.max_lat = FREAD_LATLON;
817       bounds.max_lon = FREAD_LATLON;
818       if (FREAD_C == 1) {
819         bounds.max_alt = FREAD_DBL;
820       }
821       bounds.min_lat = FREAD_LATLON;
822       bounds.min_lon = FREAD_LATLON;
823       if (FREAD_C == 1) {
824         bounds.min_alt = FREAD_DBL;
825       }
826     }
827 
828     if (links == 0) {
829       /* Without links we need all information from wpt */
830       Waypoint* tmp = gdb_reader_find_waypt(wpt, 0);
831       if (tmp != nullptr) {
832         delete wpt;
833         wpt = new Waypoint(*tmp);
834       } else {
835         if (waypt_bounds_valid(&bounds)) {
836           warning(MYNAME ": (has bounds)\n");
837         }
838 
839         warning(MYNAME ": Data corruption detected!\n");
840         fatal(MYNAME ": Sleeping route point without coordinates!\n");
841       }
842     }
843 
844     /* VERSION DEPENDENT CODE */
845     if (gdb_ver >= GDB_VER_2) {
846       FREAD(buf, 8);
847       if (gdb_ver >= GDB_VER_3) {
848         FREAD(buf, 2);
849       }
850     }
851 #if GDB_DEBUG
852     DBG(GDB_DBG_RTE, 1)
853     printf(MYNAME "-rte_pt \"%s\": coordinates = %c%0.6f, %c%0.6f\n",
854            qPrintable(wpt->shortname),
855            wpt->latitude < 0 ? 'S' : 'N', wpt->latitude,
856            wpt->longitude < 0 ? 'W' : 'E', wpt->longitude);
857 #endif
858     wpt = gdb_add_route_waypt(rte, wpt, wpt_class);
859     if (wpt != nullptr) {
860       garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
861       if (gmsd == nullptr) {
862         gmsd = garmin_fs_alloc(-1);
863         wpt->fs.FsChainAdd(gmsd);
864       }
865       garmin_fs_t::set_wpt_class(gmsd, wpt_class);
866       gmsd->ilinks = il_root;
867       il_root = nullptr;
868     }
869 
870     while (il_root) {
871       garmin_ilink_t* il = il_root;
872       il_root = il_root->next;
873       xfree(il);
874     }
875   } /* ENDFOR: for (i = 0; i < points; i++) */
876 
877   /* VERSION DEPENDENT CODE */
878   if (gdb_ver <= GDB_VER_2) {
879     rte->rte_urls.AddUrlLink(UrlLink(fread_cstr()));
880   } else {
881     rte->rte_urls.AddUrlLink(UrlLink(gdb_fread_strlist()));
882 
883     int color_idx = FREAD_i32;
884     rte->line_color.bbggrr = gt_color_value(color_idx);
885     int autoroute = FREAD_C;
886     if (autoroute == 1) {
887       FREAD(tbuf, 6); /* unknown bytes */
888       int route_style = FREAD_C;
889       int calc_type = FREAD_i32;
890       int vehicle_type = FREAD_C;
891       int road_selection = FREAD_i32;
892       double driving_speed[5];
893       driving_speed[0] = FREAD_DBL;
894       driving_speed[1] = FREAD_DBL;
895       driving_speed[2] = FREAD_DBL;
896       driving_speed[3] = FREAD_DBL;
897       driving_speed[4] = FREAD_DBL;
898       FREAD(tbuf, 8); /* unknown bytes */
899 #if GDB_DEBUG
900       DBG(GDB_DBG_RTE, 1)
901       printf(MYNAME "-rte_pt: autoroute info: route style %d, calculation type %d, vehicle type %d, road selection %d\n"
902              "                            driving speeds (kph) %.0f, %.0f, %.0f, %.0f, %.0f\n",
903              route_style, calc_type, vehicle_type, road_selection,
904              driving_speed[0], driving_speed[1], driving_speed[2], driving_speed[3], driving_speed[4]);
905 #else
906       Q_UNUSED(route_style);
907       Q_UNUSED(calc_type);
908       Q_UNUSED(vehicle_type);
909       Q_UNUSED(road_selection);
910       Q_UNUSED(driving_speed);
911 #endif
912     }
913 
914     rte->rte_desc = fread_cstr();
915 #if 0
916     /* replace CRLF's with ", " */
917     if (rte->rte_desc) {
918       char* c = rte->rte_desc;
919       while ((c = strstr(c, "\r\n"))) {
920         *c++ = ',';
921         *c++ = ' ';
922       }
923     }
924 #endif
925   }
926   return rte;
927 }
928 
929 /*-----------------------------------------------------------------------------*/
930 
931 static route_head*
read_track()932 read_track()
933 {
934   char dummy;
935 
936   trk_ct++;
937 
938   auto* res = new route_head;
939   res->rte_name = fread_cstr();
940 //	res->rte_num = trk_ct;
941 
942   FREAD(&dummy, 1);		/* display - 1 byte */
943   int color_idx = FREAD_i32;		/* color -  1 dword */
944   res->line_color.bbggrr = gt_color_value(color_idx);
945 
946   int points = FREAD_i32;
947 
948   for (int index = 0; index < points; index++) {
949     auto* wpt = new Waypoint;
950 
951     trkpt_ct++;
952 
953     wpt->latitude = FREAD_LATLON;
954     wpt->longitude = FREAD_LATLON;
955     if (FREAD_C == 1) {
956       double alt = FREAD_DBL;
957       if (alt < 1.0e24) {
958         wpt->altitude = alt;
959       }
960     }
961     if (FREAD_C == 1) {
962       wpt->SetCreationTime(FREAD_i32);
963     }
964     if (FREAD_C == 1) {
965       WAYPT_SET(wpt, depth, FREAD_DBL);
966     }
967     if (FREAD_C == 1) {
968       WAYPT_SET(wpt, temperature, FREAD_DBL);
969     }
970 
971     track_add_wpt(res, wpt);
972   }
973 
974   /* VERSION DEPENDENT CODE */
975   if (gdb_ver >= GDB_VER_3) {
976     res->rte_urls.AddUrlLink(UrlLink(gdb_fread_strlist()));
977   } else { /* if (gdb_ver <= GDB_VER_2) */
978     res->rte_urls.AddUrlLink(UrlLink(FREAD_CSTR_AS_QSTR));
979   }
980 #if GDB_DEBUG
981   DBG(GDB_DBG_TRK, res->rte_urls.HasUrlLink())
982   printf(MYNAME "-trk \"%s\": url = %s\n",
983          qPrintable(res->rte_name), qPrintable(res->rte_urls.GetUrlLink().url_));
984 #endif
985   return res;
986 }
987 
988 /*******************************************************************************/
989 
990 static void
gdb_rd_init(const QString & fname)991 gdb_rd_init(const QString& fname)
992 {
993   fin = gbfopen_le(fname, "rb", MYNAME);
994   ftmp = gbfopen_le(nullptr, "wb", MYNAME);
995   read_file_header();
996   /* VERSION DEPENDENT CODE */
997   if (gdb_ver >= GDB_VER_UTF8) {
998     cet_convert_init(CET_CHARSET_UTF8, 1);
999   }
1000 
1001   wayptq_in.clear();
1002   wayptq_in_hidden.clear();
1003 
1004   bool via = gdb_opt_via;
1005   bool drop_wpt = gdb_opt_drop_hidden_wpt;
1006   gdb_roadbook = gdb_opt_roadbook;
1007   gdb_hide_wpt = via || drop_wpt || gdb_roadbook;
1008   gdb_hide_rpt = via || gdb_roadbook;
1009 
1010   waypt_ct = 0;
1011   waypth_ct = 0;
1012   rtept_ct = 0;
1013   trkpt_ct = 0;
1014   rte_ct = 0;
1015   trk_ct = 0;
1016 }
1017 
1018 static void
gdb_rd_deinit()1019 gdb_rd_deinit()
1020 {
1021   disp_summary(fin);
1022   gdb_flush_waypt_queue(&wayptq_in);
1023   gdb_flush_waypt_queue(&wayptq_in_hidden);
1024   gbfclose(ftmp);
1025   gbfclose(fin);
1026 }
1027 
1028 static void
read_data()1029 read_data()
1030 {
1031   int incomplete = 0;	/* number of incomplete reads */
1032 
1033   for (;;) {
1034     char typ;
1035     gt_waypt_classes_e wpt_class;
1036     Waypoint* wpt;
1037     route_head* trk, *rte;
1038 
1039     int len = FREAD_i32;
1040     if (FREAD(&typ, 1) < 1) {
1041       fatal(MYNAME ": Attempt to read past EOF.");
1042     }
1043     if (typ == 'V') {
1044       break;  /* break the loop */
1045     }
1046 
1047     gbfrewind(ftmp);
1048     gbfwrite(nullptr, 0, 0, ftmp);	/* truncate */
1049     gbfcopyfrom(ftmp, fin, len);
1050     gbfrewind(ftmp);
1051 
1052     gbfile* fsave = fin;			/* swap standard 'fin' with cached input */
1053     fin = ftmp;
1054 
1055     char dump = 1;
1056     wpt_class = GDB_DEF_CLASS;
1057 
1058     switch (typ) {
1059     case 'W':
1060       wpt = read_waypoint(&wpt_class);
1061       if (!gdb_hide_wpt || (wpt_class == 0)) {
1062         waypt_add(wpt);
1063         auto* dupe = new Waypoint(*wpt);
1064         wayptq_in.append(dupe);
1065       } else {
1066         wayptq_in_hidden.append(wpt);
1067       }
1068       break;
1069     case 'R':
1070       rte = read_route();
1071       if (rte) {
1072         route_add_head(rte);
1073       }
1074       break;
1075     case 'T':
1076       trk = read_track();
1077       if (trk) {
1078         track_add_head(trk);
1079       }
1080       break;
1081     default:
1082       dump = 0;	/* make a dump only for main types */
1083       break;
1084     }
1085 
1086     int delta = len - gbftell(ftmp);
1087     is_fatal(delta > 1000000, "Internal consistency error.  Delta too big");
1088 
1089     // Avoid finite loop on bogus beta files from '06.
1090     // THe 100000 is totally pulled from my hat.
1091     // is_fatal((delta > 1000000) || (delta < 0), "Internal GDB error; invalid delta.");
1092 
1093     if (dump && delta) {
1094       if (! incomplete++) {
1095         warning(MYNAME ":==========================================\n");
1096         warning(MYNAME ":===          W A R N I N G             ===\n");
1097       }
1098       if (typ == 'W')
1099         warning(MYNAME ":(%d%c-%02d): delta = %d (flag=%3d/%02x)-",
1100                 gdb_ver, typ, wpt_class, delta, waypt_flag, waypt_flag);
1101       else {
1102         warning(MYNAME ":(%d%c): delta = %d -", gdb_ver, typ, delta);
1103       }
1104       if (delta > 0) {
1105         char* buf = (char*) xmalloc(delta);
1106         if (FREAD(buf, delta) < 1) {
1107           fatal(MYNAME ": Attempt to read past EOF.\n");
1108         }
1109         for (int i = 0; i < delta; i++) {
1110           warning(" %02x", (unsigned char)buf[i]);
1111         }
1112         xfree(buf);
1113       }
1114       warning("\n");
1115     }
1116 
1117     fin = fsave;
1118   }
1119 
1120 
1121   if (incomplete) {
1122     warning(MYNAME ":------------------------------------------\n");
1123     warning(MYNAME ": \"%s\"\n", fin->name);
1124     warning(MYNAME ":------------------------------------------\n");
1125     warning(MYNAME ":       Please mail this information\n");
1126     warning(MYNAME "     and, if you can, the used GDB file\n");
1127     warning(MYNAME ":  to gpsbabel-misc@lists.sourceforge.net\n");
1128     warning(MYNAME ":==========================================\n");
1129   }
1130 }
1131 
1132 /*******************************************************************************/
1133 
1134 /*
1135  * reset_short_handle: used for waypoint, route and track names
1136  */
1137 static void
reset_short_handle(const char * defname)1138 reset_short_handle(const char* defname)
1139 {
1140   if (short_h != nullptr) {
1141     mkshort_del_handle(&short_h);
1142   }
1143 
1144   short_h = mkshort_new_handle();
1145 
1146   setshort_length(short_h, GDB_NAME_BUFFERLEN);
1147   setshort_badchars(short_h, "\r\n\t");
1148   setshort_mustupper(short_h, 0);
1149   setshort_mustuniq(short_h, 1);
1150   setshort_whitespace_ok(short_h, 1);
1151   setshort_repeating_whitespace_ok(short_h, 1);
1152   setshort_defname(short_h, defname);
1153 }
1154 
1155 /* ----------------------------------------------------------------------------*/
1156 
1157 static void
write_header()1158 write_header()
1159 {
1160   char buff[128], tbuff[32];
1161   char* c;
1162   int len, n = 0;
1163   struct tm tm;
1164 
1165   FWRITE_CSTR("MsRcf");
1166   FWRITE_i32(2);
1167 
1168   strncpy(buff, "Dx", sizeof(buff));
1169   buff[1] = 'k' - 1 + gdb_ver;
1170   FWRITE_CSTR(buff);
1171 
1172 #if 0
1173   /* Take this if anything is wrong with our self generated watermark */
1174   strncpy(buff, "A].SQA*Dec 27 2004*17:40:51", sizeof(buff));	/* MapSource V6.5 */
1175 #else
1176   /* This is our "Watermark" to show this file was created by GPSbabel */
1177 
1178   /* history:
1179 
1180   "A].GPSBabel_1.2.7-beta*Sep 13 2005*20:10:00" - gpsbabel V1.2.7 BETA
1181   "A].GPSBabel_1.2.8-beta*Jan 18 2006*20:11:00" - gpsbabel 1.2.8-beta01182006_clyde
1182   "A].GPSBabel_1.2.8-beta*Apr 18 2006*20:12:00" - gpsbabel 1.2.8-beta20060405
1183   "A].GPSBabel-1.3*Jul 02 2006*20:13:00" -        gpsbabel 1.3.0
1184   "A].GPSBabel-1.3.1*Sep 03 2006*20:14:00" -      gpsbabel 1.3.1
1185   "A].GPSBabel-1.3.2*Nov 01 2006*22:23:39" -      gpsbabel 1.3.2
1186 
1187   New since 11/01/2006:
1188   version:   version and release of gpsbabel (defined in configure.ac)
1189   timestamp: date and time of gdb.c (handled by CVS)
1190 
1191   "A].GPSBabel-1.3.2*Nov 01 2006*22:23:39" -      gpsbabel 1.3.2
1192   "A].GPSBabel-beta20061125*Feb 06 2007*23:24:14" gpsbabel beta20061125
1193   "A].GPSBabel-1.3.3*Feb 20 2007*20:51:15" -      gpsbabel 1.3.3
1194 
1195   */
1196 
1197   memset(&tm, 0, sizeof(tm));
1198 
1199   n = sscanf(gdb_release_date+7, "%d-%d-%d %d:%d:%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
1200   if (n != 6) {
1201     // The $Date string in gdb_release_date[] above is bad.
1202     fatal(MYNAME ": internal date format error on %s\n", gdb_release_date + 7);
1203   }
1204 
1205   tm.tm_year -= 1900;
1206   tm.tm_mon -= 1;
1207 
1208   n = strftime(tbuff, sizeof(tbuff), "%b %d %Y*%H:%M:%S", &tm);
1209   if (n == 0) {
1210     // The build of the struct tm was bad.
1211     fatal(MYNAME ": internal date generation error for %s\n", gdb_release_date + 7);
1212   }
1213 
1214   snprintf(buff, sizeof(buff), "A].GPSBabel-%s*%s", gpsbabel_version, tbuff);
1215 #endif
1216   len = strlen(buff);
1217   buff[2] = 2;
1218 
1219   c = buff;
1220   while ((c = strchr(c, '*'))) {
1221     *c++ = '\0';
1222   }
1223 
1224   FWRITE_i32(len);
1225   FWRITE(buff, len + 1);
1226   FWRITE_CSTR("MapSource");		/* MapSource magic */
1227 }
1228 
1229 /*-----------------------------------------------------------------------------*/
1230 
1231 /*
1232  * gdb_check_waypt: As implemented in waypt_add, but we have some leaks where
1233  *                  waypoints are modified after waypt_add. Maybe we need a data check
1234  *                  after each input module.
1235  */
1236 
1237 static void
gdb_check_waypt(Waypoint * wpt)1238 gdb_check_waypt(Waypoint* wpt)
1239 {
1240   double lat_orig = wpt->latitude;
1241   double lon_orig = wpt->longitude;
1242 
1243   if (wpt->latitude < -90) {
1244     wpt->latitude += 180;
1245   } else if (wpt->latitude > +90) {
1246     wpt->latitude -= 180;
1247   }
1248   if (wpt->longitude < -180) {
1249     wpt->longitude += 360;
1250   } else if (wpt->longitude > +180) {
1251     wpt->longitude -= 360;
1252   }
1253 
1254   if ((wpt->latitude < -90) || (wpt->latitude > 90.0))
1255     fatal("Invalid latitude %f in waypoint %s.\n",
1256           lat_orig, !wpt->shortname.isEmpty() ? qPrintable(wpt->shortname) : "<no name>");
1257   if ((wpt->longitude < -180) || (wpt->longitude > 180.0))
1258     fatal("Invalid longitude %f in waypoint %s.\n",
1259           lon_orig, !wpt->shortname.isEmpty() ? qPrintable(wpt->shortname) : "<no name>");
1260 }
1261 
1262 /*-----------------------------------------------------------------------------*/
1263 
1264 static void
write_waypoint(const Waypoint * wpt,const QString & shortname,garmin_fs_t * gmsd,const int icon,const int display)1265 write_waypoint(
1266   const Waypoint* wpt, const QString& shortname, garmin_fs_t* gmsd,
1267   const int icon, const int display)
1268 {
1269   char zbuf[32], ffbuf[32];
1270 
1271   waypt_ct++;	/* increase informational number of written waypoints */
1272 
1273   memset(zbuf, 0, sizeof(zbuf));
1274   memset(ffbuf, 0xFF, sizeof(ffbuf));
1275 
1276   int wpt_class = wpt->wpt_flags.fmt_use;		/* trick */
1277 
1278   FWRITE_CSTR(shortname);			/* unique (!!!) shortname */
1279   FWRITE_i32(wpt_class);			/* waypoint class */
1280   FWRITE_CSTR(garmin_fs_t::get_cc(gmsd, ""));		/* country code */
1281 
1282   if (wpt_class != 0) {
1283     waypth_ct++;
1284   }
1285 
1286 #ifdef GMSD_EXPERIMENTAL
1287   if (gmsd && gmsd->flags.subclass && (wpt_class >= gt_waypt_class_map_point)) {
1288     FWRITE(gmsd->subclass, sizeof(gmsd->subclass));
1289   } else
1290 #endif
1291   {
1292     FWRITE(zbuf, 4);		/* subclass part 1 */
1293     FWRITE(ffbuf, 12);		/* subclass part 2 */
1294     FWRITE(zbuf, 2);		/* subclass part 3 */
1295     FWRITE(ffbuf, 4);		/* unknown */
1296   }
1297 
1298   FWRITE_LATLON(wpt->latitude);		/* latitude */
1299   FWRITE_LATLON(wpt->longitude);		/* longitude */
1300   FWRITE_DBL(wpt->altitude, unknown_alt);	/* altitude */
1301   if (!wpt->notes.isEmpty()) {
1302     FWRITE_CSTR(wpt->notes);
1303   } else {
1304     FWRITE_CSTR(wpt->description);
1305   }
1306   FWRITE_DBL(WAYPT_GET(wpt, proximity, unknown_alt), unknown_alt);	/* proximity */
1307   FWRITE_i32(display);			/* display */
1308   FWRITE_i32(0);				/* color */
1309   FWRITE_i32(icon);			/* icon */
1310   FWRITE_CSTR(garmin_fs_t::get_city(gmsd, ""));	/* city */
1311   FWRITE_CSTR(garmin_fs_t::get_state(gmsd, ""));	/* state */
1312   FWRITE_CSTR(garmin_fs_t::get_facility(gmsd, ""));	/* facility */
1313   FWRITE_C(0);				/* unknown */
1314   FWRITE_DBL(WAYPT_GET(wpt, depth, unknown_alt), unknown_alt);	/* depth */
1315 
1316   /* VERSION DEPENDENT CODE */
1317   if (gdb_ver <= GDB_VER_2) {
1318     FWRITE(zbuf, 3);
1319     FWRITE(zbuf, 4);
1320     QString ld;
1321     if (wpt->HasUrlLink()) {
1322       UrlLink l = wpt->GetUrlLink();
1323       ld = l.url_;
1324     }
1325     QString descr = (wpt_class < gt_waypt_class_map_point) ?
1326                     ld : wpt->description;
1327     if ((descr != nullptr) && (wpt_class >= gt_waypt_class_map_point) && \
1328         descr == CSTRc(wpt->shortname)) {
1329       descr.clear();
1330     }
1331     FWRITE_CSTR(descr);
1332   } else { /* if (gdb_ver > GDB_VER_3) */
1333 //    url_link* url_next;
1334 //    const char* str;
1335     QString str;
1336 
1337     if (wpt_class < gt_waypt_class_map_point) {	/* street address */
1338       str  = garmin_fs_t::get_addr(gmsd, "");
1339     } else {
1340       str = "";
1341     }
1342     FWRITE_CSTR(str);
1343     FWRITE(zbuf, 5);				/* instruction dependent */
1344 
1345     /* GBD doesn't have a native description field */
1346     /* here we misuse the instruction field */
1347 #if 1
1348     QString d = wpt->description;
1349     if (wpt->description == wpt->shortname) {
1350       d.clear();
1351     }
1352     if (str == wpt->notes) {
1353       d.clear();
1354     }
1355     FWRITE_CSTR(d);				/* instruction */
1356 #else
1357     str = wpt->description;
1358     if (str && (strcmp(str, wpt->shortname) == 0)) {
1359       str = NULL;
1360     }
1361     if (str && wpt->notes && (strcmp(str, wpt->notes) == 0)) {
1362       str = NULL;
1363     }
1364     FWRITE_CSTR(str);				/* instruction */
1365 #endif
1366 
1367     FWRITE_i32(wpt->urls.size());
1368     foreach (UrlLink l, wpt->urls) {
1369       FWRITE_CSTR(l.url_);
1370     }
1371   }
1372 
1373   FWRITE_i16(garmin_fs_t::get_category(gmsd, gdb_category));
1374   FWRITE_DBL(WAYPT_GET(wpt, temperature, 0), 0);
1375   FWRITE_TIME(wpt->GetCreationTime().toTime_t());
1376 
1377   /* VERSION DEPENDENT CODE */
1378   if (gdb_ver >= GDB_VER_3) {
1379     QString str = garmin_fs_t::get_phone_nr(gmsd, "");
1380     if (!str.isEmpty()) {
1381       FWRITE_i32(1);
1382       FWRITE_CSTR(str);
1383       FWRITE_CSTR("");
1384     } else {
1385       FWRITE_i32(0);
1386     }
1387     FWRITE_CSTR(garmin_fs_t::get_country(gmsd, ""));
1388     FWRITE_CSTR(garmin_fs_t::get_postal_code(gmsd, ""));
1389   }
1390 }
1391 
1392 static void
route_compute_bounds(const route_head * rte,bounds * bounds)1393 route_compute_bounds(const route_head* rte, bounds* bounds)
1394 {
1395   waypt_init_bounds(bounds);
1396   foreach (Waypoint* wpt, rte->waypoint_list) {
1397     gdb_check_waypt(wpt);
1398     waypt_add_to_bounds(bounds, wpt);
1399   }
1400 }
1401 
1402 static void
route_write_bounds(bounds * bounds)1403 route_write_bounds(bounds* bounds)
1404 {
1405   if (waypt_bounds_valid(bounds)) {
1406     FWRITE_C(0);
1407     FWRITE_LATLON(bounds->max_lat);
1408     FWRITE_LATLON(bounds->max_lon);
1409     FWRITE_DBL(bounds->max_alt, unknown_alt);
1410     FWRITE_LATLON(bounds->min_lat);
1411     FWRITE_LATLON(bounds->min_lon);
1412     FWRITE_DBL(bounds->min_alt, -(unknown_alt));
1413   } else {
1414     FWRITE_C(1);
1415   }
1416 }
1417 
1418 static void
write_route(const route_head * rte,const QString & rte_name)1419 write_route(const route_head* rte, const QString& rte_name)
1420 {
1421   bounds bounds;
1422   char zbuf[32], ffbuf[32];
1423 
1424   memset(zbuf, 0, sizeof(zbuf));
1425   memset(ffbuf, 0xFF, sizeof(ffbuf));
1426 
1427   FWRITE_CSTR(rte_name);
1428   FWRITE_C(0);				/* display/autoname - 1 byte */
1429 
1430   route_compute_bounds(rte, &bounds);
1431   route_write_bounds(&bounds);
1432 
1433   int points = ELEMENTS(rte);
1434   FWRITE_i32(points);
1435 
1436   int index = 0;
1437 
1438   for (auto it =rte->waypoint_list.cbegin(); it != rte->waypoint_list.cend(); ++it) {
1439 
1440     Waypoint* wpt = *it;
1441     Waypoint* next = nullptr;
1442     if (index < points) {
1443       next = *std::next(it);
1444     }
1445 
1446     index++;
1447     rtept_ct++;	/* increase informational number of written route points */
1448 
1449     if (index == 1) {
1450       gdb_check_waypt(wpt);
1451     }
1452     if (index < points) {
1453       gdb_check_waypt(next);
1454     }
1455 
1456     Waypoint* test = gdb_find_wayptq(&wayptq_out, wpt, 1);
1457     if (test != nullptr) {
1458       wpt = test;
1459     } else {
1460       fatal(MYNAME ": Sorry, that should never happen!!!\n");
1461     }
1462 
1463     garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
1464 
1465     /* extra_data may contain a modified shortname */
1466     FWRITE_CSTR((wpt->extra_data) ? (char*)wpt->extra_data : wpt->shortname);
1467 
1468     int wpt_class = wpt->wpt_flags.fmt_use;			/* trick */
1469 
1470     FWRITE_i32(wpt_class);				/* waypoint class */
1471     FWRITE_CSTR(garmin_fs_t::get_cc(gmsd, ""));			/* country */
1472 #ifdef GMSD_EXPERIMENTAL
1473     if (gmsd && gmsd->flags.subclass && (wpt_class >= gt_waypt_class_map_point)) {
1474       FWRITE(gmsd->subclass, sizeof(gmsd->subclass));
1475     } else
1476 #endif
1477     {
1478       FWRITE(zbuf, 4);				/* subclass part 1 */
1479       FWRITE(ffbuf, 12);				/* subclass part 2 */
1480       FWRITE(zbuf, 2);				/* subclass part 3 */
1481       FWRITE(ffbuf, 4);				/* unknown */
1482     }
1483 
1484     FWRITE_C(0);					/* unknown value or string */
1485     FWRITE_C(3);					/* unknown 18 bytes starting with 0x03 */
1486     FWRITE(zbuf, 3);
1487     FWRITE(ffbuf, 4);
1488     FWRITE(zbuf, 10);
1489 
1490     if (index == points) {
1491       FWRITE_i32(0);		/* no more steps */
1492       FWRITE_C(1);		/* skip bounds */
1493     } else { /* if (index < points) */
1494       FWRITE_i32(2);		/* two interstep links */
1495 
1496       FWRITE_LATLON(wpt->latitude);
1497       FWRITE_LATLON(wpt->longitude);
1498       FWRITE_DBL(wpt->altitude, unknown_alt);
1499       FWRITE_LATLON(next->latitude);
1500       FWRITE_LATLON(next->longitude);
1501       FWRITE_DBL(next->altitude, unknown_alt);
1502 
1503       waypt_init_bounds(&bounds);
1504       waypt_add_to_bounds(&bounds, wpt);
1505       waypt_add_to_bounds(&bounds, next);
1506       route_write_bounds(&bounds);
1507 
1508     }
1509 
1510     /* VERSION DEPENDENT CODE */
1511     if (gdb_ver >= GDB_VER_2) {
1512       FWRITE(ffbuf, 8);
1513       if (gdb_ver >= GDB_VER_3) {
1514         FWRITE(zbuf, 2);
1515       }
1516     }
1517   }
1518 
1519   /* VERSION DEPENDENT CODE */
1520   if (gdb_ver <= GDB_VER_2) {
1521     if (rte->rte_urls.HasUrlLink()) {
1522       FWRITE_CSTR(rte->rte_urls.GetUrlLink().url_);
1523     } else {
1524       FWRITE_CSTR("");
1525     }
1526   } else { /* if (gdb_ver >= GDB_VER_3) */
1527     if (rte->rte_urls.HasUrlLink()) {
1528       FWRITE_CSTR_LIST(rte->rte_urls.GetUrlLink().url_);
1529     } else {
1530       FWRITE_CSTR_LIST("");
1531     }
1532     /* "Magenta" (14) is MapSource default */
1533     FWRITE_i32((rte->line_color.bbggrr < 0) ? 14 : gt_color_index_by_rgb(rte->line_color.bbggrr));
1534     FWRITE_C(0);
1535     FWRITE_CSTR(rte->rte_desc);
1536   }
1537 }
1538 
1539 static void
write_track(const route_head * trk,const QString & trk_name)1540 write_track(const route_head* trk, const QString& trk_name)
1541 {
1542   int points = ELEMENTS(trk);
1543 
1544   FWRITE_CSTR(trk_name);
1545   FWRITE_C(0);
1546   /* "Unknown" (0) is MapSource default */
1547   FWRITE_i32(gt_color_index_by_rgb(trk->line_color.bbggrr));
1548 
1549   FWRITE_i32(points);	/* total number of waypoints in waypoint list */
1550 
1551   foreach (const Waypoint* wpt, trk->waypoint_list) {
1552 
1553     trkpt_ct++;	/* increase informational number of written route points */
1554 
1555     FWRITE_LATLON(wpt->latitude);
1556     FWRITE_LATLON(wpt->longitude);
1557     FWRITE_DBL(wpt->altitude, unknown_alt);
1558     FWRITE_TIME(wpt->GetCreationTime().toTime_t());
1559     double d = WAYPT_GET(wpt, depth, unknown_alt);
1560     FWRITE_DBL(d, unknown_alt);
1561     d = WAYPT_GET(wpt, temperature, -99999);
1562     FWRITE_DBL(d, -99999);
1563   }
1564 
1565   /* finalize track */
1566 
1567   /* VERSION DEPENDENT CODE */
1568   if (gdb_ver <= GDB_VER_2) {
1569     if (trk->rte_urls.HasUrlLink()) {
1570       FWRITE_CSTR(trk->rte_urls.GetUrlLink().url_);
1571     } else {
1572       FWRITE_CSTR("");
1573     }
1574   } else { /* if (gdb_ver >= GDB_VER_3 */
1575     if (trk->rte_urls.HasUrlLink()) {
1576       FWRITE_CSTR_LIST(trk->rte_urls.GetUrlLink().url_);
1577     } else {
1578       FWRITE_CSTR_LIST("");
1579     }
1580   }
1581 }
1582 
1583 /*-----------------------------------------------------------------------------*/
1584 
1585 static void
finalize_item(gbfile * origin,const char identifier)1586 finalize_item(gbfile* origin, const char identifier)
1587 {
1588   int len = gbftell(fout);
1589 
1590   fout = origin;
1591   gbfseek(ftmp, 0, SEEK_SET);
1592 
1593   FWRITE_i32(len);
1594   FWRITE_C(identifier);
1595   gbfcopyfrom(fout, ftmp, len);
1596 
1597   gbfseek(ftmp, 0, SEEK_SET);	/* Truncate memory stream */
1598   gbfwrite(nullptr, 0, 0, ftmp);
1599 }
1600 
1601 /*-----------------------------------------------------------------------------*/
1602 
1603 static void
write_waypoint_cb(const Waypoint * refpt)1604 write_waypoint_cb(const Waypoint* refpt)
1605 {
1606   /* do this when backup always happens in main */
1607 // but, but, casting away the const here is wrong...
1608   (const_cast<Waypoint*>(refpt))->shortname = refpt->shortname.trimmed();
1609   Waypoint* test = gdb_find_wayptq(&wayptq_out, refpt, 1);
1610 
1611   if (refpt->HasUrlLink() && test && test->HasUrlLink() && route_flag == 0) {
1612     UrlLink orig_link = refpt->GetUrlLink();
1613     UrlLink test_link = test->GetUrlLink();
1614     if (orig_link.url_ != test_link.url_) {
1615       test = nullptr;
1616     }
1617   }
1618 
1619   if ((test != nullptr) && (route_flag == 0)) {
1620     if (test->notes != refpt->notes) {
1621       test = nullptr;
1622     }
1623   }
1624 
1625   if (test == nullptr) {
1626     int display;
1627     auto* wpt = new Waypoint(*refpt);
1628 
1629     gdb_check_waypt(wpt);
1630     wayptq_out.append(wpt);
1631 
1632     gbfile* fsave = fout;
1633     fout = ftmp;
1634 
1635     /* prepare the waypoint */
1636     garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
1637 
1638     int wpt_class = garmin_fs_t::get_wpt_class(gmsd, -1);
1639     if (wpt_class == -1) {
1640       wpt_class = (route_flag) ? GDB_DEF_HIDDEN_CLASS : GDB_DEF_CLASS;
1641     }
1642     wpt->wpt_flags.fmt_use = wpt_class; 	/* trick, we need this for the route(s) */
1643 
1644     int icon = garmin_fs_t::get_icon(gmsd, -1);
1645     if (icon < 0) {
1646       if (wpt->icon_descr.isNull()) {
1647         icon = GDB_DEF_ICON;
1648       } else {
1649         icon = gt_find_icon_number_from_desc(wpt->icon_descr, GDB);
1650       }
1651     }
1652 
1653     switch (garmin_fs_t::get_display(gmsd, -1)) {		/* display */
1654     case -1:
1655       if (wpt_class < 8) {
1656         display = gt_gdb_display_mode_symbol_and_name;
1657       } else {
1658         display = gt_gdb_display_mode_symbol;
1659       }
1660       break;
1661     case gt_display_mode_symbol:
1662       display = gt_gdb_display_mode_symbol;
1663       break;
1664     case gt_display_mode_symbol_and_comment:
1665       display = gt_gdb_display_mode_symbol_and_comment;
1666       break;
1667     default:
1668       display = gt_gdb_display_mode_symbol_and_name;
1669       break;
1670     }
1671 
1672     QString name = wpt->shortname;
1673 
1674     if (global_opts.synthesize_shortnames || name.isEmpty()) {
1675       name = wpt->notes;
1676       if (name.isEmpty()) {
1677         name = wpt->description;
1678       }
1679       if (name.isEmpty()) {
1680         name = wpt->shortname;
1681       }
1682     }
1683 
1684     name = mkshort(short_h, name);
1685     write_waypoint(wpt, name, gmsd, icon, display);
1686 
1687     finalize_item(fsave, 'W');
1688   }
1689 }
1690 
1691 static void
write_route_cb(const route_head * rte)1692 write_route_cb(const route_head* rte)
1693 {
1694   if (ELEMENTS(rte) <= 0) {
1695     return;
1696   }
1697 
1698   QString name;
1699   if (rte->rte_name.isNull()) {
1700     name = mkshort(short_h, QString::asprintf("Route%04d", rte->rte_num));
1701   } else {
1702     name = mkshort(short_h, rte->rte_name);
1703   }
1704 
1705   rte_ct++;	/* increase informational number of written routes */
1706 
1707   gbfile* fsave = fout;
1708   fout = ftmp;
1709   write_route(rte, name);
1710   finalize_item(fsave, 'R');
1711 }
1712 
1713 static void
write_track_cb(const route_head * trk)1714 write_track_cb(const route_head* trk)
1715 {
1716   if (ELEMENTS(trk) <= 0) {
1717     return;
1718   }
1719 
1720   QString name;
1721   if (trk->rte_name.isNull()) {
1722     name = mkshort(short_h, QString::asprintf("Track%04d", trk->rte_num));
1723   } else {
1724     name = mkshort(short_h, trk->rte_name);
1725   }
1726 
1727   trk_ct++;	/* increase informational number of written tracks */
1728 
1729   gbfile* fsave = fout;
1730   fout = ftmp;
1731   write_track(trk, name);
1732 
1733   finalize_item(fsave, 'T');
1734 }
1735 
1736 /*-----------------------------------------------------------------------------*/
1737 
1738 static void
gdb_wr_init(const QString & fname)1739 gdb_wr_init(const QString& fname)
1740 {
1741   fout = gbfopen_le(fname, "wb", MYNAME);
1742   ftmp = gbfopen_le(nullptr, "wb", MYNAME);
1743 
1744   gdb_category = (gdb_opt_category) ? atoi(gdb_opt_category) : 0;
1745   gdb_ver = (gdb_opt_ver && *gdb_opt_ver) ? atoi(gdb_opt_ver) : 0;
1746 
1747   if (gdb_category) {
1748     is_fatal((gdb_category < 1) || (gdb_category > 16),
1749              MYNAME ": cat must be between 1 and 16!");
1750     gdb_category = 1 << (gdb_category - 1);
1751   }
1752 
1753   if (gdb_opt_bitcategory) {
1754     gdb_category = strtol(gdb_opt_bitcategory, nullptr, 0);
1755   }
1756 
1757   if (gdb_ver >= GDB_VER_UTF8) {
1758     cet_convert_init(CET_CHARSET_UTF8, 1);
1759   }
1760 
1761   wayptq_out.clear();
1762   short_h = nullptr;
1763 
1764   waypt_ct = 0;
1765   waypth_ct = 0;
1766   rtept_ct = 0;
1767   trkpt_ct = 0;
1768   rte_ct = 0;
1769   trk_ct = 0;
1770 }
1771 
1772 static void
gdb_wr_deinit()1773 gdb_wr_deinit()
1774 {
1775   disp_summary(fout);
1776   gdb_flush_waypt_queue(&wayptq_out);
1777   mkshort_del_handle(&short_h);
1778   gbfclose(fout);
1779   gbfclose(ftmp);
1780 }
1781 
1782 static void
write_data()1783 write_data()
1784 {
1785   if (gdb_opt_ver) {
1786     gdb_ver = atoi(gdb_opt_ver);
1787   }
1788   write_header();
1789 
1790   reset_short_handle("WPT");
1791   route_flag = 0;
1792   waypt_disp_all(write_waypoint_cb);
1793   route_flag = 1;
1794   route_disp_all(nullptr, nullptr, write_waypoint_cb);
1795 
1796   reset_short_handle("Route");
1797   route_disp_all(write_route_cb, nullptr, nullptr);
1798 
1799   reset_short_handle("Track");
1800   track_disp_all(write_track_cb, nullptr, nullptr);
1801 
1802   FWRITE_i32(2);			/* finalize gdb with empty map segment */
1803   FWRITE_CSTR("V");
1804   FWRITE_C(1);
1805 }
1806 
1807 /*******************************************************************************/
1808 
1809 static QVector<arglist_t> gdb_args = {
1810   {
1811     "cat", &gdb_opt_category,
1812     "Default category on output (1..16)",
1813     nullptr, ARGTYPE_INT, "1", "16", nullptr
1814   },
1815   {
1816     "bitscategory", &gdb_opt_bitcategory, "Bitmap of categories",
1817     nullptr, ARGTYPE_INT, "1", "65535", nullptr
1818   },
1819   {
1820     "ver", &gdb_opt_ver,
1821     "Version of gdb file to generate (1..3)",
1822     "2", ARGTYPE_INT, "1", "3", nullptr
1823   },
1824   {
1825     "via", &gdb_opt_via,
1826     "Drop route points that do not have an equivalent waypoint (hidden points)",
1827     nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
1828   },
1829   {
1830     "dropwpt", &gdb_opt_drop_hidden_wpt,
1831     "Don't create waypoints for non-user points",
1832     nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
1833   },
1834   {
1835     "roadbook", &gdb_opt_roadbook,
1836     "Include major turn points (with description) from calculated route",
1837     nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
1838   },
1839 
1840 };
1841 
1842 ff_vecs_t gdb_vecs = {
1843   ff_type_file,
1844   FF_CAP_RW_ALL,
1845   gdb_rd_init,
1846   gdb_wr_init,
1847   gdb_rd_deinit,
1848   gdb_wr_deinit,
1849   read_data,
1850   write_data,
1851   nullptr,
1852   &gdb_args,
1853   CET_CHARSET_MS_ANSI, 0	/* O.K.: changed to NON-FIXED */
1854   /* because of utf8 strings since GDB V3 */
1855   , NULL_POS_OPS,
1856   nullptr
1857 };
1858 
1859 /*******************************************************************************/
1860