1 /*
2 
3     Read/write Alan Map500 Waypoints, Routes and Tracklogs.
4 
5     Provides "alanwpr" and "alantrl" formats for gpsbabel.
6     Currently supports OS 2.xx only.
7 
8     Copyright (C) 2007  Gunar Megger, 0xff@quantentunnel.de
9     Copyright (C) 2002-2014 Robert Lipe, robertlipe+source@gpsbabel.org
10 
11     This program is free software; you can redistribute it and/or modify
12     it under the terms of the GNU General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU General Public License for more details.
20 
21     You should have received a copy of the GNU General Public License
22     along with this program; if not, write to the Free Software
23     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24 
25  */
26 
27 #include <cctype>                  // for isprint
28 #include <cstdio>                  // for snprintf, sprintf, SEEK_SET, size_t
29 #include <cstdint>                 // for int16_t, int32_t, uint8_t, uint32_t, uint16_t, int8_t
30 #include <cstring>                 // for memset, strlen, strncpy, memcpy, strncmp
31 #include <ctime>                   // for gmtime, time, time_t
32 
33 #include <QtCore/QString>          // for QString
34 #include <QtCore/QVector>          // for QVector
35 
36 #include "defs.h"
37 #include "gbfile.h"                // for gbfwrite, gbfile, gbfread, gbfclose, gbfopen, gbfseek
38 #include "src/core/datetime.h"     // for DateTime
39 
40 #define MYNAME "alan"
41 
42 #define MAXWPT      1000		/* old 500 */
43 
44 #define MAXRTE      50			/* old 20 */
45 #define MAXWPTINRTE 150			/* old 30 */
46 
47 #define MAXTRK      8			/* old 5 */
48 #define MAXPTINTRK  2500
49 
50 #define WPT_NAME_LEN    8
51 #define WPT_COMMENT_LEN 12
52 
53 #define RTE_NAME_LEN    8
54 #define RTE_COMMENT_LEN 12
55 
56 #define TRK_NAME_LEN    12
57 #define TRK_COMMENT_LEN 13
58 
59 struct wpthdr {
60   uint32_t id;
61   int16_t num;
62   int16_t next;
63   int16_t idx[MAXWPT];
64   uint8_t used[MAXWPT];
65 };
66 
67 struct wpt {
68   char name[WPT_NAME_LEN];
69   char comment[WPT_COMMENT_LEN];
70   struct {
71     int32_t x;				/* degree * 36000 */
72     int32_t y;				/* degree * 36000  */
73   } pt;
74   int32_t date;
75   int32_t time;
76   int16_t usecount;
77   int8_t checked;
78   int8_t reserved;
79 };
80 
81 struct rtehdr {
82   uint32_t id;
83   int16_t num;
84   int16_t next;
85   int16_t idx[MAXRTE];
86   uint8_t used[MAXRTE];
87   int16_t rteno;
88 };
89 
90 struct rte {
91   char name[RTE_NAME_LEN];
92   char comment[RTE_COMMENT_LEN];
93   int16_t wptnum;
94   int16_t wptidx[MAXWPTINRTE];
95   int16_t reserved;
96   int32_t date;
97   int32_t time;
98 };
99 
100 struct wprdata {
101   struct wpthdr wpthdr;
102   struct wpt    wpt[MAXWPT];
103   struct rtehdr rtehdr;
104   struct rte    rte[MAXRTE];
105 };
106 
107 struct trkhdr {
108   int16_t totalpt;
109   int16_t next;
110   char name[TRK_NAME_LEN];		/* 10, null terminated */
111   char comment[TRK_COMMENT_LEN];	/* 12, null terminated */
112   uint8_t reserved[3];
113   uint32_t occupied;
114   uint32_t show;
115   uint32_t fill;
116 };
117 
118 struct loghdr {
119   uint32_t id;
120   int16_t num;
121   int16_t next;
122   int32_t date;
123   int32_t time;
124   struct trkhdr trkhdr[MAXTRK];
125 };
126 
127 struct trklog {
128   struct {
129     int32_t x;				/* degree * 36000 */
130     int32_t y;				/* degree * 36000  */
131   } pt[MAXPTINTRK];
132   struct {
133     int16_t speed;			/* km/h * 200 */
134     int16_t height;			/* m * 5 */
135   } sh[MAXPTINTRK];
136 };
137 
138 struct trldata {
139   struct loghdr loghdr;
140   struct trklog trklog[MAXTRK];
141 };
142 
143 #define WPT_HDR_ID 0x5C38A600
144 #define RTE_HDR_ID 0xD87F5900
145 #define TRL_HDR_ID 0x38CB1200
146 
147 #define WPT_IDX_NONE -1			/* 0xffff */
148 #define WPT_USED      0xff
149 #define WPT_UNUSED    0
150 #define WPT_CHECKED   1
151 #define WPT_UNCHECKED 0
152 
153 #define RTE_IDX_NONE   -1		/* 0xffff */
154 #define RTE_USED        0xff
155 #define RTE_UNUSED      0
156 #define RTE_RTENO_NONE -1
157 
158 #define TRK_USED     1
159 #define TRK_UNUSED   0
160 #define TRK_SHOW     1
161 #define TRK_HIDE     0
162 #define TRK_FILL     1
163 #define TRK_WRAP     0
164 
165 #define MAP500_PT_SCALE 36000.0
166 #define pt2deg(P) ((double)(P) / MAP500_PT_SCALE)
167 #define deg2pt(D) (int32_t)si_round((double)(D) * MAP500_PT_SCALE)
168 
169 #define MAP500_ALTITUDE_SCALE   5.0
170 #define hgt2m(A) ((double)(A) / MAP500_ALTITUDE_SCALE)
171 #define m2hgt(A) (int16_t)si_round((double)(A) * MAP500_ALTITUDE_SCALE)
172 
173 #define MAP500_SPEED_SCALE    720.0
174 #define sp2mps(S) ((double)(S) / MAP500_SPEED_SCALE)
175 #define mps2sp(S) (int16_t)si_round((double)(S) * MAP500_SPEED_SCALE)
176 
177 #define  BYTEORDER_TEST  0x04030201	/* 32bit reference value */
178 enum {
179   SWAP_NONE  = 0x1234,			/* map500 regular */
180   SWAP_BYTES = 0x2143,			/* bytes swapped */
181   SWAP_WORDS = 0x3412,			/* words swapped */
182   SWAP_BOTH  = 0x4321			/* words + bytes swapped */
183 };
184 
185 /**************************************************************************/
186 
187 static gbfile* fin = nullptr, *fout = nullptr;
188 static struct wprdata WPR;
189 static struct trldata TRL;
190 
191 static QVector<arglist_t> wpr_args = {
192   /*
193   {"os3", &osversion, "Operating system version 3",
194           NULL, ARGTYPE_BOOL, ARGNOMINMAX },
195   */
196 };
197 static QVector<arglist_t> trl_args = {
198   /*
199   {"os3", &osversion, "Operating system version 3",
200           NULL, ARGTYPE_BOOL, ARGNOMINMAX },
201   */
202 };
203 
204 /**************************************************************************/
205 // FIXME: Why is this code doing its own byte order conversion?
byte_order()206 static unsigned int byte_order()
207 {
208   // avoid cppcheck error: The address of local variable 'test' is accessed at non-zero index.
209   // avoid undefined behavior accessing inactive union member.
210   // avoid  "strict aliasing" warnings.
211   // see https://en.cppreference.com/w/cpp/language/reinterpret_cast#Notes
212   uint32_t test = BYTEORDER_TEST;
213   unsigned char ptr[4];
214 
215   static_assert(sizeof ptr == sizeof test, "byte order test construction failure.");
216   memcpy(&ptr[0], &test, sizeof test);
217 
218   unsigned int order = (ptr[0] << 12) | (ptr[1] << 8) | (ptr[2] << 4) | ptr[3];
219 
220   return order;
221 }
222 
sw_bytes(void * word)223 static void sw_bytes(void* word)
224 {
225   auto* p = (uint8_t*) word;
226   auto* r = (uint16_t*) word;
227 
228   *r = (uint16_t)(p[1] << 8 | p[0]);
229 }
sw_words(void * dword)230 static void sw_words(void* dword)
231 {
232   auto* p = (uint16_t*) dword;
233   auto* r = (uint32_t*) dword;
234 
235   *r = (uint32_t)(p[0] << 16 | p[1]);
236 }
rev_bytes(void * dword)237 static void rev_bytes(void* dword)
238 {
239   auto* p = (uint8_t*) dword;
240   auto* r = (uint32_t*) dword;
241 
242   *r = (uint32_t)(p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]);
243 }
244 
swap_wpthdr(struct wpthdr * wpthdr,void (* swap16_func)(void *),void (* swap32_func)(void *))245 static void swap_wpthdr(struct wpthdr* wpthdr,
246                         void (*swap16_func)(void*), void (*swap32_func)(void*))
247 {
248   if (swap32_func != nullptr) {
249     swap32_func(&wpthdr->id);
250   }
251   if (swap16_func != nullptr) {
252     swap16_func(&wpthdr->num);
253     swap16_func(&wpthdr->next);
254     for (short &i : wpthdr->idx) {
255       swap16_func(&i);
256     }
257   }
258 }
259 
swap_wpt(struct wpt * wpt,void (* swap16_func)(void *),void (* swap32_func)(void *))260 static void swap_wpt(struct wpt* wpt,
261                      void (*swap16_func)(void*), void (*swap32_func)(void*))
262 {
263   if (swap16_func != nullptr) {
264     swap16_func(&wpt->usecount);
265   }
266   if (swap32_func != nullptr) {
267     swap32_func(&wpt->pt.x);
268     swap32_func(&wpt->pt.y);
269     swap32_func(&wpt->date);
270     swap32_func(&wpt->time);
271   }
272 }
273 
swap_rtehdr(struct rtehdr * rtehdr,void (* swap16_func)(void *),void (* swap32_func)(void *))274 static void swap_rtehdr(struct rtehdr* rtehdr,
275                         void (*swap16_func)(void*), void (*swap32_func)(void*))
276 {
277   if (swap16_func != nullptr) {
278     swap16_func(&rtehdr->num);
279     swap16_func(&rtehdr->next);
280     for (short &i : rtehdr->idx) {
281       swap16_func(&i);
282     }
283     swap16_func(&rtehdr->rteno);
284   }
285   if (swap32_func != nullptr) {
286     swap32_func(&rtehdr->id);
287   }
288 }
289 
swap_rte(struct rte * rte,void (* swap16_func)(void *),void (* swap32_func)(void *))290 static void swap_rte(struct rte* rte,
291                      void (*swap16_func)(void*), void (*swap32_func)(void*))
292 {
293   if (swap16_func != nullptr) {
294     swap16_func(&rte->wptnum);
295     for (short &i : rte->wptidx) {
296       swap16_func(&i);
297     }
298     swap16_func(&rte->reserved);
299   }
300   if (swap32_func != nullptr) {
301     swap32_func(&rte->date);
302     swap32_func(&rte->time);
303   }
304 }
305 
wpr_swap(struct wprdata * wprdata)306 static void wpr_swap(struct wprdata* wprdata)
307 {
308   void (*swap16_func)(void*);
309   void (*swap32_func)(void*);
310   int i;
311 
312   switch (byte_order()) {
313   case SWAP_NONE:		   /* same byte oder, LITTLE_ENDIAN */
314     return;
315     break;
316   case SWAP_BOTH:		   /* swap words and bytes, BIG_ENDIAN */
317     swap16_func = sw_bytes;
318     swap32_func = rev_bytes;
319     break;
320   case SWAP_WORDS:		   /* swap words, PDP_ENDIAN */
321     swap16_func = nullptr;
322     swap32_func = sw_words;
323     break;
324   case SWAP_BYTES:		   /* swap bytes */
325     swap16_func = sw_bytes;
326     swap32_func = nullptr;
327     break;
328   default:
329     return;			   /* never reached */
330   }
331 
332   swap_wpthdr(&(wprdata->wpthdr), swap16_func, swap32_func);
333   for (i=0; i< MAXWPT; i++) {
334     swap_wpt(&(wprdata->wpt[i]), swap16_func, swap32_func);
335   }
336   swap_rtehdr(&(wprdata->rtehdr), swap16_func, swap32_func);
337   for (i=0; i<MAXRTE; i++) {
338     swap_rte(&(wprdata->rte[i]), swap16_func, swap32_func);
339   }
340 }
341 
swap_trkhdr(struct trkhdr * trkhdr,void (* swap16_func)(void *),void (* swap32_func)(void *))342 static void swap_trkhdr(struct trkhdr* trkhdr,
343                         void (*swap16_func)(void*), void (*swap32_func)(void*))
344 {
345   if (swap16_func != nullptr) {
346     swap16_func(&(trkhdr->totalpt));
347     swap16_func(&(trkhdr->next));
348   }
349   if (swap32_func != nullptr) {
350     swap32_func(&(trkhdr->occupied));
351     swap32_func(&(trkhdr->show));
352     swap32_func(&(trkhdr->fill));
353   }
354 }
355 
swap_loghdr(struct loghdr * loghdr,void (* swap16_func)(void *),void (* swap32_func)(void *))356 static void swap_loghdr(struct loghdr* loghdr,
357                         void (*swap16_func)(void*), void (*swap32_func)(void*))
358 {
359   if (swap16_func != nullptr) {
360     swap16_func(&(loghdr->num));
361     swap16_func(&(loghdr->next));
362   }
363   if (swap32_func != nullptr) {
364     swap32_func(&(loghdr->id));
365     swap32_func(&(loghdr->date));
366     swap32_func(&(loghdr->time));
367   }
368   for (auto &i : loghdr->trkhdr) {
369     swap_trkhdr(&i, swap16_func, swap32_func);
370   }
371 }
372 
swap_trklog(struct trklog * trklog,void (* swap16_func)(void *),void (* swap32_func)(void *))373 static void swap_trklog(struct trklog* trklog,
374                         void (*swap16_func)(void*), void (*swap32_func)(void*))
375 {
376   int i;
377 
378   if (swap16_func != nullptr) {
379     for (i=0; i<MAXPTINTRK; i++) {
380       swap16_func(&(trklog->sh[i].speed));
381       swap16_func(&(trklog->sh[i].height));
382     }
383   }
384   if (swap32_func != nullptr) {
385     for (i=0; i<MAXPTINTRK; i++) {
386       swap32_func(&(trklog->pt[i].x));
387       swap32_func(&(trklog->pt[i].y));
388     }
389   }
390 }
391 
trl_swap(struct trldata * trldata)392 static void trl_swap(struct trldata* trldata)
393 {
394   void (*swap16_func)(void*);
395   void (*swap32_func)(void*);
396 
397   switch (byte_order()) {
398   case SWAP_NONE:		   /* same byte oder, LITTLE_ENDIAN */
399     return;
400     break;
401   case SWAP_BOTH:		   /* swap words and bytes, BIG_ENDIAN */
402     swap16_func = sw_bytes;
403     swap32_func = rev_bytes;
404     break;
405   case SWAP_WORDS:		   /* swap words, PDP_ENDIAN */
406     swap16_func = nullptr;
407     swap32_func = sw_words;
408     break;
409   case SWAP_BYTES:		   /* swap bytes */
410     swap16_func = sw_bytes;
411     swap32_func = nullptr;
412     break;
413   default:
414     return;                        /* never reached */
415   }
416 
417   swap_loghdr(&(trldata->loghdr), swap16_func, swap32_func);
418   for (auto &i : trldata->trklog) {
419     swap_trklog(&i, swap16_func, swap32_func);
420   }
421 }
422 
423 
424 /**************************************************************************/
425 
str2lab(char * dest,const char * src,int len,const char * fmt,int n)426 static void str2lab(char* dest, const char* src, int len, const char* fmt,
427                     int n)
428 {
429   int j = 0;
430   if (src != nullptr) {
431     for (int i=0; i<len && src[i] != '\0'; i++) {
432       if (isprint(src[i])) {
433         dest[j++] = src[i];
434       }
435     }
436   }
437   if (j == 0 && fmt != nullptr) {
438     snprintf(dest, len, fmt, n);
439     j = strlen(dest);
440   }
441   if (j < len) {
442     memset(dest+j, ' ', len-j);
443   }
444 }
445 
str2lab(char * dest,const QString & src,int len,const char * fmt,int n)446 static void str2lab(char* dest, const QString& src, int len, const char* fmt,
447                     int n)
448 {
449   str2lab(dest, CSTR(src), len, fmt, n);
450 }
451 
pack_time(time_t t,int32_t * date,int32_t * time)452 static void pack_time(time_t t, int32_t* date, int32_t* time)
453 {
454   struct tm* tm = gmtime(&t);
455   *date = tm->tm_mday | ((tm->tm_mon+1)<<8) | ((tm->tm_year+1900)<<16);
456   *time = t % 86400;
457 }
458 
unpack_time(int32_t date,int32_t time)459 static time_t unpack_time(int32_t date, int32_t time)
460 {
461   static int m_to_d[12] =
462   {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
463 
464   short year = (date >> 16) & 0xffff;
465   short month = (date >> 8) & 0xff;	/* 1-12 */
466   short day = date & 0xff;          /* 1-31 */
467 
468   month -= 1;			/* fit struct tm */
469   year += month / 12;
470 
471   if (month < 0) {
472     year -= 1;
473     month += 12;
474   }
475   time_t result = (year - 1970) * 365 + m_to_d[month];
476   if (month <= 1) {
477     year -= 1;
478   }
479   result += (year - 1968) / 4;
480   result -= (year - 1900) / 100;
481   result += (year - 1600) / 400;
482   result += day;
483   result -= 1;
484   result *= 86400;
485   result += time;	     /* map500 time is inseconds of the day */
486 
487   return result;
488 }
489 
490 /**************************************************************************/
491 
get_wpt(struct wprdata * wprdata,unsigned n)492 static Waypoint* get_wpt(struct wprdata* wprdata, unsigned n)
493 {
494   int j;
495 
496   struct wpthdr* wpthdr = &(wprdata->wpthdr);
497   int idx = wpthdr->idx[n];
498 
499   if (idx == WPT_IDX_NONE || wpthdr->used[idx] == WPT_UNUSED) {
500     return nullptr;
501   }
502   struct wpt* wpt = &(wprdata->wpt[idx]);
503 
504   auto* WP = new Waypoint;
505   WP->latitude  = -pt2deg(wpt->pt.y);
506   WP->longitude =  pt2deg(wpt->pt.x);
507   WP->SetCreationTime(unpack_time(wpt->date, wpt->time));
508   for (j=WPT_NAME_LEN-1; j >= 0 && wpt->name[j] == ' '; j--) {}
509   char *s = xstrndup(wpt->name,j+1);
510   WP->shortname = s;
511   xfree(s);
512   for (j=WPT_COMMENT_LEN-1; j >= 0 && wpt->comment[j] == ' '; j--) {}
513   if (j >= 0) {
514     char *descr = xstrndup(wpt->comment, j+1);
515     WP->description = descr;
516     xfree(descr);
517   } else {
518     WP->description = "";
519   }
520   WP->notes = "";
521 
522   return WP;
523 }
524 
wpr_read()525 static void wpr_read()
526 {
527   struct wprdata wprdata;
528   int i, j;
529   Waypoint* WP;
530 
531   if (gbfread(&wprdata, sizeof(struct wprdata), 1, fin) != 1) {
532     fatal(MYNAME ": Read error on '%s'. Perhaps this isn't an alan file\n", fin->name);
533   }
534   wpr_swap(&wprdata);
535   if (wprdata.wpthdr.id != WPT_HDR_ID ||
536       wprdata.rtehdr.id != RTE_HDR_ID) {
537     fatal(MYNAME ": %s is not in Alan .wpr format.\n", fin->name);
538   }
539 
540   /* waypoints */
541   for (i=0; i<MAXWPT; i++) {
542     WP = get_wpt(&wprdata, i);
543     if (WP != nullptr) {
544       waypt_add(WP);
545     }
546   }
547 
548   /* routes */
549   struct rtehdr* rtehdr = &(wprdata.rtehdr);
550   for (i=0; i<MAXRTE; i++) {
551     int idx = rtehdr->idx[i];
552     if (idx == RTE_IDX_NONE || rtehdr->used[idx] == RTE_UNUSED) {
553       continue;
554     }
555     struct rte* rte = &(wprdata.rte[idx]);
556 
557     auto* RT = new route_head;
558     RT->rte_num = i;
559     for (j=RTE_NAME_LEN-1; j >= 0 && rte->name[j] == ' '; j--) {}
560     char *s = xstrndup(rte->name,j+1);
561     RT->rte_name = s;
562     xfree(s);
563     for (j=RTE_COMMENT_LEN-1; j >= 0 && rte->comment[j] == ' '; j--) {}
564     if (j >= 0) {
565       char *desc = xstrndup(rte->comment,j+1);
566       RT->rte_desc = desc;
567       xfree(desc);
568     } else {
569       RT->rte_desc = "";
570     }
571 
572     route_add_head(RT);
573 
574     /* route points */
575     for (j=0; j<rte->wptnum; j++) {
576       WP = get_wpt(&wprdata, rte->wptidx[j]);
577       if (WP != nullptr) {
578         route_add_wpt(RT, WP);
579       }
580     }
581   }
582 }
583 
trl_read()584 static void trl_read()
585 {
586   struct trldata trldata;
587   int i, j;
588 
589   for (i=0; i<MAXTRK; i+=2) {
590     gbfseek(fin, 0x10000 * (i/2), SEEK_SET);
591     if (gbfread(&(trldata.trklog[i]), sizeof(struct trklog), 2, fin) != 2) {
592       fatal(MYNAME ": Read error on '%s'. Perhaps this isn't an alan file.\n", fin->name);
593     }
594   }
595   gbfseek(fin, 0x10000 * MAXTRK/2, SEEK_SET);
596   if (gbfread(&(trldata.loghdr), sizeof(struct loghdr), 1, fin) != 1) {
597     fatal(MYNAME ": Read error on '%s'. Perhaps this isn't an alan file.\n", fin->name);
598   }
599   trl_swap(&trldata);
600   if (trldata.loghdr.id != TRL_HDR_ID) {
601     fatal(MYNAME ": %s is not in Alan .trl format.\n", fin->name);
602   }
603 
604   for (i=0; i<MAXTRK; i++) {
605     /* track header */
606     struct trkhdr* trkhdr = &(trldata.loghdr.trkhdr[i]);
607     if (trkhdr->occupied == TRK_UNUSED) {
608       continue;
609     }
610     auto* TL = new route_head;
611     for (j=TRK_NAME_LEN-1;
612          j >= 0 && (trkhdr->name[j] == ' ' || trkhdr->name[j] == '\0');
613          j--) {}
614     char *s1 = xstrndup(trkhdr->name,j+1);
615     TL->rte_name = s1;
616     xfree(s1);
617     /*  TL->rte_name[TRK_NAME_LEN+1] = 0; */	/* MAYBE BAD ADDRESS (Valgrind) */
618     for (j=TRK_COMMENT_LEN-1;
619          j >= 0 && (trkhdr->comment[j] == ' ' || trkhdr->comment[j] == '\0');
620          j--) {}
621     s1 = xstrndup(trkhdr->comment,j+1);
622     TL->rte_desc = s1;
623     xfree(s1);
624     /*  TL->rte_desc[TRK_COMMENT_LEN+1] = 0; */	/* MAYBE BAD ADDRESS (Valgrind) */
625     TL->rte_num = i;
626 
627     track_add_head(TL);
628 
629     /* track points */
630     struct trklog* trklog = &(trldata.trklog[i]);
631     for (j=0; j<trkhdr->totalpt; j++) {
632       auto* WP = new Waypoint;
633       WP->latitude  = -pt2deg(trklog->pt[j].y);
634       WP->longitude =  pt2deg(trklog->pt[j].x);
635       WP->altitude  =  hgt2m(trklog->sh[j].height);
636       if (trklog->sh[j].speed >= 0)
637         WAYPT_SET(WP, speed, sp2mps(trklog->sh[j].speed));
638         else {			/* bad speed < 0 - set to 0.0 */
639           WAYPT_UNSET(WP, speed);
640         }
641       track_add_wpt(TL, WP);
642     }
643   }
644 }
645 
646 /**************************************************************************/
647 
find_wpt(struct wprdata * wprdata,const Waypoint * WP)648 static int find_wpt(struct wprdata* wprdata, const Waypoint* WP)
649 {
650   struct wpt pattern;
651 
652   str2lab(pattern.name, WP->shortname, WPT_NAME_LEN, nullptr, 0);
653   pattern.pt.x = deg2pt(WP->longitude);
654   pattern.pt.y = deg2pt(-WP->latitude);
655 
656   struct wpt* wpt = wprdata->wpt;
657   for (int i = 0; i<MAXWPT; i++) {
658     int wpt_idx = wprdata->wpthdr.idx[i];
659     if (wpt_idx == WPT_IDX_NONE ||
660         wprdata->wpthdr.used[wpt_idx] == WPT_UNUSED) {
661       continue;
662     }
663     if (strncmp(wpt[wpt_idx].name, pattern.name, WPT_NAME_LEN) == 0 &&
664         wpt[wpt_idx].pt.x == pattern.pt.x &&
665         wpt[wpt_idx].pt.y == pattern.pt.y) {
666       return i;
667     }
668   }
669 
670   return -1;
671 }
672 
add_wpt(struct wprdata * wprdata,const Waypoint * WP,int isroute)673 static int add_wpt(struct wprdata* wprdata, const Waypoint* WP,int isroute)
674 {
675   struct wpt* wpt;
676   int i;
677 
678   struct wpthdr* wpthdr = &(wprdata->wpthdr);
679 
680   int hdr_idx = find_wpt(wprdata, WP);
681   if (hdr_idx >= 0) {
682     /* duplicate waypoint */
683     if (isroute) {
684       wpt = &(wprdata->wpt[wpthdr->idx[hdr_idx]]);
685       wpt->usecount ++;
686     }
687     return hdr_idx;
688   }
689 
690   for (i=0; i<MAXWPT && wpthdr->idx[i] != WPT_IDX_NONE; i++) { }
691   hdr_idx = i;
692   for (i=0; i<MAXWPT && wpthdr->used[i] != WPT_UNUSED; i++) { }
693   int wpt_idx = i;
694   if (wpthdr->num >= MAXWPT || hdr_idx >= MAXWPT || wpt_idx >= MAXWPT) {
695     fatal(MYNAME ": Can't store more than %u waypoints\n", MAXWPT);
696   }
697 
698   wpt = &(wprdata->wpt[wpt_idx]);
699   str2lab(wpt->name, WP->shortname, WPT_NAME_LEN, "W%05d", wpt_idx);
700   str2lab(wpt->comment, WP->description, WPT_COMMENT_LEN, nullptr, 0);
701   wpt->pt.x = deg2pt(WP->longitude);
702   wpt->pt.y = deg2pt(-WP->latitude);
703   wpt->usecount = isroute ? 1 : 0;
704   wpt->checked  = isroute ? 0 : 1;
705   wpt->reserved = 0;
706   pack_time(WP->GetCreationTime().toTime_t(), &(wpt->date), &(wpt->time));
707 
708   wpthdr->idx[hdr_idx] = wpt_idx;
709   wpthdr->used[wpt_idx] = WPT_USED;
710   wpthdr->num++;
711   wpthdr->next++;
712   if (wpthdr->next >= MAXWPT) {	/* overrun */
713     wpthdr->next = 0;
714   }
715 
716   return hdr_idx;
717 }
718 
wpr_waypoint(const Waypoint * WP)719 static void wpr_waypoint(const Waypoint* WP)
720 {
721   add_wpt(&WPR, WP, 0);
722 }
723 
wpr_route_hdr(const route_head * RT)724 static void wpr_route_hdr(const route_head* RT)
725 {
726   int i;
727 
728   struct rtehdr* rtehdr = &(WPR.rtehdr);
729   for (i=0; i<MAXRTE && rtehdr->idx[i] != RTE_IDX_NONE; i++) { }
730   int hdr_idx = i;
731   for (i=0; i<MAXRTE && rtehdr->used[i] != RTE_UNUSED; i++) { }
732   int rte_idx = i;
733   if (rtehdr->num >= MAXRTE || hdr_idx >= MAXRTE || rte_idx >= MAXRTE) {
734     fatal(MYNAME ": Can't store more than %u routes", MAXRTE);
735   }
736 
737   struct rte* rte = &(WPR.rte[rte_idx]);
738   str2lab(rte->name, RT->rte_name, RTE_NAME_LEN, "R%03d", rte_idx);
739   str2lab(rte->comment, RT->rte_desc, RTE_COMMENT_LEN, nullptr, 0);
740   pack_time(time(nullptr), &(rte->date), &(rte->time));
741 
742   rtehdr->idx[hdr_idx] = rte_idx;
743   rtehdr->used[rte_idx] = RTE_USED;
744   rtehdr->num++;
745   rtehdr->next++;
746   if (rtehdr->next >= MAXRTE) {	/* overrun */
747     rtehdr->next = 0;
748   }
749 
750   /* if you want the new route to be active, uncomment the next line */
751   /* rtehdr->rteno = rte_idx; */
752 }
753 
wpr_route_wpt(const Waypoint * WP)754 static void wpr_route_wpt(const Waypoint* WP)
755 {
756   struct rte* rte = &(WPR.rte[WPR.rtehdr.num -1]);
757   if (rte->wptnum >= MAXWPTINRTE) {
758     fatal(MYNAME ": Can't store more than %u waypoints per route", MAXWPTINRTE);
759   }
760 
761   int wpt_idx = add_wpt(&WPR, WP, 1);
762 
763   rte->wptidx[rte->wptnum] = wpt_idx;
764   rte->wptnum ++;
765 }
766 
wpr_route_trl(const route_head *)767 static void wpr_route_trl(const route_head*)
768 {
769   /* should we do some final sanity checks? */
770 }
771 
wpr_write()772 static void wpr_write()
773 {
774   int i;
775 
776   WPR.wpthdr.id = WPT_HDR_ID;
777   WPR.wpthdr.num = WPR.wpthdr.next = 0;
778   for (i=0; i<MAXWPT; i++) {
779     WPR.wpthdr.idx[i]  = WPT_IDX_NONE;
780     WPR.wpthdr.used[i] = WPT_UNUSED;
781   }
782   memset(WPR.wpt, 0, MAXWPT * sizeof(struct wpt));
783   WPR.rtehdr.id = RTE_HDR_ID;
784   WPR.rtehdr.num = WPR.rtehdr.next = 0;
785   for (i=0; i<MAXRTE; i++) {
786     WPR.rtehdr.idx[i] = RTE_IDX_NONE;
787     WPR.rtehdr.used[i] = RTE_UNUSED;
788   }
789   WPR.rtehdr.rteno = RTE_RTENO_NONE;
790   memset(WPR.rte, 0, MAXRTE * sizeof(struct rte));
791 
792   waypt_disp_all(wpr_waypoint);
793   route_disp_all(wpr_route_hdr, wpr_route_trl, wpr_route_wpt);
794 
795   wpr_swap(&WPR);
796   if (gbfwrite(&WPR, sizeof(struct wprdata), 1, fout) != 1) {
797     fatal(MYNAME ": Write error on %s\n", fout->name);
798   }
799 }
800 
801 /**************************************************************************/
802 
trl_track_hdr(const route_head * TL)803 static void trl_track_hdr(const route_head* TL)
804 {
805   int idx;
806 
807   struct trkhdr* trkhdr = TRL.loghdr.trkhdr;
808 
809   for (idx=0; idx< MAXTRK && trkhdr[idx].occupied != TRK_UNUSED; idx++) {}
810   if (idx >= MAXTRK) {
811     fatal(MYNAME ": Can't store more than %u tracklogs", MAXTRK);
812   }
813 
814   if (!TL->rte_name.isEmpty()) {
815     strncpy(trkhdr[idx].name, CSTRc(TL->rte_name), TRK_NAME_LEN - 1);
816   }
817   if (*(trkhdr[idx].name) == '\0') {
818     sprintf(trkhdr[idx].name, "T%03d", idx);
819   }
820   trkhdr[idx].name[TRK_NAME_LEN-1] = '\0';
821 
822   if (!TL->rte_desc.isEmpty()) {
823     strncpy(trkhdr[idx].comment, CSTRc(TL->rte_desc), TRK_COMMENT_LEN - 1);
824     int l = strlen(CSTRc(TL->rte_desc));
825     if (l < TRK_COMMENT_LEN-1) {
826       memset(trkhdr[idx].comment + l, ' ', TRK_COMMENT_LEN - 1 - l);
827     }
828   }
829   trkhdr[idx].comment[TRK_COMMENT_LEN-1] = '\0';
830 
831   trkhdr[idx].occupied = TRK_USED;
832   trkhdr[idx].totalpt = 0;
833   trkhdr[idx].next = 0;
834 
835   TRL.loghdr.num = idx;
836 }
837 
trl_track_wpt(const Waypoint * WP)838 static void trl_track_wpt(const Waypoint* WP)
839 {
840   int trk_idx = TRL.loghdr.num;
841 
842   struct trkhdr* trkhdr = &(TRL.loghdr.trkhdr[trk_idx]);
843   if (trkhdr->totalpt >= MAXPTINTRK) {
844     fatal(MYNAME ": Can't store more than %u points per track", MAXPTINTRK);
845   }
846   int log_idx = trkhdr->next;
847 
848   struct trklog* trklog = &(TRL.trklog[trk_idx]);
849   trklog->pt[log_idx].x = deg2pt(WP->longitude);
850   trklog->pt[log_idx].y = deg2pt(-WP->latitude);
851   if WAYPT_HAS(WP, speed) {
852     trklog->sh[log_idx].speed =  mps2sp(WP->speed);
853   }
854   if (WP->altitude != unknown_alt) {
855     trklog->sh[log_idx].height = m2hgt(WP->altitude);
856   }
857 
858   trkhdr->totalpt ++;
859   trkhdr->next = trkhdr->totalpt;
860 }
861 
trl_track_tlr(const route_head *)862 static void trl_track_tlr(const route_head*)
863 {
864   int trk_idx = TRL.loghdr.num;
865   struct trkhdr* trkhdr = &(TRL.loghdr.trkhdr[trk_idx]);
866 
867   if (trkhdr->totalpt == 0) {
868     trkhdr->occupied = TRK_UNUSED;
869   }
870 
871   TRL.loghdr.num = -1;
872 }
873 
trl_write()874 static void trl_write()
875 {
876   int i;
877 
878   TRL.loghdr.id = TRL_HDR_ID;
879   TRL.loghdr.num = TRL.loghdr.next = -1;
880   TRL.loghdr.date = TRL.loghdr.time = 0;
881   for (i=0; i<MAXTRK; i++) {
882     struct trkhdr* trkhdr = &(TRL.loghdr.trkhdr[i]);
883     trkhdr->totalpt = 0;
884     trkhdr->next = 0;
885     memset(trkhdr->name, 0, TRK_NAME_LEN);
886     memset(trkhdr->comment, ' ', TRK_COMMENT_LEN);
887     trkhdr->comment[TRK_COMMENT_LEN-1] = '\0';
888     trkhdr->occupied = TRK_UNUSED;
889     trkhdr->show = TRK_HIDE;
890     trkhdr->fill = TRK_FILL;
891   }
892   memset(TRL.trklog, 0xff, sizeof(struct trklog) * MAXTRK);
893 
894   track_disp_all(trl_track_hdr, trl_track_tlr, trl_track_wpt);
895 
896   trl_swap(&TRL);
897 
898   size_t fill = 0x10000 - 2 * sizeof(struct trklog);
899   void* buf = xmalloc(fill);
900   if (buf == nullptr) {
901     fatal(MYNAME ": Not enough memory\n");
902   }
903   memset(buf, 0xff, fill);
904 
905   for (i=0; i<MAXTRK; i+=2) {
906     if (gbfwrite(&(TRL.trklog[i]), sizeof(struct trklog), 2, fout) != 2 ||
907         gbfwrite(buf, fill, 1, fout) != 1) {
908       fatal(MYNAME ": Write error on %s\n", fout->name);
909     }
910   }
911   xfree(buf);
912 
913   fill = 0x1000 - sizeof(struct loghdr);
914   buf = xmalloc(fill);
915   if (buf == nullptr) {
916     fatal(MYNAME ": Not enough memory\n");
917   }
918   memset(buf, 0xff, fill);
919 
920   if (gbfwrite(&(TRL.loghdr), sizeof(struct loghdr), 1, fout) != 1 ||
921       gbfwrite(buf, fill, 1, fout) != 1) {
922     fatal(MYNAME ": Write error on %s\n", fout->name);
923   }
924   xfree(buf);
925 }
926 
927 /**************************************************************************/
928 
alan_rd_init(const QString & fname)929 static void alan_rd_init(const QString& fname)
930 {
931   fin = gbfopen(fname, "rb", MYNAME);
932 }
933 
alan_rd_deinit()934 static void alan_rd_deinit()
935 {
936   gbfclose(fin);
937   fin = nullptr;
938 }
939 
940 
alan_wr_init(const QString & fname)941 static void alan_wr_init(const QString& fname)
942 {
943   fout = gbfopen(fname, "wb", MYNAME);
944 }
945 
alan_wr_deinit()946 static void alan_wr_deinit()
947 {
948   gbfclose(fout);
949   fout = nullptr;
950 }
951 
952 /**************************************************************************/
953 
954 ff_vecs_t alanwpr_vecs = {
955   ff_type_file,
956   {
957     (ff_cap)(ff_cap_read | ff_cap_write) 	/* waypoints */,
958     ff_cap_none              	/* tracks */,
959     (ff_cap)(ff_cap_read | ff_cap_write)	/* routes */
960   },
961   alan_rd_init,
962   alan_wr_init,
963   alan_rd_deinit,
964   alan_wr_deinit,
965   wpr_read,
966   wpr_write,
967   nullptr,
968   &wpr_args,
969   CET_CHARSET_ASCII, 0, /* ascii is the expected character set */
970   /* not fixed, can be changed through command line parameter */
971   NULL_POS_OPS,
972   nullptr
973 };
974 
975 ff_vecs_t alantrl_vecs = {
976   ff_type_file,
977   {
978     ff_cap_none            	/* waypoints */,
979     (ff_cap)(ff_cap_read | ff_cap_write) 	/* tracks */,
980     ff_cap_none           	/* routes */
981   },
982   alan_rd_init,
983   alan_wr_init,
984   alan_rd_deinit,
985   alan_wr_deinit,
986   trl_read,
987   trl_write,
988   nullptr,
989   &trl_args,
990   CET_CHARSET_ASCII, 0, /* ascii is the expected character set */
991   /* not fixed, can be changed through command line parameter */
992   NULL_POS_OPS,
993   nullptr
994 };
995