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