1 /*
2 Access to Garmin MapSource files.
3 Based on information provided by Ian Cowley & Mark Bradley
4
5 Copyright (C) 2002 Robert Lipe, robertlipe+source@gpsbabel.org
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 /* #define MPS_DEBUG 0 */
23
24 #include <cstdio> // for SEEK_CUR, sprintf, SEEK_SET, EOF, size_t
25 #include <cstdlib> // for atoi, rand, srand
26 #include <cstring> // for strcpy, memset, strlen, strcmp
27 #include <ctime> // for time_t
28 #include <cstdio> // for SEEK_CUR
29
30 #include <QtCore/QChar> // for QChar
31 #include <QtCore/QFile> // for QFile
32 #include <QtCore/QList> // for QList
33 #include <QtCore/QString> // for QString, operator==
34 #include <QtCore/QtGlobal> // for foreach
35
36 #include "defs.h"
37 #include "garmin_tables.h" // for gt_find_icon_number_from_desc, MAPSOURCE, gt_find_desc_from_icon_number, GARMIN_SERIAL, PCX, garmin_formats_e
38 #include "gbfile.h" // for gbfwrite, gbfread, gbfgetint32, gbfputint32, gbfseek, gbfputc, gbfgetc, gbfputdbl, gbfgetdbl, gbfclose, gbfeof, gbfile, gbftell, gbfgetcstr, gbfputs, gbfopen_le
39 #include "jeeps/gpsmath.h" // for GPS_Math_Deg_To_Semi, GPS_Math_Semi_To_Deg
40 #include "src/core/datetime.h" // for DateTime
41
42
43 static gbfile* mps_file_in;
44 static gbfile* mps_file_out;
45 static gbfile* mps_file_temp;
46 static short_handle mkshort_handle;
47
48 static int mps_ver_in = 0;
49 static int mps_ver_out = 0;
50 static int mps_ver_temp = 0;
51
52 /* Temporary pathname used when merging gpsbabel output with an existing file */
53 static QString tempname;
54 static QString fin_name;
55
56 static const Waypoint* prevRouteWpt;
57 /* Private queues of written out waypoints */
58 static QList<Waypoint *> written_wpt_head;
59 static QList<Waypoint *> written_route_wpt_head;
60 static short_handle written_wpt_mkshort_handle;
61
62 /* Private queue of read in waypoints assumed to be used only for routes */
63 static QList<Waypoint *> read_route_wpt_head;
64 static short_handle read_route_wpt_mkshort_handle;
65
66 #define MPSDEFAULTWPTCLASS 0
67 #define MPSHIDDENROUTEWPTCLASS 8
68
69 #define MYNAME "MAPSOURCE"
70 #define ISME 0
71 #define NOTME 1
72
73 #define DEFAULTICONDESCR "Waypoint"
74 #define DEFAULTICONVALUE 18
75
76 #define MPSNAMEBUFFERLEN 1024
77 #define MPSNOTESBUFFERLEN 4096
78 #define MPSDESCBUFFERLEN 4096
79
80
81 static char* snlen = nullptr;
82 static char* snwhiteopt = nullptr;
83 static char* mpsverout = nullptr;
84 static char* mpsmergeouts = nullptr;
85 static int mpsmergeout;
86 static char* mpsusedepth = nullptr;
87 static char* mpsuseprox = nullptr;
88
89 static
90 QVector<arglist_t> mps_args = {
91 {
92 "snlen", &snlen, "Length of generated shortnames", "10", ARGTYPE_INT, "1",
93 nullptr, nullptr
94 },
95 {
96 "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames",
97 nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
98 },
99 {
100 "mpsverout", &mpsverout,
101 "Version of mapsource file to generate (3,4,5)", nullptr,
102 ARGTYPE_INT, ARG_NOMINMAX, nullptr
103 },
104 {
105 "mpsmergeout", &mpsmergeouts, "Merge output with existing file",
106 nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
107 },
108 {
109 "mpsusedepth", &mpsusedepth,
110 "Use depth values on output (default is ignore)", nullptr,
111 ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
112 },
113 {
114 "mpsuseprox", &mpsuseprox,
115 "Use proximity values on output (default is ignore)",
116 nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
117 },
118 };
119
120 static void
mps_wpt_q_init(QList<Waypoint * > * whichQueue)121 mps_wpt_q_init(QList<Waypoint *>* whichQueue)
122 {
123 whichQueue->clear();
124 }
125
126 static void
mps_wpt_q_deinit(QList<Waypoint * > * whichQueue)127 mps_wpt_q_deinit(QList<Waypoint *>* whichQueue)
128 {
129 while (!whichQueue->isEmpty()) {
130 delete whichQueue->takeFirst();
131 }
132 }
133
134 /*
135 * Find a waypoint that we've already written out
136 *
137 */
138 static Waypoint*
mps_find_wpt_q_by_name(const QList<Waypoint * > * whichQueue,const QString & name)139 mps_find_wpt_q_by_name(const QList<Waypoint *>* whichQueue, const QString& name)
140 {
141 foreach (Waypoint* waypointp, *whichQueue) {
142 if (waypointp->shortname == name) {
143 return waypointp;
144 }
145 }
146 return nullptr;
147 }
148
149 /*
150 * Add a waypoint that we've already written out to our list
151 *
152 */
153 static void
mps_wpt_q_add(QList<Waypoint * > * whichQueue,const Waypoint * wpt)154 mps_wpt_q_add(QList<Waypoint *>* whichQueue, const Waypoint* wpt)
155 {
156 auto* written_wpt = new Waypoint(*wpt);
157 whichQueue->append(written_wpt);
158 }
159
160 static int
mps_converted_icon_number(const int icon_num,const int mpsver,garmin_formats_e garmin_format)161 mps_converted_icon_number(const int icon_num, const int mpsver, garmin_formats_e garmin_format)
162 {
163 int def_icon = DEFAULTICONVALUE;
164
165 switch (garmin_format) {
166 case MAPSOURCE:
167 if (mpsver == 5) {
168 return icon_num;
169 }
170 if (mpsver == 4) {
171 /* Water hydrant */
172 if (icon_num == 139) {
173 return def_icon;
174 } else {
175 return icon_num;
176 }
177 } else {
178 /* the Contact icons - V3 doesn't have anything like this */
179 if ((icon_num >= 119) && (icon_num <= 138)) {
180 return def_icon;
181 }
182 /* the Geocache icons - V3 use the Circle with X */
183 if ((icon_num >= 117) && (icon_num <= 118)) {
184 return 65;
185 }
186 /* Water hydrant */
187 if (icon_num == 139) {
188 return def_icon;
189 }
190 return icon_num;
191 }
192
193 case PCX:
194 case GARMIN_SERIAL:
195 if (mpsver == 5) {
196 return icon_num;
197 }
198 if (mpsver == 4) {
199 /* Water hydrant */
200 if (icon_num == 8282) {
201 return def_icon;
202 } else {
203 return icon_num;
204 }
205 }
206 /* the Contact icons - V3 doesn't have anything like this */
207 if ((icon_num >= 8257) && (icon_num <= 8276)) {
208 return def_icon;
209 }
210 /* the Geocache icons - V3 use the Circle with X */
211 if ((icon_num >= 8255) && (icon_num <= 8256)) {
212 return 179;
213 }
214 /* Water hydrant */
215 if (icon_num == 8282) {
216 return def_icon;
217 }
218 return icon_num;
219
220 default:
221 fatal(MYNAME ": unknown garmin format.\n");
222 }
223 return def_icon;
224 }
225
226 static void
mps_rd_init(const QString & fname)227 mps_rd_init(const QString& fname)
228 {
229 mps_file_in = gbfopen_le(fname, "rb", MYNAME);
230
231 read_route_wpt_mkshort_handle = mkshort_new_handle();
232 /* initialise the "private" queue of waypoints read for routes */
233 mps_wpt_q_init(&read_route_wpt_head);
234 }
235
236 static void
mps_rd_deinit()237 mps_rd_deinit()
238 {
239 gbfclose(mps_file_in);
240 if (read_route_wpt_mkshort_handle) {
241 mkshort_del_handle(&read_route_wpt_mkshort_handle);
242 }
243 /* flush the "private" queue of waypoints read for routes */
244 mps_wpt_q_deinit(&read_route_wpt_head);
245 }
246
247 static void
mps_wr_init(const QString & fname)248 mps_wr_init(const QString& fname)
249 {
250 fin_name = fname;
251 if (mpsmergeouts) {
252 mpsmergeout = atoi(mpsmergeouts);
253 }
254
255 if (mpsmergeout) {
256 mps_file_out = gbfopen_le(fname, "rb", MYNAME);
257 if (mps_file_out == nullptr) {
258 mpsmergeout = 0;
259 } else {
260 gbfclose(mps_file_out);
261 srand((unsigned) current_time().toTime_t());
262
263 for (;;) {
264 /* create a temporary name based on a random char and the existing name */
265 /* then test if it already exists, if so try again with another rand num */
266 /* yeah, yeah, so there's probably a library function for this */
267 tempname = QString("%1.%2").arg(fname).arg(rand(), 8, 16, QChar('0'));
268 mps_file_temp = gbfopen_le(tempname, "rb", MYNAME);
269 if (mps_file_temp == nullptr) {
270 break;
271 }
272 gbfclose(mps_file_temp);
273 }
274 QFile::rename(fname, tempname);
275 mps_file_temp = gbfopen_le(tempname, "rb", MYNAME);
276 }
277 }
278
279 mps_file_out = gbfopen_le(fname, "wb", MYNAME);
280
281 written_wpt_mkshort_handle = mkshort_new_handle();
282 /* initialise the "private" queue of waypoints written */
283 mps_wpt_q_init(&written_wpt_head);
284 mps_wpt_q_init(&written_route_wpt_head);
285 }
286
287 static void
mps_wr_deinit()288 mps_wr_deinit()
289 {
290 gbfclose(mps_file_out);
291
292 if (mpsmergeout) {
293 gbfclose(mps_file_temp);
294 QFile::remove(tempname);
295 tempname.clear();
296 }
297
298 if (written_wpt_mkshort_handle) {
299 mkshort_del_handle(&written_wpt_mkshort_handle);
300 }
301 /* flush the "private" queue of waypoints written */
302 mps_wpt_q_deinit(&written_wpt_head);
303 mps_wpt_q_deinit(&written_route_wpt_head);
304 fin_name.clear();
305 }
306
307 /*
308 * get characters until and including terminating NULL from mps_file_in
309 * and write into buf.
310 */
311 static void
mps_readstr(gbfile * mps_file,char * buf,size_t sz)312 mps_readstr(gbfile* mps_file, char* buf, size_t sz)
313 {
314 int c;
315 buf[sz-1] = 0;
316 while (--sz && (c = gbfgetc(mps_file)) != EOF) {
317 *buf++ = c;
318 if (c == 0) {
319 return;
320 }
321 }
322 }
323
324 /*
325 * read in from file to check a) valid format b) version of data formatting
326 * MRCB
327 */
328 static void
mps_fileHeader_r(gbfile * mps_file,int * mps_ver)329 mps_fileHeader_r(gbfile* mps_file, int* mps_ver)
330 {
331 char hdr[100];
332
333 mps_readstr(mps_file, hdr, sizeof(hdr));
334 if (strcmp(hdr, "MsRcd")) {
335 fatal(MYNAME ": This doesn't look like a mapsource file.\n");
336 }
337 /* Read record length of "format details" section */
338 int reclen = gbfgetint32(mps_file);
339 /* Read the "format details" in plus the trailing null */
340 gbfread(hdr, 3, 1, mps_file);
341 if (hdr[0] != 'D') {
342 /* No flag for the "data" section */
343 fatal(MYNAME ": This doesn't look like a mapsource file.\n");
344 }
345 if (hdr[1] == 'd') {
346 *mps_ver = 3;
347 } else if ((hdr[1] > 'd') && (hdr[1] <= 'h')) {
348 *mps_ver = 4;
349 } else if ((hdr[1] > 'h') && (hdr[1] <= 'i')) {
350 *mps_ver = 5;
351 } else {
352 fatal(MYNAME ": Unsupported version of mapsource file.\n");
353 }
354 /* Skip reliably over the "format details" section */
355 gbfseek(mps_file, reclen+1-3, SEEK_CUR);
356 /* Read record length of "program signature" section */
357 reclen = gbfgetint32(mps_file);
358 /* Skip reliably over the "program signature" section */
359 if (reclen >= 0) {
360 gbfseek(mps_file, reclen+1, SEEK_CUR);
361 }
362 }
363
364 /*
365 * write out to file
366 * MRCB
367 */
368 static void
mps_fileHeader_w(gbfile * mps_file,int mps_ver)369 mps_fileHeader_w(gbfile* mps_file, int mps_ver)
370 {
371 char hdr[100];
372
373 strcpy(hdr, "MsRc");
374 gbfwrite(hdr, 4, 1, mps_file);
375
376 /* Between versions 3 & 5 this value is 'd', but might change in the future */
377 strcpy(hdr, "d");
378 gbfwrite(hdr, 2, 1, mps_file); /* include trailing NULL char */
379
380 /* Start of a "Data" section */
381 hdr[0] = 'D';
382 /* if (mps_ver == 3) */
383 hdr[1] = 'd'; /* equates to V3.02 */
384 if (mps_ver == 4) {
385 hdr[1] = 'g'; /* equates to V4.06 */
386 }
387 if (mps_ver == 5) {
388 hdr[1] = 'i'; /* equates to V5.0 */
389 }
390 hdr[2] = 0;
391
392 int reclen = 2; /* this is 3 byte record */
393 gbfputint32(reclen, mps_file);
394 gbfwrite(hdr, 3, 1, mps_file); /* reclen + 1 */
395
396 hdr[0] = 'A';
397 /* if (mps_ver == 3) */
398 hdr[1] = 0x2E;
399 hdr[2] = 0x01; /* equates to V3.02 */
400 hdr[3] = 'S';
401 hdr[4] = 'Q';
402 hdr[5] = 'A';
403 hdr[6] = 0;
404 strcpy(hdr+7,"Oct 20 1999");
405 strcpy(hdr+19,"12:50:33");
406 if (mps_ver == 4) {
407 hdr[1] = (char) 0x96; /* equates to V4.06 */
408 strcpy(hdr+7,"Oct 22 2001");
409 strcpy(hdr+19,"15:45:33");
410 }
411 if (mps_ver == 5) {
412 hdr[1] = (char) 0xF4; /* equates to V5.0 */
413 strcpy(hdr+7,"Jul 3 2003");
414 strcpy(hdr+19,"08:35:33");
415 }
416
417 reclen = 27; /* pre measured! */
418 gbfputint32(reclen, mps_file);
419 gbfwrite(hdr, 28, 1, mps_file); /* reclen + 1 - can't use this as reclen may be wrongendian now */
420 }
421
422 /*
423 * read in from file a map segment record
424 * MRCB
425 */
426 static void
mps_mapsegment_r(gbfile * mps_file,int mps_ver)427 mps_mapsegment_r(gbfile* mps_file, int mps_ver)
428 {
429 int reclen;
430
431 (void)mps_ver;
432
433 #if 0
434 /* At the moment we're not doing anything with map segments, but here's the template code as if we were */
435 char hdr[100];
436 gbfread(&CDid, 4, 1, mps_file);
437 reclen = le_read32(&CDid);
438
439 gbfread(&CDSegmentid, 4, 1, mps_file);
440 reclen = le_read32(&CDSegmentid);
441
442 mps_readstr(mps_file, CDName, sizeof(CDName));
443 mps_readstr(mps_file, CDSegmentName, sizeof(CDSegmentName));
444 mps_readstr(mps_file, CDAreaName, sizeof(CDAreaName));
445
446 gbfread(hdr, 4, 1, mps_file); /* trailing long value */
447 #endif
448
449 gbfseek(mps_file, -5, SEEK_CUR);
450 reclen = gbfgetint32(mps_file);
451 if (reclen >= 0) {
452 gbfseek(mps_file, reclen+1, SEEK_CUR);
453 }
454 }
455
456
457 /*
458 * read in from file a mapsetname record
459 * there should always be one of these at the end of the file
460 * MRCB
461 */
462 static void
mps_mapsetname_r(gbfile * mps_file,int mps_ver)463 mps_mapsetname_r(gbfile* mps_file, int mps_ver)
464 {
465 (void)mps_ver;
466
467 /* At the moment we're not doing anything with mapsetnames, but here's the template code as if we were
468 char hdr[100];
469 mps_readstr(mps_file, hdr, sizeof(hdr));
470 char mapsetnamename[very large number?];
471 strcpy(mapsetnamename,hdr);
472 char mapsetnameAutonameFlag;
473 gbfread(&mapsetnameAutonameFlag, 1, 1, mps_file); */
474
475 gbfseek(mps_file, -5, SEEK_CUR);
476 int reclen = gbfgetint32(mps_file);
477 gbfseek(mps_file, reclen+1, SEEK_CUR);
478 }
479
480
481 /*
482 * write out to file a mapsetname record
483 * there should always be one of these at the end of the file
484 * MRCB
485 */
486 static void
mps_mapsetname_w(gbfile * mps_file,int mps_ver)487 mps_mapsetname_w(gbfile* mps_file, int mps_ver)
488 {
489 char hdr[100];
490
491 (void)mps_ver;
492
493 hdr[0] = 'V'; /* mapsetname start of record indicator */
494 hdr[1] = 0; /* zero length null terminated string */
495 hdr[2] = 1; /* mapsetname autoname flag set to DO autoname */
496 int reclen = 2; /* three bytes of the V record */
497 gbfputint32(reclen, mps_file);
498 gbfwrite(hdr, 3, 1, mps_file); /* reclen + 1 */
499 }
500
501
502 /*
503 * read in from file a waypoint record
504 * MRCB
505 */
506 static void
mps_waypoint_r(gbfile * mps_file,int mps_ver,Waypoint ** wpt,unsigned int * mpsclass)507 mps_waypoint_r(gbfile* mps_file, int mps_ver, Waypoint** wpt, unsigned int* mpsclass)
508 {
509 char tbuf[100];
510 char wptname[MPSNAMEBUFFERLEN];
511 double mps_altitude = unknown_alt;
512 double mps_proximity = unknown_alt;
513 double mps_depth = unknown_alt;
514
515 auto* thisWaypoint = new Waypoint;
516 *wpt = thisWaypoint;
517
518 mps_readstr(mps_file, wptname, sizeof(wptname));
519
520 (*mpsclass) = gbfgetint32(mps_file); /* class */
521 mps_readstr(mps_file, tbuf, sizeof(tbuf)); /* country */
522
523 gbfread(tbuf,17, 1, mps_file); /* subclass data (17) */
524
525 if ((mps_ver == 4) || (mps_ver == 5)) {
526 gbfread(tbuf, 5, 1, mps_file); /* additional subclass data (1) & terminator? (4) */
527 }
528
529 int lat = gbfgetint32(mps_file);
530 int lon = gbfgetint32(mps_file);
531
532 if (gbfgetc(mps_file) == 1) { /* altitude validity */
533 mps_altitude = gbfgetdbl(mps_file);
534 } else {
535 mps_altitude = unknown_alt;
536 gbfseek(mps_file, 8, SEEK_CUR);
537 }
538
539 QString wptdesc = gbfgetcstr(mps_file);
540
541 if (gbfgetc(mps_file) == 1) { /* proximity validity */
542 mps_proximity = gbfgetdbl(mps_file);
543 } else {
544 mps_proximity = unknown_alt;
545 gbfseek(mps_file, 8, SEEK_CUR);
546 }
547
548 (void) gbfgetint32(mps_file); /* display flag */
549 (void) gbfgetint32(mps_file); /* colour */
550 int icon = gbfgetint32(mps_file); /* display symbol */
551
552 mps_readstr(mps_file, tbuf, sizeof(tbuf)); /* city */
553 mps_readstr(mps_file, tbuf, sizeof(tbuf)); /* state */
554 mps_readstr(mps_file, tbuf, sizeof(tbuf)); /*facility */
555
556 gbfread(tbuf, 1, 1, mps_file); /* unknown */
557
558 if (gbfgetc(mps_file) == 1) { /* depth validity */
559 mps_depth = gbfgetdbl(mps_file);
560 } else {
561 mps_depth = unknown_alt;
562 (void) gbfseek(mps_file, 8, SEEK_CUR);
563 }
564
565 if ((mps_ver == 4) || (mps_ver == 5)) {
566 gbfread(tbuf, 6, 1, mps_file); /* unknown */
567 thisWaypoint->notes = gbfgetcstr(mps_file);
568 } else {
569 gbfread(tbuf, 2, 1, mps_file); /* unknown */
570 }
571
572 thisWaypoint->shortname = wptname;
573 thisWaypoint->description = wptdesc;
574 thisWaypoint->latitude = GPS_Math_Semi_To_Deg(lat);
575 thisWaypoint->longitude = GPS_Math_Semi_To_Deg(lon);
576 thisWaypoint->altitude = mps_altitude;
577 if (mps_proximity != unknown_alt) {
578 WAYPT_SET(thisWaypoint, proximity, mps_proximity);
579 }
580 if (mps_depth != unknown_alt) {
581 WAYPT_SET(thisWaypoint, depth, mps_depth);
582 }
583
584 /* might need to change this to handle version dependent icon handling */
585 thisWaypoint->icon_descr = gt_find_desc_from_icon_number(icon, MAPSOURCE);
586 }
587
588 /*
589 * write out to file a waypoint record
590 * MRCB
591 */
592 static void
mps_waypoint_w(gbfile * mps_file,int mps_ver,const Waypoint * wpt,const bool isRouteWpt)593 mps_waypoint_w(gbfile* mps_file, int mps_ver, const Waypoint* wpt, const bool isRouteWpt)
594 {
595 char zbuf[25];
596 char ffbuf[25];
597 int display = 1;
598 int colour = 0; /* (unknown colour) black is 1, white is 16 */
599
600 double mps_altitude = wpt->altitude;
601 double mps_proximity = (mpsuseprox ? WAYPT_GET(wpt, proximity, unknown_alt) : unknown_alt);
602 double mps_depth = unknown_alt;
603
604 int lat = GPS_Math_Deg_To_Semi(wpt->latitude);
605 int lon = GPS_Math_Deg_To_Semi(wpt->longitude);
606 if (WAYPT_HAS(wpt, depth) && mpsusedepth) {
607 mps_depth = wpt->depth;
608 }
609 QString src;
610 if (!wpt->description.isEmpty()) {
611 src = wpt->description;
612 }
613 if (!wpt->notes.isEmpty()) {
614 src = wpt->notes;
615 }
616 QString ident = global_opts.synthesize_shortnames ?
617 mkshort(mkshort_handle, src) :
618 CSTRc(wpt->shortname);
619
620 memset(zbuf, 0, sizeof(zbuf));
621 memset(ffbuf, 0xff, sizeof(ffbuf));
622
623 /* might need to change this to handle version dependent icon handling */
624 int icon = gt_find_icon_number_from_desc(wpt->icon_descr, MAPSOURCE);
625 if (get_cache_icon(wpt)) {
626 icon = gt_find_icon_number_from_desc(get_cache_icon(wpt), MAPSOURCE);
627 }
628
629 icon = mps_converted_icon_number(icon, mps_ver, MAPSOURCE);
630
631 /* two NULL (0x0) bytes at end of each string */
632 char* ascii_description = xstrdup(wpt->description);
633 int reclen = ident.length() + strlen(ascii_description) + 2;
634 if ((mps_ver == 4) || (mps_ver == 5)) {
635 /* v4.06 & V5.0*/
636 reclen += 85; /* "W" (1) + strlen(name) + NULL (1) + class(4) + country(sz) +
637 subclass(18) + unknown(4) + lat(4) + lon(4) + alt(9) + strlen(desc)
638 + NULL (1) + prox(9) + display(4) + colour(4) + symbol(4) + city(sz) +
639 state(sz) + facility(sz) + unknown2(1) + depth(9) + unknown3(7) */
640 /* -1 as reclen is interpreted from zero meaning a reclength of one */
641 if (!wpt->notes.isEmpty()) {
642 reclen += strlen(CSTRc(wpt->notes));
643 }
644 } else {
645 /* v3.02 */
646 reclen += 75; /* "W" (1) + strlen(name) + NULL (1) + + class(4) + country(sz) +
647 subclass(17) + lat(4) + lon(4) + alt(9) + strlen(desc) +
648 NULL (1) + prox(9) + display(4) +
649 colour(4) + symbol(4) + city(sz) + state(sz) + facility(sz) +
650 unknown2(1) + depth(9) + unknown3(2) */
651 /* -1 as reclen is interpreted from zero meaning a reclength of one */
652 }
653
654 gbfputint32(reclen, mps_file);
655 gbfwrite("W", 1, 1, mps_file);
656 gbfputs(ident, mps_file);
657 gbfwrite(zbuf, 1, 1, mps_file); /* NULL termination to ident */
658
659 if (isRouteWpt) {
660 zbuf[0] = (char)MPSHIDDENROUTEWPTCLASS;
661 } else {
662 zbuf[0] = (char)MPSDEFAULTWPTCLASS;
663 }
664 gbfwrite(zbuf, 4, 1, mps_file); /* class */
665
666 zbuf[0]=0;
667 gbfwrite(zbuf, 1, 1, mps_file); /* country empty string */
668
669 if ((mps_ver == 4) || (mps_ver == 5)) {
670 gbfwrite(zbuf, 4, 1, mps_file); /* subclass part 1 */
671 gbfwrite(ffbuf, 12, 1, mps_file); /* subclass part 2 */
672 gbfwrite(zbuf, 2, 1, mps_file); /* subclass part 3 */
673 gbfwrite(ffbuf, 4, 1, mps_file); /* unknown */
674 } else {
675 gbfwrite(zbuf, 8, 1, mps_file);
676 gbfwrite(ffbuf, 8, 1, mps_file);
677 gbfwrite(zbuf, 1, 1, mps_file);
678 }
679
680 gbfputint32(lat, mps_file);
681 gbfputint32(lon, mps_file);
682
683 if (mps_altitude == unknown_alt) {
684 gbfwrite(zbuf, 9, 1, mps_file);
685 } else {
686 gbfputc(1, mps_file);
687 gbfputdbl(mps_altitude, mps_file);
688 }
689 if (!wpt->description.isEmpty()) {
690 gbfputs(ascii_description, mps_file);
691 }
692 gbfwrite(zbuf, 1, 1, mps_file); /* NULL termination */
693 xfree(ascii_description);
694 ascii_description = nullptr;
695
696 if (mps_proximity == unknown_alt) {
697 gbfwrite(zbuf, 9, 1, mps_file);
698 } else {
699 gbfputc(1, mps_file);
700 gbfputdbl(mps_proximity, mps_file);
701 }
702
703 gbfputint32(display, mps_file); /* Show waypoint w/ name */
704 gbfputint32(colour, mps_file);
705 gbfputint32(icon, mps_file);
706
707 gbfwrite(zbuf, 3, 1, mps_file); /* city, state, facility */
708
709 gbfwrite(zbuf, 1, 1, mps_file); /* unknown */
710
711 if (mps_depth == unknown_alt) {
712 gbfwrite(zbuf, 9, 1, mps_file);
713 } else {
714 gbfputc(1, mps_file);
715 gbfputdbl(mps_depth, mps_file);
716 }
717
718 gbfwrite(zbuf, 2, 1, mps_file); /* unknown */
719 if ((mps_ver == 4) || (mps_ver == 5)) {
720 gbfwrite(zbuf, 4, 1, mps_file); /* unknown */
721 if (!wpt->notes.isEmpty()) {
722 gbfputs(wpt->notes, mps_file);
723 }
724 gbfwrite(zbuf, 1, 1, mps_file); /* string termination */
725 }
726 }
727
728 /*
729 * wrapper to include the mps_ver_out information
730 * A waypoint is only written if it hasn't been written before
731 * based on it shortname alone
732 *
733 */
734 static void
mps_waypoint_w_unique_wrapper(const Waypoint * wpt)735 mps_waypoint_w_unique_wrapper(const Waypoint* wpt)
736 {
737 /* Search for this waypoint in the ones already written */
738 Waypoint* wptfound = mps_find_wpt_q_by_name(&written_wpt_head, CSTRc(wpt->shortname));
739 /* is the next line necessary? Assumes we know who's called us and in what order */
740 if (wptfound == nullptr) {
741 wptfound = mps_find_wpt_q_by_name(&written_route_wpt_head, CSTRc(wpt->shortname));
742 }
743
744 /* if this waypoint hasn't been written then it is okay to do so */
745 if (wptfound == nullptr) {
746 mps_waypoint_w(mps_file_out, mps_ver_out, wpt, false);
747
748 /* ensure we record in our "private" queue what has been
749 written so that we don't write it again */
750 mps_wpt_q_add(&written_wpt_head, wpt);
751 }
752 }
753
754 /*
755 * wrapper to include the mps_ver_out information
756 * A waypoint is only written if it hasn't been written before
757 * based on it shortname alone
758 * Provided as a separate function from above in case we find
759 * have to do other things
760 *
761 */
762 static void
mps_route_wpt_w_unique_wrapper(const Waypoint * wpt)763 mps_route_wpt_w_unique_wrapper(const Waypoint* wpt)
764 {
765 /* Search for this waypoint in the ones already written */
766 Waypoint* wptfound = mps_find_wpt_q_by_name(&written_wpt_head, CSTRc(wpt->shortname));
767 if (wptfound == nullptr)
768 /* so, not a real wpt, so must check route wpts already written as reals */
769 {
770 wptfound = mps_find_wpt_q_by_name(&written_route_wpt_head, CSTRc(wpt->shortname));
771 }
772
773 /* if this waypoint hasn't been written then it is okay to do so
774 but assume it is only required for the route
775 */
776 if (wptfound == nullptr) {
777 /* Although we haven't written one out, this might still be a "real" waypoint
778 If so, we need to write it out now accordingly */
779 wptfound = find_waypt_by_name(wpt->shortname);
780
781 if (wptfound == nullptr) {
782 /* well, we tried to find: it wasn't written and isn't a real waypoint */
783 mps_waypoint_w(mps_file_out, mps_ver_out, wpt, true);
784 mps_wpt_q_add(&written_route_wpt_head, wpt);
785 } else {
786 mps_waypoint_w(mps_file_out, mps_ver_out, wpt, false);
787 /* Simulated real user waypoint */
788 mps_wpt_q_add(&written_wpt_head, wpt);
789 }
790 }
791 }
792 #if 0
793 /*
794 * wrapper to include the mps_ver_out information
795 * This one always writes a waypoint. If it has been written before
796 * then generate a unique name before writing
797 *
798 */
799 static void
800 mps_waypoint_w_uniqloc_wrapper(Waypoint* wpt)
801 {
802 Waypoint* wptfound = NULL;
803 char* newName;
804
805 /* Search for this waypoint in the ones already written */
806 wptfound = mps_find_wpt_q_by_name(&written_wpt_head, wpt->shortname);
807 /* is the next line necessary? Assumes we know who's called us and in what order */
808 if (wptfound == NULL) {
809 wptfound = mps_find_wpt_q_by_name(&written_route_wpt_head, wpt->shortname);
810 }
811
812 if (wptfound != NULL) {
813 /* check if this is the same waypoint by looking at the lat lon
814 not ideal, but better then having two same named waypoints
815 that kills MapSource. If it is the same then don't bother
816 adding it in. If it isn't, then rename it
817 */
818 if (((wpt->latitude - wptfound->latitude) != 0) ||
819 ((wpt->longitude - wptfound->longitude) != 0)) {
820 /* Not the same lat lon, so rename and add */
821 newName = mkshort(written_wpt_mkshort_handle, wpt->shortname);
822 wptfound = new Waypoint(*wpt);
823 xfree(wptfound->shortname);
824 wptfound->shortname = newName;
825 mps_waypoint_w(mps_file_out, mps_ver_out, wptfound, false);
826 mps_wpt_q_add(&written_wpt_head, wpt);
827 }
828 } else {
829 mps_waypoint_w(mps_file_out, mps_ver_out, wpt, false);
830 /* ensure we record in out "private" queue what has been
831 written so that we don't write it again */
832 mps_wpt_q_add(&written_wpt_head, wpt);
833 }
834 }
835 #endif
836
837 /*
838 * read in from file a route record
839 * MRCB
840 */
841 static void
mps_route_r(gbfile * mps_file,int mps_ver,route_head ** rte)842 mps_route_r(gbfile* mps_file, int mps_ver, route_head** rte)
843 {
844 char tbuf[100];
845 char wptname[MPSNAMEBUFFERLEN];
846 int lat = 0;
847 int lon = 0;
848 char rte_autoname;
849 int interlinkStepCount;
850 unsigned int mpsclass;
851
852 route_head* rte_head;
853 int rte_count;
854
855 Waypoint* thisWaypoint;
856 Waypoint* tempWpt;
857
858 double mps_altitude = unknown_alt;
859 double mps_depth = unknown_alt;
860
861 QString rtename = gbfgetcstr(mps_file);
862 #ifdef MPS_DEBUG
863 fprintf(stderr, "mps_route_r: reading route %s\n", rtename);
864 #endif
865
866 gbfread(&rte_autoname, 1, 1, mps_file); /* autoname flag */
867
868 gbfread(tbuf, 1, 1, mps_file); /* skip min/max values */
869 if (tbuf[0] == 0) {
870
871 lat = gbfgetint32(mps_file); /* max lat of whole route */
872 lon = gbfgetint32(mps_file); /* max lon of whole route */
873
874 if (gbfgetc(mps_file) == 1) { /* altitude validity */
875 mps_altitude = gbfgetdbl(mps_file);
876 } else {
877 mps_altitude = unknown_alt;
878 gbfseek(mps_file, 8, SEEK_CUR);
879 }
880
881 lat = gbfgetint32(mps_file); /* min lat of whole route */
882 lon = gbfgetint32(mps_file); /* min lon of whole route */
883
884 if (gbfgetc(mps_file) == 1) { /* altitude validity */
885 mps_altitude = gbfgetdbl(mps_file);
886 } else {
887 mps_altitude = unknown_alt;
888 gbfseek(mps_file, 8, SEEK_CUR);
889 }
890 }
891
892 rte_count = gbfgetint32(mps_file); /* number of waypoints in route */
893
894 /* This might be rather presumptuous, but is it valid in any format to have route with no points? */
895 /* Let's assume not, so if the route count is zero or less, let's get out of here and allow the */
896 /* caller to do any file resync */
897 if (rte_count < 0) {
898 return;
899 }
900
901 #ifdef MPS_DEBUG
902 fprintf(stderr, "mps_route_r: route contains %d waypoints\n", rte_count);
903 #endif
904
905 rte_head = new route_head;
906 rte_head->rte_name = rtename;
907 route_add_head(rte_head);
908 *rte = rte_head;
909
910 rte_count--; /* need to loop round for one less than the number of waypoints */
911
912 while (rte_count--) {
913
914 mps_readstr(mps_file, wptname, sizeof(wptname));
915 #ifdef MPS_DEBUG
916 fprintf(stderr, "mps_route_r: reading route waypoint %s\n", wptname);
917 #endif
918
919 mpsclass = gbfgetint32(mps_file); /* class */
920 (void)mpsclass;
921 mps_readstr(mps_file, tbuf, sizeof(tbuf)); /* country */
922
923 if ((mps_ver == 4) || (mps_ver == 5)) {
924 gbfread(tbuf, 22, 1, mps_file); /* subclass data */
925
926 /* This is a bit unpleasant. Routes have a variable length of
927 data (min 22 bytes) terminated by a zero */
928 do {
929 gbfread(tbuf, 1, 1, mps_file);
930 } while (tbuf[0] && !gbfeof(mps_file));
931
932 /* The next thing is the unknown 0x03 0x00 .. 0x00 (18 bytes) */
933 gbfread(tbuf, 18, 1, mps_file);
934 } else {
935 gbfread(tbuf, 17, 1, mps_file); /* subclass data */
936 gbfread(tbuf, 18, 1, mps_file); /* unknown 0x00 0x03 0x00 .. 0x00 */
937 }
938
939 /* link details */
940 interlinkStepCount = gbfgetint32(mps_file); /* NOT always 2, but will assume > 0 */
941
942 #ifdef MPS_DEBUG
943 fprintf(stderr, "mps_route_r: interlink steps are %d\n", interlinkStepCount);
944 #endif
945
946 /* Basically we're knackered if the step count is less than one since we hard code reading of the */
947 /* first, so if there isn't one, we'd lose sync on the file and read junk */
948 /* Given we've already done some route head allocation, do we return or do we die? It'd be good */
949 /* do some clean up before returning. */
950 if (interlinkStepCount < 1) {
951 /* For RJL - are the following lines correct ? */
952 /* route_free(rte_head);
953 route_del_head(rte_head); */
954 return;
955 }
956
957 /* first end of link */
958 lat = gbfgetint32(mps_file);
959 lon = gbfgetint32(mps_file);
960
961 if (gbfgetc(mps_file) == 1) { /* altitude validity */
962 mps_altitude = gbfgetdbl(mps_file);
963 } else {
964 mps_altitude = unknown_alt;
965 gbfseek(mps_file, 8, SEEK_CUR);
966 }
967
968 /* with MapSource routes, the real waypoint details are held as a separate waypoint, so copy from there
969 if found. With MapSource, one should consider the real waypoint list as definitive */
970 tempWpt = find_waypt_by_name(wptname);
971
972 if (tempWpt != nullptr) {
973 thisWaypoint = new Waypoint(*tempWpt);
974 } else {
975 tempWpt = mps_find_wpt_q_by_name(&read_route_wpt_head, wptname);
976
977 if (tempWpt != nullptr) {
978 thisWaypoint = new Waypoint(*tempWpt);
979 } else {
980 /* should never reach here, but we do need a fallback position */
981 #ifdef MPS_DEBUG
982 fprintf(stderr, "mps_route_r: reached the point we never should\n");
983 #endif
984 thisWaypoint = new Waypoint;
985 thisWaypoint->shortname = wptname;
986 thisWaypoint->latitude = GPS_Math_Semi_To_Deg(lat);
987 thisWaypoint->longitude = GPS_Math_Semi_To_Deg(lon);
988 thisWaypoint->altitude = mps_altitude;
989 if (mps_depth != unknown_alt) {
990 WAYPT_SET(thisWaypoint, depth, mps_depth);
991 }
992 }
993 }
994
995 route_add_wpt(rte_head, thisWaypoint);
996
997 /* take two off the count since we separately read the start and end parts of the link */
998 /* MRCB 2004/09/15 - NOPE, sorry, this needs to one, since interlink steps can be > 0 */
999 for (int thisInterlinkStep = interlinkStepCount - 1; thisInterlinkStep > 0; thisInterlinkStep--) {
1000 /* Could do this by doing a calculation on length of each co-ordinate and just doing one read
1001 but doing it this way makes it easier in the future to make use of this data */
1002 lat = gbfgetint32(mps_file);
1003 lon = gbfgetint32(mps_file);
1004
1005 if (gbfgetc(mps_file) == 1) { /* altitude validity */
1006 mps_altitude = gbfgetdbl(mps_file);
1007 } else {
1008 mps_altitude = unknown_alt;
1009 gbfseek(mps_file, 8, SEEK_CUR);
1010 }
1011 }
1012
1013 gbfread(tbuf, 1, 1, mps_file); /* NULL */
1014
1015 gbfread(tbuf, 4, 1, mps_file); /* link max lat */
1016 gbfread(tbuf, 4, 1, mps_file); /* link max lon */
1017 gbfread(tbuf, 9, 1, mps_file); /* link max alt validity + alt */
1018
1019 gbfread(tbuf, 4, 1, mps_file); /* link min lat */
1020 gbfread(tbuf, 4, 1, mps_file); /* link min lon */
1021 gbfread(tbuf, 9, 1, mps_file); /* link min alt validity + alt */
1022
1023 } /* while (trk_count--) */
1024
1025 /* when the loop is done, there's still one waypoint to read with a small trailer */
1026 /* all we want is the waypoint name; lat, lon and alt are already set from above */
1027 mps_readstr(mps_file, wptname, sizeof(wptname));
1028 #ifdef MPS_DEBUG
1029 fprintf(stderr, "mps_route_r: reading final route waypoint %s\n", wptname);
1030 #endif
1031
1032
1033 mpsclass = gbfgetint32(mps_file); /* class */
1034 mps_readstr(mps_file, tbuf, sizeof(tbuf)); /* country */
1035
1036 if ((mps_ver == 4) || (mps_ver == 5)) {
1037 gbfread(tbuf, 22, 1, mps_file); /* subclass data */
1038
1039 /* This is a bit unpleasant. Routes have a variable length of
1040 data (min 22 bytes) terminated by a zero */
1041 do {
1042 gbfread(tbuf, 1, 1, mps_file);
1043 } while (tbuf[0] && !gbfeof(mps_file));
1044
1045 /* The next thing is the unknown 0x03 0x00 .. 0x00 (18 bytes) */
1046 gbfread(tbuf, 18, 1, mps_file);
1047 } else {
1048 gbfread(tbuf, 17, 1, mps_file); /* subclass data */
1049 gbfread(tbuf, 18, 1, mps_file); /* unknown 0x00 0x03 0x00 .. 0x00 */
1050 }
1051
1052 gbfread(tbuf, 5, 1, mps_file); /* 5 byte trailer */
1053 /* with MapSource routes, the real waypoint details are held as a separate waypoint, so copy from there
1054 if found because there is more info held in a real waypoint than in its route counterpart,
1055 e.g. the display symbol (aka icon)
1056 */
1057 tempWpt = find_waypt_by_name(wptname);
1058
1059 if (tempWpt != nullptr) {
1060 thisWaypoint = new Waypoint(*tempWpt);
1061 } else {
1062 tempWpt = mps_find_wpt_q_by_name(&read_route_wpt_head, wptname);
1063
1064 if (tempWpt != nullptr) {
1065 thisWaypoint = new Waypoint(*tempWpt);
1066 } else {
1067 /* should never reach here, but we do need a fallback position */
1068 thisWaypoint = new Waypoint;
1069 thisWaypoint->shortname = wptname;
1070 thisWaypoint->latitude = GPS_Math_Semi_To_Deg(lat);
1071 thisWaypoint->longitude = GPS_Math_Semi_To_Deg(lon);
1072 thisWaypoint->altitude = mps_altitude;
1073 }
1074 }
1075
1076 route_add_wpt(rte_head, thisWaypoint);
1077 }
1078
1079 /*
1080 * write out to file a route header
1081 * MRCB
1082 */
1083 static void
mps_routehdr_w(gbfile * mps_file,int mps_ver,const route_head * rte)1084 mps_routehdr_w(gbfile* mps_file, int mps_ver, const route_head* rte)
1085 {
1086 char* rname;
1087 char hdr[20];
1088 char zbuf[20];
1089
1090 time_t uniqueValue = 0;
1091
1092 double maxlat=-90.0;
1093 double maxlon=-180.0;
1094 double minlat=90.0;
1095 double minlon=180.0;
1096 double maxalt=unknown_alt;
1097 double minalt=-unknown_alt;
1098
1099 prevRouteWpt = nullptr; /* clear the stateful flag used to know when the start of route wpts happens */
1100
1101 memset(zbuf, 0, sizeof(zbuf));
1102
1103 /* total nodes (waypoints) this route */
1104 unsigned int rte_datapoints = 0;
1105 int allWptNameLengths = 0;
1106
1107 //if (rte->waypoint_list.next) { /* this test doesn't do what I want i.e test if this is a valid route - treat as a placeholder for now */
1108 if (true) {
1109 foreach (const Waypoint* testwpt, rte->waypoint_list) {
1110 if (rte_datapoints == 0) {
1111 uniqueValue = testwpt->GetCreationTime().toTime_t();
1112 }
1113 if (testwpt->latitude > maxlat) {
1114 maxlat = testwpt->latitude;
1115 }
1116 if (testwpt->latitude < minlat) {
1117 minlat = testwpt->latitude;
1118 }
1119 if (testwpt->longitude > maxlon) {
1120 maxlon = testwpt->longitude;
1121 }
1122 if (testwpt->longitude < minlon) {
1123 minlon = testwpt->longitude;
1124 }
1125 if (testwpt->altitude != unknown_alt) {
1126 if ((testwpt->altitude > maxalt) ||
1127 (maxalt == unknown_alt)) {
1128 maxalt = testwpt->altitude;
1129 }
1130 if ((testwpt->altitude < minalt) ||
1131 (minalt == -unknown_alt)) {
1132 minalt = testwpt->altitude;
1133 }
1134 }
1135
1136 QString src;
1137 if (!testwpt->description.isEmpty()) {
1138 src = testwpt->description;
1139 }
1140 if (!testwpt->notes.isEmpty()) {
1141 src = testwpt->notes;
1142 }
1143 QString ident = global_opts.synthesize_shortnames ?
1144 mkshort(mkshort_handle, src) :
1145 CSTRc(testwpt->shortname);
1146 allWptNameLengths += ident.length() + 1;
1147
1148 rte_datapoints++;
1149 }
1150
1151 if (uniqueValue == 0) {
1152 uniqueValue = current_time().toTime_t();
1153 }
1154
1155 /* route name */
1156 if (rte->rte_name.isEmpty()) {
1157 sprintf(hdr, "Route%04x", (unsigned) uniqueValue);
1158 rname = xstrdup(hdr);
1159 } else {
1160 rname = xstrdup(rte->rte_name);
1161 }
1162
1163 int rname_len = strlen(rname);
1164 unsigned int reclen = rname_len + 42; /* "T" (1) + strlen(tname) + NULL (1) + autoname flag (2) +
1165 route lat lon max (2x4) + route max alt (9) +
1166 route lat lon min (2x4) + route min alt (9) +
1167 num route datapoints value (4) */
1168
1169 /* V3 - each waypoint: waypoint name + NULL (1) + class (4) + country + NULL (1) +
1170 subclass (17) + unknown (18) */
1171 /* V4,5 - each waypoint: waypoint name + NULL (1) + class (4) + country + NULL (1) +
1172 subclass (18) + unknown (4) + unknown (19) */
1173 /* V* - each route link: 0x00000002 (4) + end 1 lat (4) + end 1 lon (4) + end 1 alt (9) +
1174 end 2 lat (4) + end 2 lon (4) + end 2 alt (9) + NULL (1) +
1175 link max lat (4) + link max lon (4) + link max alt (9) +
1176 link min lat (4) + link min lon (4) + link min alt (9) */
1177
1178 if ((mps_ver == 4) || (mps_ver == 5)) {
1179 reclen += allWptNameLengths + rte_datapoints * 46 +
1180 (rte_datapoints - 1) * 73 + 4; /* link details plus overall trailing bytes */
1181 } else {
1182 reclen += allWptNameLengths + rte_datapoints * 40 +
1183 (rte_datapoints - 1) * 73 + 4; /* link details plus overall trailing bytes */
1184 }
1185
1186 gbfputint32(reclen, mps_file);
1187 gbfputc('R', mps_file);
1188 gbfwrite(rname, 1, rname_len, mps_file);
1189
1190 xfree(rname);
1191
1192 hdr[0] = 0; /* NULL of string termination */
1193 hdr[1] = 0; /* don't autoname */
1194 hdr[2] = 0; /* MSB of don't autoname */
1195 gbfwrite(hdr, 3, 1, mps_file); /* NULL string terminator + route autoname flag */
1196
1197 int lat = GPS_Math_Deg_To_Semi(maxlat);
1198 int lon = GPS_Math_Deg_To_Semi(maxlon);
1199
1200 gbfputint32(lat, mps_file);
1201 gbfputint32(lon, mps_file);
1202
1203 if (maxalt == unknown_alt) {
1204 gbfwrite(zbuf, 9, 1, mps_file);
1205 } else {
1206 gbfputc(1, mps_file);
1207 gbfputdbl(maxalt, mps_file);
1208 }
1209
1210 lat = GPS_Math_Deg_To_Semi(minlat);
1211 lon = GPS_Math_Deg_To_Semi(minlon);
1212
1213 gbfputint32(lat, mps_file);
1214 gbfputint32(lon, mps_file);
1215
1216 if (minalt == -unknown_alt) {
1217 gbfwrite(zbuf, 9, 1, mps_file);
1218 } else {
1219 gbfputc(1, mps_file);
1220 gbfputdbl(minalt, mps_file);
1221 }
1222
1223 gbfputint32(rte_datapoints, mps_file);
1224 }
1225 }
1226
1227 static void
mps_routehdr_w_wrapper(const route_head * rte)1228 mps_routehdr_w_wrapper(const route_head* rte)
1229 {
1230 mps_routehdr_w(mps_file_out, mps_ver_out, rte);
1231 }
1232
1233
1234 /*
1235 * write out to file a route datapoint
1236 * MRCB
1237 */
1238 static void
mps_routedatapoint_w(gbfile * mps_file,int mps_ver,const Waypoint * rtewpt)1239 mps_routedatapoint_w(gbfile* mps_file, int mps_ver, const Waypoint* rtewpt)
1240 {
1241 char zbuf[20];
1242 char ffbuf[20];
1243
1244 int maxlat;
1245 int maxlon;
1246 int minlat;
1247 int minlon;
1248 double maxalt=unknown_alt;
1249 double minalt=-unknown_alt;
1250
1251 memset(zbuf, 0, sizeof(zbuf));
1252 memset(ffbuf, 0xff, sizeof(ffbuf));
1253
1254 if (prevRouteWpt != nullptr) {
1255 /* output the route link details */
1256 int reclen = 2;
1257 gbfputint32(reclen, mps_file);
1258
1259 /* output end point 1 */
1260 int lat = GPS_Math_Deg_To_Semi(prevRouteWpt->latitude);
1261 int lon = GPS_Math_Deg_To_Semi(prevRouteWpt->longitude);
1262
1263 gbfputint32(lat, mps_file);
1264 gbfputint32(lon, mps_file);
1265
1266 double mps_altitude = prevRouteWpt->altitude;
1267 if (mps_altitude == unknown_alt) {
1268 gbfwrite(zbuf, 9, 1, mps_file);
1269 } else {
1270 gbfputc(1, mps_file);
1271 gbfputdbl(mps_altitude, mps_file);
1272 }
1273
1274 /* output end point 2 */
1275 lat = GPS_Math_Deg_To_Semi(rtewpt->latitude);
1276 lon = GPS_Math_Deg_To_Semi(rtewpt->longitude);
1277
1278 gbfputint32(lat, mps_file);
1279 gbfputint32(lon, mps_file);
1280
1281 mps_altitude = rtewpt->altitude;
1282 if (mps_altitude == unknown_alt) {
1283 gbfwrite(zbuf, 9, 1, mps_file);
1284 } else {
1285 gbfputc(1, mps_file);
1286 gbfputdbl(mps_altitude, mps_file);
1287 }
1288
1289 if (rtewpt->latitude > prevRouteWpt->latitude) {
1290 maxlat = GPS_Math_Deg_To_Semi(rtewpt->latitude);
1291 minlat = GPS_Math_Deg_To_Semi(prevRouteWpt->latitude);
1292 } else {
1293 minlat = GPS_Math_Deg_To_Semi(rtewpt->latitude);
1294 maxlat = GPS_Math_Deg_To_Semi(prevRouteWpt->latitude);
1295 }
1296
1297 if (rtewpt->longitude > prevRouteWpt->longitude) {
1298 maxlon = GPS_Math_Deg_To_Semi(rtewpt->longitude);
1299 minlon = GPS_Math_Deg_To_Semi(prevRouteWpt->longitude);
1300 } else {
1301 minlon = GPS_Math_Deg_To_Semi(rtewpt->longitude);
1302 maxlon = GPS_Math_Deg_To_Semi(prevRouteWpt->longitude);
1303 }
1304
1305 if (rtewpt->altitude != unknown_alt) {
1306 maxalt = rtewpt->altitude;
1307 }
1308 if (rtewpt->altitude != unknown_alt) {
1309 minalt = rtewpt->altitude;
1310 }
1311 if (prevRouteWpt->altitude != unknown_alt) {
1312 if ((prevRouteWpt->altitude > maxalt) ||
1313 (maxalt == unknown_alt)) {
1314 maxalt = prevRouteWpt->altitude;
1315 }
1316 if ((prevRouteWpt->altitude < minalt) ||
1317 (minalt == -unknown_alt)) {
1318 minalt = prevRouteWpt->altitude;
1319 }
1320 }
1321
1322 gbfwrite(zbuf, 1, 1, mps_file);
1323
1324 /* output max coords of the link */
1325 gbfputint32(maxlat, mps_file);
1326 gbfputint32(maxlon, mps_file);
1327
1328 if (maxalt == unknown_alt) {
1329 gbfwrite(zbuf, 9, 1, mps_file);
1330 } else {
1331 gbfputc(1, mps_file);
1332 gbfputdbl(maxalt, mps_file);
1333 }
1334
1335 /* output min coords of the link */
1336 gbfputint32(minlat, mps_file);
1337 gbfputint32(minlon, mps_file);
1338
1339 if (minalt == -unknown_alt) {
1340 gbfwrite(zbuf, 9, 1, mps_file);
1341 } else {
1342 gbfputc(1, mps_file);
1343 gbfputdbl(minalt, mps_file);
1344 }
1345
1346 }
1347
1348 QString src;
1349 if (!rtewpt->description.isEmpty()) {
1350 src = rtewpt->description;
1351 }
1352 if (!rtewpt->notes.isEmpty()) {
1353 src = rtewpt->notes;
1354 }
1355 QString ident = global_opts.synthesize_shortnames ?
1356 mkshort(mkshort_handle, src) :
1357 CSTRc(rtewpt->shortname);
1358
1359 gbfputs(ident, mps_file);
1360 gbfwrite(zbuf, 1, 1, mps_file); /* NULL termination to ident */
1361
1362 Waypoint* wptfound = mps_find_wpt_q_by_name(&written_route_wpt_head, ident);
1363 if (wptfound != nullptr) {
1364 zbuf[0] = (char)MPSHIDDENROUTEWPTCLASS;
1365 } else {
1366 zbuf[0] = (char)MPSDEFAULTWPTCLASS;
1367 }
1368 gbfwrite(zbuf, 4, 1, mps_file); /* class */
1369
1370 zbuf[0]=0;
1371 gbfwrite(zbuf, 1, 1, mps_file); /* country - i.e. empty string */
1372
1373 if ((mps_ver == 4) || (mps_ver == 5)) {
1374 gbfwrite(zbuf, 4, 1, mps_file); /* subclass part 1 */
1375 gbfwrite(ffbuf, 12, 1, mps_file); /* subclass part 2 */
1376 gbfwrite(zbuf, 2, 1, mps_file); /* subclass part 3 */
1377 gbfwrite(ffbuf, 4, 1, mps_file); /* unknown */
1378
1379 gbfwrite(zbuf, 1, 1, mps_file);
1380 gbfputc(3, mps_file);
1381 gbfwrite(zbuf, 17, 1, mps_file);
1382 } else {
1383 gbfwrite(zbuf, 8, 1, mps_file); /* subclass part 1 */
1384 gbfwrite(ffbuf, 8, 1, mps_file); /* subclass part 2 */
1385 gbfwrite(zbuf, 1, 1, mps_file); /* subclass part 3 */
1386
1387 /* unknown */
1388 gbfwrite(zbuf, 1, 1, mps_file);
1389 gbfputc(3, mps_file);
1390 gbfwrite(zbuf, 16, 1, mps_file);
1391 }
1392
1393 prevRouteWpt = rtewpt;
1394 }
1395
1396 static void
mps_routedatapoint_w_wrapper(const Waypoint * rte)1397 mps_routedatapoint_w_wrapper(const Waypoint* rte)
1398 {
1399 mps_routedatapoint_w(mps_file_out, mps_ver_out, rte);
1400 }
1401
1402
1403 /*
1404 * write out to file a route trailer
1405 * MRCB
1406 */
1407 static void
mps_routetrlr_w(gbfile * mps_file,int mps_ver,const route_head *)1408 mps_routetrlr_w(gbfile* mps_file, int mps_ver, const route_head* /* rte */)
1409 {
1410 char hdr[2];
1411 int value = 0;
1412
1413 (void)mps_ver;
1414 hdr[0] = 1;
1415
1416 //if (rte->waypoint_list.next) { /* this test doesn't do what I want i.e test if this is a valid route - treat as a placeholder for now */
1417 if (true) {
1418 gbfwrite(&value, 4, 1, mps_file);
1419 gbfwrite(hdr, 1, 1, mps_file);
1420 }
1421 }
1422
1423 static void
mps_routetrlr_w_wrapper(const route_head * rte)1424 mps_routetrlr_w_wrapper(const route_head* rte)
1425 {
1426 mps_routetrlr_w(mps_file_out, mps_ver_out, rte);
1427 }
1428
1429
1430 /*
1431 * read in from file a track record
1432 * MRCB
1433 */
1434 static void
mps_track_r(gbfile * mps_file,int mps_ver,route_head ** trk)1435 mps_track_r(gbfile* mps_file, int mps_ver, route_head** trk)
1436 {
1437 int lat;
1438 int lon;
1439
1440 int dateTime = 0;
1441 route_head* track_head;
1442 int trk_count;
1443
1444 Waypoint* thisWaypoint;
1445 double mps_altitude = unknown_alt;
1446 double mps_depth = unknown_alt;
1447
1448 (void)mps_ver;
1449
1450 QString trkname = gbfgetcstr(mps_file);
1451 #ifdef MPS_DEBUG
1452 fprintf(stderr, "mps_track_r: reading track %s\n", trkname);
1453 #endif
1454
1455 (void) gbfgetc(mps_file); /* display flag */
1456 (void) gbfgetint32(mps_file); /* colour */
1457
1458 trk_count = gbfgetint32(mps_file); /* number of datapoints in tracklog */
1459
1460 /* I don't know, but perhaps it's valid to have a track with no waypoints */
1461 /* Seems dumb, but truth is stranger than fiction. Of course, it could be */
1462 /* that there are more than MAXINT / 2 waypoints, yeah sure */
1463 /* Allow the caller the perform the file resync caused by bombing out early */
1464 if (trk_count < 0) {
1465 return;
1466 }
1467 #ifdef MPS_DEBUG
1468 fprintf(stderr, "mps_track_r: there are %d track waypoints\n", trk_count);
1469 #endif
1470
1471 track_head = new route_head;
1472 track_head->rte_name = trkname;
1473 track_add_head(track_head);
1474 *trk = track_head;
1475
1476 while (trk_count--) {
1477
1478 lat = gbfgetint32(mps_file);
1479 lon = gbfgetint32(mps_file);
1480
1481 if (gbfgetc(mps_file) == 1) { /* altitude validity */
1482 mps_altitude = gbfgetdbl(mps_file);
1483 } else {
1484 mps_altitude = unknown_alt;
1485 gbfseek(mps_file, 8, SEEK_CUR);
1486 }
1487
1488 if (gbfgetc(mps_file) == 1) { /* date/time validity */
1489 dateTime = gbfgetint32(mps_file);
1490 } else {
1491 (void) gbfgetint32(mps_file);
1492 }
1493
1494 if (gbfgetc(mps_file) == 1) { /* depth validity */
1495 mps_depth = gbfgetdbl(mps_file);
1496 } else {
1497 mps_depth = unknown_alt;
1498 gbfseek(mps_file, 8, SEEK_CUR);
1499 }
1500
1501 thisWaypoint = new Waypoint;
1502 thisWaypoint->latitude = GPS_Math_Semi_To_Deg(lat);
1503 thisWaypoint->longitude = GPS_Math_Semi_To_Deg(lon);
1504 thisWaypoint->SetCreationTime(dateTime);
1505 thisWaypoint->altitude = mps_altitude;
1506 if (mps_depth != unknown_alt) {
1507 WAYPT_SET(thisWaypoint, depth, mps_depth);
1508 }
1509 track_add_wpt(track_head, thisWaypoint);
1510
1511 } /* while (trk_count--) */
1512
1513
1514 }
1515
1516 /*
1517 * write out to file a tracklog header
1518 * MRCB
1519 */
1520 static void
mps_trackhdr_w(gbfile * mps_file,int mps_ver,const route_head * trk)1521 mps_trackhdr_w(gbfile* mps_file, int mps_ver, const route_head* trk)
1522 {
1523 unsigned int colour = 0; /* unknown colour */
1524 char* tname;
1525 char hdr[20];
1526 time_t uniqueValue = 0;
1527
1528 (void)mps_ver;
1529
1530 /* total nodes (waypoints) this track */
1531 unsigned int trk_datapoints = 0;
1532 //if (trk->waypoint_list.next) { /* this test doesn't do what I want i.e test if this is a valid track - treat as a placeholder for now */
1533 if (true) {
1534 foreach (const Waypoint* testwpt, trk->waypoint_list) {
1535 if (trk_datapoints == 0) {
1536 uniqueValue = testwpt->GetCreationTime().toTime_t();
1537 }
1538 trk_datapoints++;
1539 }
1540
1541 if (uniqueValue == 0) {
1542 uniqueValue = current_time().toTime_t();
1543 }
1544
1545 /* track name */
1546 if (trk->rte_name.isEmpty()) {
1547 sprintf(hdr, "Track%04x", (unsigned) uniqueValue);
1548 tname = xstrdup(hdr);
1549 } else {
1550 tname = xstrdup(trk->rte_name);
1551 }
1552
1553 int tname_len = strlen(tname);
1554 unsigned int reclen = tname_len + 11; /* "T" (1) + strlen(tname) + NULL (1) + display flag (1) + colour (4) +
1555 num track datapoints value (4) */
1556
1557 reclen += (trk_datapoints * 31) - 1; /* lat (4) + lon (4) + alt (9) + date (5) + depth (9) ;*/
1558 /* -1 is because reclen starts from 0 which means a length of 1 */
1559 gbfputint32(reclen, mps_file);
1560 gbfputc('T', mps_file);
1561 gbfwrite(tname, 1, tname_len, mps_file);
1562
1563 xfree(tname);
1564
1565 hdr[0] = 0;
1566 hdr[1] = 1;
1567 gbfwrite(hdr, 2, 1, mps_file); /* NULL string terminator + display flag */
1568
1569 gbfputint32(colour, mps_file);
1570
1571 gbfputint32(trk_datapoints, mps_file);
1572 }
1573
1574 }
1575
1576 static void
mps_trackhdr_w_wrapper(const route_head * trk)1577 mps_trackhdr_w_wrapper(const route_head* trk)
1578 {
1579 mps_trackhdr_w(mps_file_out, mps_ver_out, trk);
1580 }
1581
1582
1583 /*
1584 * write out to file a tracklog datapoint
1585 * MRCB
1586 */
1587 static void
mps_trackdatapoint_w(gbfile * mps_file,int mps_ver,const Waypoint * wpt)1588 mps_trackdatapoint_w(gbfile* mps_file, int mps_ver, const Waypoint* wpt)
1589 {
1590 time_t t = wpt->GetCreationTime().toTime_t();
1591 char zbuf[10];
1592
1593 double mps_altitude = wpt->altitude;
1594 double mps_depth = unknown_alt;
1595
1596 (void)mps_ver;
1597
1598 int lat = GPS_Math_Deg_To_Semi(wpt->latitude);
1599 int lon = GPS_Math_Deg_To_Semi(wpt->longitude);
1600 if (WAYPT_HAS(wpt, depth) && mpsusedepth) {
1601 mps_depth = wpt->depth;
1602 }
1603
1604 memset(zbuf, 0, sizeof(zbuf));
1605
1606 gbfputint32(lat, mps_file);
1607 gbfputint32(lon, mps_file);
1608
1609 if (mps_altitude == unknown_alt) {
1610 gbfwrite(zbuf, 9, 1, mps_file);
1611 } else {
1612 gbfputc(1, mps_file);
1613 gbfputdbl(mps_altitude, mps_file);
1614 }
1615
1616 if (t > 0) { /* a valid time is assumed to > 0 */
1617 gbfputc(1, mps_file);
1618 gbfputint32(t, mps_file);
1619 } else {
1620 gbfwrite(zbuf, 5, 1, mps_file);
1621 }
1622
1623 if (mps_depth == unknown_alt) {
1624 gbfwrite(zbuf, 9, 1, mps_file);
1625 } else {
1626 gbfputc(1, mps_file);
1627 gbfputdbl(mps_depth, mps_file);
1628 }
1629 }
1630
1631 static void
mps_trackdatapoint_w_wrapper(const Waypoint * wpt)1632 mps_trackdatapoint_w_wrapper(const Waypoint* wpt)
1633 {
1634 mps_trackdatapoint_w(mps_file_out, mps_ver_out, wpt);
1635 }
1636
1637
1638 static void
mps_read()1639 mps_read()
1640 {
1641 Waypoint* wpt;
1642 route_head* rte;
1643 route_head* trk;
1644
1645 char recType;
1646 int reclen;
1647 int morework;
1648 unsigned int mpsWptClass;
1649 long mpsFileInPos;
1650
1651 mps_ver_in = 0; /* although initialised at declaration, what happens if there are two mapsource
1652 input files? */
1653 mps_fileHeader_r(mps_file_in, &mps_ver_in);
1654
1655 #ifdef DUMP_ICON_TABLE
1656 printf("static icon_mapping_t garmin_icon_table[] = {\n");
1657 #endif
1658
1659 morework = 1;
1660 while (morework && !gbfeof(mps_file_in)) {
1661
1662 /* Read record length of next section */
1663 reclen = gbfgetint32(mps_file_in);
1664
1665 if (reclen < 0) {
1666 fatal(MYNAME ": a record length read from the input file is invalid. \nEither the file is corrupt or unsupported.\n");
1667 }
1668
1669 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
1670 gbfread(&recType, 1, 1, mps_file_in);
1671 mpsFileInPos = gbftell(mps_file_in);
1672 switch (recType) {
1673 case 'W':
1674 /* Waypoint record */
1675 /* With routes, we need the waypoint info that reveals, for example, the symbol type */
1676 mps_waypoint_r(mps_file_in, mps_ver_in, &wpt, &mpsWptClass);
1677
1678 #ifdef MPS_DEBUG
1679 fprintf(stderr,"Read a waypoint - %s\n", wpt->shortname);
1680 #endif
1681
1682 if (gbftell(mps_file_in) != mpsFileInPos + reclen) {
1683 /* should junk this record and not save it since we're out of sync with the file */
1684 /* Should and how do we warn the user? */
1685 #ifdef MPS_DEBUG
1686 fprintf(stderr,"Lost sync with the file reading waypoint - %s\n", wpt->shortname);
1687 #endif
1688 gbfseek(mps_file_in, mpsFileInPos + reclen, SEEK_SET);
1689 delete wpt;
1690 } else {
1691 /* only add to the "real" list if a "user" waypoint otherwise add to the private list */
1692 if (mpsWptClass == MPSDEFAULTWPTCLASS) {
1693 waypt_add(wpt);
1694 } else {
1695 mps_wpt_q_add(&read_route_wpt_head, wpt);
1696 delete wpt;
1697 }
1698 #ifdef DUMP_ICON_TABLE
1699 printf("\t{ %4u, \"%s\" },\n", icon, wpt->shortname);
1700 #endif
1701 }
1702 break;
1703
1704 case 'R':
1705 /* Route record */
1706 mps_route_r(mps_file_in, mps_ver_in, &rte);
1707 if (gbftell(mps_file_in) != mpsFileInPos + reclen) {
1708 /* should junk this record and not save it since we're out of sync with the file */
1709 /* Should and how do we warn the user? */
1710 #ifdef MPS_DEBUG
1711 fprintf(stderr,"Lost sync with the file reading route - %s\n", rte->rte_name);
1712 #endif
1713 gbfseek(mps_file_in, mpsFileInPos + reclen, SEEK_SET);
1714 }
1715 break;
1716
1717 case 'T':
1718 /* Track record */
1719 mps_track_r(mps_file_in, mps_ver_in, &trk);
1720 if (gbftell(mps_file_in) != mpsFileInPos + reclen) {
1721 /* should junk this record and not save it since we're out of sync with the file */
1722 /* Should and how do we warn the user? */
1723 #ifdef MPS_DEBUG
1724 fprintf(stderr,"Lost sync with the file reading track - %s\n", trk->rte_name);
1725 #endif
1726 gbfseek(mps_file_in, mpsFileInPos + reclen, SEEK_SET);
1727 }
1728 break;
1729
1730 case 'L':
1731 /* Map segment record */
1732 mps_mapsegment_r(mps_file_in, mps_ver_in);
1733 if (gbftell(mps_file_in) != mpsFileInPos + reclen) {
1734 /* should junk this record and not save it since we're out of sync with the file */
1735 /* Should and how do we warn the user? */
1736 gbfseek(mps_file_in, mpsFileInPos + reclen, SEEK_SET);
1737 }
1738 break;
1739
1740 case 'V':
1741 /* Mapset record */
1742 mps_mapsetname_r(mps_file_in, mps_ver_in);
1743 /* Last record in the file */
1744 morework = 0;
1745 break;
1746 default:
1747 /* Unknown record type. Skip over it. */
1748 gbfseek(mps_file_in, reclen, SEEK_CUR);
1749 }
1750
1751 } /* while (!gbfeof(mps_file_in)) */
1752
1753 #ifdef DUMP_ICON_TABLE
1754 printf("\t{ -1, NULL },\n");
1755 printf("};\n");
1756 #endif
1757
1758
1759 }
1760
1761 static void
mps_write()1762 mps_write()
1763 {
1764 Waypoint* wpt;
1765 route_head* rte;
1766 route_head* trk;
1767
1768 char recType = -1;
1769 int reclen;
1770 /* TODO: This kills a compiler warning but I'm not sure it's right */
1771 int reclen2 = 0;
1772 unsigned int tocopy;
1773 unsigned int block;
1774
1775 unsigned int mpsWptClass;
1776
1777 unsigned char copybuf[8192];
1778
1779 int short_length = atoi(snlen);
1780
1781 if (mpsmergeout) {
1782 /* need to skip over the merging header and test merge version */
1783 mps_fileHeader_r(mps_file_temp, &mps_ver_temp);
1784
1785 if (mpsverout) {
1786 if (mps_ver_temp != atoi(mpsverout)) {
1787 /* Need to clean up after a junk version specified */
1788 /* close the real output file + renamed original output file */
1789 /* then delete the "real" file and rename the temporarily renamed file back */
1790 gbfclose(mps_file_temp);
1791 gbfclose(mps_file_out);
1792 QFile::remove(fin_name);
1793 QFile::rename(tempname, fin_name);
1794 fatal(MYNAME ": merge source version is %d, requested out version is %d\n", mps_ver_temp, atoi(mpsverout));
1795 }
1796 } else {
1797 mpsverout = (char*) xmalloc(10);
1798 sprintf(mpsverout,"%d", mps_ver_temp);
1799 }
1800 }
1801
1802 if (mpsverout) {
1803 mps_ver_out = atoi(mpsverout);
1804 } else {
1805 mps_ver_out = 5;
1806 }
1807
1808 mkshort_handle = mkshort_new_handle();
1809
1810 setshort_length(mkshort_handle, short_length);
1811
1812 if (snwhiteopt) {
1813 setshort_whitespace_ok(mkshort_handle, atoi(snwhiteopt));
1814 } else {
1815 setshort_whitespace_ok(mkshort_handle, 0);
1816 }
1817
1818 mps_fileHeader_w(mps_file_out, mps_ver_out);
1819
1820 /* .mps file order is wpts, rtes, trks then mapsets. If we've not been asked to write
1821 wpts, but we are merging, then read in the waypoints from the original file and
1822 write them out, prior to doing rtes.
1823 */
1824 /* if ((mpsmergeout) && (global_opts.objective != wptdata)) { */
1825 if ((mpsmergeout) && (! doing_wpts)) {
1826 while (!gbfeof(mps_file_temp)) {
1827
1828 reclen2 = gbfgetint32(mps_file_temp);
1829
1830 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
1831 gbfread(&recType, 1, 1, mps_file_temp);
1832
1833 if (recType == 'W') {
1834 gbfwrite(&reclen, 4, 1, mps_file_out); /* write out untouched */
1835 gbfwrite(&recType, 1, 1, mps_file_out);
1836
1837 long tempFilePos = gbftell(mps_file_temp);
1838 /* need to read in the waypoint info only because later we may need to check for uniqueness
1839 since we're here because the user didn't request waypoints, this should be acceptable */
1840 mps_waypoint_r(mps_file_temp, mps_ver_temp, &wpt, &mpsWptClass);
1841 mps_wpt_q_add(&written_wpt_head, wpt);
1842 delete wpt;
1843 /* now return to the start of the waypoint data to do a "clean" copy */
1844 gbfseek(mps_file_temp, tempFilePos, SEEK_SET);
1845
1846 /* copy the data using a "reasonably" sized buffer */
1847 for (tocopy = reclen2; tocopy > 0; tocopy -= block) {
1848 block = (tocopy > sizeof(copybuf) ? sizeof(copybuf) : tocopy);
1849 gbfread(copybuf, block, 1, mps_file_temp);
1850 gbfwrite(copybuf, block, 1, mps_file_out);
1851 }
1852 } else {
1853 break;
1854 }
1855 } /* while (!gbfeof(mps_file_temp)) */
1856 } /* if (mpsmergeout) */
1857
1858 /* irrespective of merging, now write out any waypoints */
1859 /* if (global_opts.objective == wptdata) { */
1860 if (doing_wpts) {
1861
1862 if (mpsmergeout) {
1863 /* since we're processing waypoints, we should read in from whatever version and write out */
1864 /* in the selected version */
1865 while (!gbfeof(mps_file_temp)) {
1866
1867 reclen2 = gbfgetint32(mps_file_temp);
1868
1869 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
1870 gbfread(&recType, 1, 1, mps_file_temp);
1871
1872 if (recType == 'W') {
1873 /* need to be careful that we aren't duplicating a wpt defined from elsewhere */
1874 mps_waypoint_r(mps_file_temp, mps_ver_temp, &wpt, &mpsWptClass);
1875 if (mpsWptClass == MPSDEFAULTWPTCLASS) {
1876 waypt_add(wpt);
1877 } else {
1878 delete wpt;
1879 }
1880 } else {
1881 break;
1882 }
1883 }
1884 }
1885 waypt_disp_all(mps_waypoint_w_unique_wrapper);
1886 }
1887
1888 /* prior to writing any tracks as requested, if we're doing a merge, read in the rtes
1889 from the original file and then write them out, ready for tracks to follow
1890 */
1891
1892 /* if ((mpsmergeout) && (global_opts.objective != rtedata)) { */
1893 if ((mpsmergeout) && (! doing_rtes)) {
1894 while (!gbfeof(mps_file_temp)) {
1895
1896 /* this might all fail if the relevant waypoints haven't been written */
1897 if (recType == 'R') {
1898 gbfwrite(&reclen, 4, 1, mps_file_out); /* write out untouched */
1899 gbfwrite(&recType, 1, 1, mps_file_out);
1900
1901 /* copy the data using a "reasonably" sized buffer */
1902 for (tocopy = reclen2; tocopy > 0; tocopy -= block) {
1903 block = (tocopy > sizeof(copybuf) ? sizeof(copybuf) : tocopy);
1904 gbfread(copybuf, block, 1, mps_file_temp);
1905 gbfwrite(copybuf, block, 1, mps_file_out);
1906 }
1907 } else {
1908 break;
1909 }
1910 reclen2 = gbfgetint32(mps_file_temp);
1911
1912 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
1913 gbfread(&recType, 1, 1, mps_file_temp);
1914
1915 } /* while (!gbfeof(mps_file_temp)) */
1916 } /* if (mpsmergeout) */
1917
1918 /* routes are next in the wpts, rtes, trks, mapset sequence */
1919 /* if (global_opts.objective == rtedata) { */
1920 if (doing_rtes) {
1921
1922 if (mpsmergeout) {
1923 /* since we're processing routes, we should read in from whatever version and write out */
1924 /* in the selected version */
1925 while (!gbfeof(mps_file_temp)) {
1926
1927 if (recType == 'R') {
1928 mps_route_r(mps_file_temp, mps_ver_temp, &rte);
1929 } else {
1930 break;
1931 }
1932
1933 reclen2 = gbfgetint32(mps_file_temp);
1934
1935 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
1936 gbfread(&recType, 1, 1, mps_file_temp);
1937 }
1938 }
1939 /* need to make sure there is a "real" waypoint for each route waypoint
1940 Need to be careful about creating duplicate wpts as MapSource chokes on these
1941 so, if the user requested waypoints to be output too, then write the route
1942 waypoints only if unique in the total list of waypoints ("real" and route derived)
1943 If the user didn't request waypoints to be output, then output the route derived
1944 waypoints without consideration for uniqueness for "real" waypoints that haven't
1945 been output (phew!)
1946 */
1947 route_disp_all(nullptr, nullptr, mps_route_wpt_w_unique_wrapper);
1948
1949 route_disp_all(mps_routehdr_w_wrapper, mps_routetrlr_w_wrapper, mps_routedatapoint_w_wrapper);
1950 }
1951
1952 /* If merging but we haven't been requested to write out tracks, then read in tracks from
1953 the original file and write these out prior to any mapset writes later on
1954 */
1955 /* if ((mpsmergeout) && (global_opts.objective != trkdata)) { */
1956 if ((mpsmergeout) && (! doing_trks)) {
1957 while (!gbfeof(mps_file_temp)) {
1958
1959 if (recType == 'T') {
1960 gbfwrite(&reclen, 4, 1, mps_file_out); /* write out untouched */
1961 gbfwrite(&recType, 1, 1, mps_file_out);
1962
1963 /* copy the data using a "reasonably" sized buffer */
1964 for (tocopy = reclen2; tocopy > 0; tocopy -= block) {
1965 block = (tocopy > sizeof(copybuf) ? sizeof(copybuf) : tocopy);
1966 gbfread(copybuf, block, 1, mps_file_temp);
1967 gbfwrite(copybuf, block, 1, mps_file_out);
1968 }
1969 } else {
1970 break;
1971 }
1972 reclen2 = gbfgetint32(mps_file_temp);
1973
1974 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
1975 gbfread(&recType, 1, 1, mps_file_temp);
1976
1977 } /* while (!gbfeof(mps_file_temp)) */
1978 } /* if (mpsmergeout) */
1979
1980 /* tracks are next in the wpts, rte, trks, mapset sequence in .mps files */
1981 /* if (global_opts.objective == trkdata) { */
1982 if (doing_trks) {
1983 if (mpsmergeout) {
1984 /* since we're processing tracks, we should read in from whatever version and write out
1985 in the selected version */
1986 while (!gbfeof(mps_file_temp)) {
1987
1988 if (recType == 'T') {
1989 mps_track_r(mps_file_temp, mps_ver_temp, &trk);
1990 } else {
1991 break;
1992 }
1993
1994 reclen2 = gbfgetint32(mps_file_temp);
1995
1996 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
1997 gbfread(&recType, 1, 1, mps_file_temp);
1998 }
1999 }
2000 track_disp_all(mps_trackhdr_w_wrapper, nullptr, mps_trackdatapoint_w_wrapper);
2001 }
2002
2003 if (mpsmergeout) {
2004 /* should now be reading a either a map segment or a mapset - since we would write out an empty one,
2005 let's use the one from the merge file which may well have decent data in */
2006 for (;;) {
2007 gbfwrite(&reclen, 4, 1, mps_file_out); /* write out untouched */
2008 gbfwrite(&recType, 1, 1, mps_file_out);
2009
2010 /* copy the data using a "reasonably" sized buffer */
2011 for (tocopy = reclen2; tocopy > 0; tocopy -= block) {
2012 block = (tocopy > sizeof(copybuf) ? sizeof(copybuf) : tocopy);
2013 gbfread(copybuf, block, 1, mps_file_temp);
2014 gbfwrite(copybuf, block, 1, mps_file_out);
2015 }
2016
2017 if (recType != 'V') {
2018 reclen2 = gbfgetint32(mps_file_temp);
2019
2020 /* Read the record type "flag" in - using gbfread in case in the future need more than one char */
2021 gbfread(&recType, 1, 1, mps_file_temp);
2022 } else {
2023 break;
2024 }
2025 }
2026
2027 } else {
2028 mps_mapsetname_w(mps_file_out, mps_ver_out);
2029 }
2030
2031 mkshort_del_handle(&mkshort_handle);
2032
2033 }
2034
2035 ff_vecs_t mps_vecs = {
2036 ff_type_file,
2037 FF_CAP_RW_ALL,
2038 mps_rd_init,
2039 mps_wr_init,
2040 mps_rd_deinit,
2041 mps_wr_deinit,
2042 mps_read,
2043 mps_write,
2044 nullptr,
2045 &mps_args,
2046 CET_CHARSET_MS_ANSI, 0, /* CET-REVIEW */
2047 NULL_POS_OPS,
2048 nullptr,
2049 };
2050