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