1 /*
2     Support for GPS TrackMaker data file.
3 
4     Copyright (C) 2005  Gustavo Niemeyer <gustavo@niemeyer.net>.
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20 
21 /*
22  * Documentation can be found at
23  * https://www.trackmaker.com/download/ref_guide_eng.pdf
24  * https://www.trackmaker.com/download/GTM211_format.pdf
25  */
26 
27 #include "defs.h"
28 #include "jeeps/gpsmath.h"
29 #include <QtCore/QList>
30 
31 static gbfile* file_in, *file_out;
32 static int indatum;
33 static int wp_count;
34 static int ws_count;
35 static int tr_count;
36 static int ts_count;
37 static int rt_count;
38 static int im_count;
39 static const route_head* rte_active;
40 static int start_new;
41 
42 #define MYNAME "GTM"
43 #define EPOCH89DIFF 631065600
44 /* was 631076400 but that seems to include a three-hour bias */
45 #define WAYPOINTSTYLES \
46 	"\xf5\xff\xff\xff\x0f\x00Times New Roman\x00\x00\x00\x00\x00\x90\x01"\
47 	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"\
48 	"\xf5\xff\xff\xff\x0f\x00Times New Roman\x01\x00\x00\x00\x00\x90\x01"\
49 	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"\
50 	"\xf5\xff\xff\xff\x0f\x00Times New Roman\x02\x00\x00\x00\x00\x90\x01"\
51 	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"\
52 	"\xf5\xff\xff\xff\x0f\x00Times New Roman\x03\x00\x00\x00\x00\x90\x01"\
53 	"\x00\x00\x00\x00\x00\x00\x8b\xff\xff\xff\xff\x00\x00\x00\x00\x00\x01"
54 
55 #define unknown_alt_gtm -10000000
56 
57 /* Read functions, according to specification. */
58 
59 #define fread_discard(a,b) gbfseek(a, (b), SEEK_CUR)
60 #define fread_byte(a) (unsigned char) gbfgetc(a)
61 
62 #if 0
63 /* not used */
64 static short int
65 fread_bool(gbfile* fd)
66 {
67   char buf[2];
68   gbfread(buf, 2, 1, fd);
69   return le_read16(buf) ? 1 : 0;
70 }
71 #endif
72 
73 #define fread_integer(a) gbfgetint16(a)
74 #define fread_long(a) gbfgetint32(a)
75 #define fread_single(a) gbfgetflt(a)
76 #define fread_double(a) gbfgetdbl(a)
77 
78 static QString
fread_string(gbfile * fd)79 fread_string(gbfile* fd)
80 {
81   int len = fread_integer(fd);
82 
83   if (len == 0) {
84     return nullptr;
85   }
86 
87   char* val = (char*) xmalloc(len+1);
88   gbfread(val, 1, len, fd);
89   while (len != 0 && val[len-1] == ' ') {
90     len--;
91   }
92   val[len] = 0;
93   QString v(val);
94   xfree(val);
95   return v;
96 }
97 
98 static void
fread_string_discard(gbfile * fd)99 fread_string_discard(gbfile* fd)
100 {
101   fread_string(fd);
102 }
103 
104 static QString
fread_fixedstring(gbfile * fd,int len)105 fread_fixedstring(gbfile* fd, int len)
106 {
107   char* val = (char*) xmalloc(len+1);
108 
109   gbfread(val, 1, len, fd);
110   while (len != 0 && val[len-1] == ' ') {
111     len--;
112   }
113   val[len] = 0;
114   QString v(val);
115   xfree(val);
116 
117   return v;
118 }
119 
120 /* Write functions, according to specification. */
121 
122 static void
fwrite_null(gbfile * fd,int len)123 fwrite_null(gbfile* fd, int len)
124 {
125   char buf[1024];
126 
127   memset(buf, 0, len);
128   gbfwrite(buf, 1, len, fd);
129 }
130 
131 #define fwrite_byte(a,b) gbfputc((signed char)(b), a)
132 #define fwrite_bool(a,b) gbfputuint16((b) ? 0xffff : 0, a)
133 #define fwrite_integer(a,b) gbfputint16((b), a)
134 #define fwrite_long(a,b) gbfputint32((b), a)
135 #define fwrite_single(a,b) gbfputflt((b), a)
136 #define fwrite_double(a,b) gbfputdbl((b), a)
137 
138 static void
fwrite_string(gbfile * fd,const char * str)139 fwrite_string(gbfile* fd, const char* str)
140 {
141   if (str && str[0]) {
142     int len = strlen(str);
143     fwrite_integer(fd, len);
144     gbfwrite(str, 1, len, fd);
145   } else {
146     fwrite_integer(fd, 0);
147   }
148 }
149 static void
fwrite_string(gbfile * fd,const QString & str)150 fwrite_string(gbfile* fd, const QString& str)
151 {
152   if (str.isEmpty()) {
153     fwrite_integer(fd, 0);
154   } else {
155     fwrite_integer(fd, str.length());
156     gbfwrite(CSTRc(str), 1, str.length(), fd);
157   }
158 }
159 
160 static void
fwrite_fixedstring(gbfile * fd,const char * str,int fieldlen)161 fwrite_fixedstring(gbfile* fd, const char* str, int fieldlen)
162 {
163   int len = str ? strlen(str) : 0;
164 
165   if (len > fieldlen) {
166     len = fieldlen;
167   }
168   if (str) {
169     gbfwrite(str, 1, len, fd);
170   }
171   for (; len != fieldlen; len++) {
172     gbfputc(' ', fd);
173   }
174 }
175 
176 static void
fwrite_fixedstring(gbfile * fd,const QString & str,int fieldlen)177 fwrite_fixedstring(gbfile* fd, const QString& str, int fieldlen)
178 {
179   fwrite_fixedstring(fd, CSTR(str), fieldlen);
180 }
181 
182 /* Auxiliary functions */
183 
184 #define MAX_INDATUM_INDEX 263
185 
186 static const int indatum_array[MAX_INDATUM_INDEX] = {
187   -1, // < 1
188   0, 0, 0, 0, 0, 0, 0, // < 8 : Adindan
189   1, // < 9 : Afgooye
190   2, // < 10 : Ain el Abd
191   -1, -1, -1, -1, // < 14
192   6, 6, 6, 6, 6, 6, 6, 6, 6, // < 23 : ARC 1950
193   7, 7, 7, // < 26 : ARC 1960
194   8, // < 27 : Ascension Island 58
195   -1, -1, -1, -1, -1, // < 32
196   13, // < 33 : Australian Geo 84
197   -1, // < 34
198   15, // < 35 : Bellevue IGN
199   16, // < 36 : Bermuda 1957
200   -1, -1, // < 38
201   17, // < 39 : Bukit Rimpah
202   18, // < 40 : Camp Area Astro
203   19, // < 41 : Campo Inchauspe
204   22, // < 42 : Canton Islan 1966
205   23, // < 43 : Cape
206   24, // < 44 : Cape Canaveral
207   26, // < 45 : Carthe
208   28, // < 46 : Chatham
209   29, // < 47 : Chua Astro
210   30, // < 48 : Corrego Alegre
211   -1, -1, // < 50
212   33, // < 51 : Djakarta (Batavia)
213   34, // < 52 : DOS 1968
214   35, // < 53 : Easter Island 1967
215   -1, // < 54
216   38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, // < 69 : European 1950 Mean
217   39, // < 70 : European 1979 Mean
218   -1, // < 71
219   41, // < 72 : Gandajika
220   42, // < 73 : Geodetic Datum 49
221   -1, // < 74
222   45, // < 75 : Guam 1963
223   46, // < 76 : Gunung Segara
224   -1, // < 77
225   49, // < 78 : Hearth North
226   -1, // < 79
227   50, // < 80 : Hjorsey 1955
228   51, // < 81 : Hong Kong 1963
229   52, // < 82 : Hu-Tzu-Shan
230   53, 53, 53, 53, 53, 53, 53, // < 89 : Indian
231   -1, // < 90
232   55, // < 91 : Ireland 1965
233   -1, // < 92
234   56, // < 93 : ISTS 073 69
235   57, // < 94 : Johnston Island 61
236   58, // < 95 : Kandawala
237   59, // < 96 : Kerguelen Island
238   60, // < 97 : Kertau 48
239   -1, -1, // < 99
240   61, // < 100 : L.C. 5 Astro
241   -1, // < 101
242   63, // < 102 : Liberia 1964
243   64, 64, // < 104 : Luzon
244   -1, // < 105
245   65, // < 106 : Mahe 1971
246   -1, // < 107
247   69, // < 108 : Merchich
248   71, // < 109 : Midway Astro 61
249   73, 73, // < 111 : Minna
250   -1, // < 112
251   75, 75, 75, // < 115 : Nahrwan
252   76, // < 116 : Naparima BWI
253   3, 3, 3, // < 119 : Alaska NAD27
254   14, 14, // < 121 : Bahamas NAD27
255   20, 20, 20, 20, 20, // < 126 : Canada Mean NAD27
256   21, // < 127 : Canal Zone NAD27
257   31, // < 128 : Cuba NAD27
258   44, // < 129 : Greenland NAD27
259   -1, -1, // < 131
260   20, // < 132 : Canada Mean NAD27
261   -1, -1, -1, // < 135
262   70, // < 136 : Mexico NAD27
263   -1, -1, -1, -1, -1, -1, -1, -1, // < 144
264   80, // < 145 : Old Egyptian
265   81, // < 146 : Old Hawaiian
266   82, // < 147 : Old Hawaiian Kauai
267   83, // < 148 : Old Hawaiian Maui
268   81, // < 149 : Old Hawaiian Mean
269   84, // < 150 : Old Hawaiian Oahu
270   85, // < 151 : Oman
271   86, 86, 86, 86, 86, // < 156 : OSG Britain
272   87, // < 157 : Pico de Las Nieves
273   88, // < 158 : Pitcairn Astro 67
274   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // < 171
275   91, // < 172 : Puerto Rico
276   92, // < 173 : Pulkovo 1942
277   94, // < 174 : Quatar National
278   -1, -1, // < 176
279   95, // < 177 : Rome 1940
280   96, 96, 96, 96, 96, 96, 96, // < 184 : S-42 (Pulkovo 1942)
281   -1, // < 185
282   100, // < 186 : Santo DOS
283   99, // < 187 : Sao Braz
284   -1, -1, -1, -1, // < 191
285   105, 105, // < 193 : SAD-69/Mean
286   98, // < 194 : SAD-69/Brazil
287   105, 105, 105, 105, 105, 105, 105, 105, 105, 105, // < 204 : SAD-69/Mean
288   106, // < 205 : South Asia
289   109, // < 206 : Tananarive 1926
290   111, // < 207 : Timbalai 1948
291   112, 112, 112, 112, // < 211 : Tokyo mean
292   113, // < 212 : Tristan Astro 1968
293   115, // < 213 : Viti Levu 1916
294   -1, -1, // < 215
295   116, // < 216 : Wake Eniwetok 1960
296   117, // < 217 : WGS 72
297   118, // < 218 : WGS 84
298   119, // < 219 : Yacare
299   120, // < 220 : Zanderij
300   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // < 231
301   98, // < 232 : SAD-69/Brazil
302   -1, -1, // < 234
303   117, // < 235 : WGS 72
304   0, // < 236 : Adindan
305   2, // < 237 : Ain el Abd
306   7, // < 238 : ARC 1960
307   8, // < 239 : Ascension Island 58
308   -1, -1, // < 241
309   52, // < 242 : Hu-Tzu-Shan
310   53, 53, 53, // < 245 : Indian
311   -1, // < 246
312   57, // < 247 : Johnston Island 61
313   64, // < 248 : Luzon
314   -1, // < 249
315   75, // < 250 : Nahrwan
316   76, // < 251 : Naparima BWI
317   -1, -1, -1, // < 254
318   82, // < 255 : Old Hawaiian Kauai
319   83, // < 256 : Old Hawaiian Maui
320   84, // < 257 : Old Hawaiian Oahu
321   -1, -1, // < 259
322   101, // < 260 : Sapper Hill 43
323   111, // < 261 : Timbalai 1948
324   112, // < 262 : Tokyo mean
325   116 // < 263 : Wake Eniwetok 1960
326 };
327 
set_datum(int n)328 static void set_datum(int n)
329 {
330   indatum = -1;
331   if (n > 0 && n < MAX_INDATUM_INDEX) {
332     indatum = indatum_array[n];
333   }
334 
335   if (indatum == -1) {
336     warning(MYNAME ": Unsupported datum (%d), won't convert to WGS84\n", n);
337   }
338 }
339 
340 static const char* icon_descr[] = {
341   "", "Airport", "Ball Park", "Bank", "Bar", "Boat Ramp", "Campground", "Car",
342   "City (Large)", "City (Medium)", "City (Small)", "Dam", "Danger Area",
343   "Drinking Water", "Fishing Area", "Gas Station", "Glider Area", "Golf Course",
344   "Heliport", "Hotel", "Animals", "Information", "Man Overboard", "Marina",
345   "Mine", "Medical Facility", "Parachute Area", "Park", "Parking Area",
346   "Picnic Area", "Private Field", "Residence", "Restaurant", "Restroom",
347   "Scenic Area", "School", "Seaplane Base", "Shipwreck", "Shopping Center",
348   "Short Tower", "Policy Station", "Ski Resort", "Soft Field", "Swimming Area",
349   "Tall Tower", "Telephone", "Tracback Point", "Ultralight Area", "Waypoint",
350   "Boat", "Exit", "Flag", "Duck", "Buoy", "Back Track", "Beach", "Bridge",
351   "Building", "Car Repair", "Cemetery", "Church", "Civil", "Convenience Store",
352   "Crossing", "Fast Food", "Forest", "Ghost Town", "Levee", "Military",
353   "Oil Field", "Post Office", "Rv Park", "Scales", "Summit", "Toll Booth",
354   "Trail Head", "Truck Stop", "Tunnel", "Highway", "Gate", "Fall", "Fence",
355   "Mata-Burro", "Fitness Center", "Movie Theater", "Live Theater", "Zoo", "Horn",
356   "Bowling", "Car Rental", "City (Capitol)", "Controlled Area", "Stadium",
357   "Museum", "Amusement Park", "Skull", "Department Store", "Pharmacy", "Pizza",
358   "Diver Down Flag 1", "Light", "Pin", "", "Pigsty", "Tree", "Bamboo",
359   "Banana Plant", "Arrow-Down", "Bifurcation", "Cavern", "River", "Rock",
360   "Arrow-Up", "Trunk", "Soccer Field", "Sporting Court", "Flag, Green", "Trench",
361   "Ship-Yellow", "Green Sign", "Swamp", "Lake", "Stop!",
362   "Fishing Hot Spot Facility", "Speed Reducer", "Stairway", "Cactus", "Ship-Red",
363   "Letter - S", "Letter - D", "Letter - N",
364   "Crossing", "Cross", "Flag, Red", "Curve1", "Curve2", "Curve3", "Curve4",
365   "Letter - W", "Letter - L", "Letter - R", "Radio Beacon", "Road Sign",
366   "Geocache", "Geocache Found", "Traffic Light", "Bus Station", "Train Station",
367   "School", "Mile Marker", "Conservation Area", "Waypoint", "Box", "Aerial",
368   "Auto Repair", "Boat", "Exit Ramp", "Fixed Nav Aid", "Floating Buoy", "Garden",
369   "Fish Farm", "Lighthouse", "Truck Service", "Resort", "Scuba", "Shooting",
370   "Sight Seeing", "Sounding", "Winery", "Navaid, Amber", "Navaid, Black",
371   "Navaid, Blue", "Navaid, Green", "Navaid, Green/Red", "Navaid, Green/White",
372   "Navaid, Orange", "Navaid, Red", "Navaid, Red/Green", "Navaid, Red/White",
373   "Navaid, Violet", "Navaid, White", "Navaid, White/Green", "Navaid, White/Red",
374   "Buoy, White", "Dot, White", "Red Square", "Red Diamond", "Green Square",
375   "Green Diamond", "Restricted Area", "Navaid (unlit)", "Dot (Small)", "Libraries", "Waypoint", "Waypoint1",
376   "Waypoint2", "Mark (1)", "Mark (2)", "Mark (3)", "Cross (Red)", "Store",
377   "Exclamation", "Flag (EUA)", "Flag (CAN)", "Flag (BRA)", "Man", "Animals",
378   "Deer Tracks", "Tree Stand", "Bridge", "Fence", "Intersection",
379   "Non Direct Beacon", "VHF Omni Range", "Vor/Tacan", "Vor-Dme",
380   "1st Approach Fix", "Localizer Outer", "Missed Appr. Pt", "Tacan",
381   "CheckPoint", nullptr
382 };
383 
384 
convert_datum(double * lat,double * lon)385 static void convert_datum(double* lat, double* lon)
386 {
387   double amt;
388   if (indatum != -1 && indatum != 118) {
389     GPS_Math_Known_Datum_To_WGS84_M(*lat, *lon, 0.0,
390                                     lat, lon, &amt, indatum);
391   }
392 }
393 
394 /* Callbacks */
395 
396 static void
gtm_rd_init(const QString & fname)397 gtm_rd_init(const QString& fname)
398 {
399   file_in = gbfopen_le(fname, "rb", MYNAME);
400   int version = fread_integer(file_in);
401   QString name = fread_fixedstring(file_in, 10);
402   if (version == -29921) {
403     fatal(MYNAME ": Uncompress the file first\n");
404   }
405   if (name != "TrackMaker") {
406     fatal(MYNAME ": Invalid file format\n");
407   }
408   if (version != 211) {
409     fatal(MYNAME ": Invalid format version\n");
410   }
411 
412   /* Header */
413   fread_discard(file_in, 15);
414   ws_count = fread_long(file_in);
415   fread_discard(file_in, 4);
416   wp_count = fread_long(file_in);
417   tr_count = fread_long(file_in);
418   rt_count = fread_long(file_in);
419   fread_discard(file_in, 16);
420   im_count = fread_long(file_in);
421   ts_count = fread_long(file_in);
422   fread_discard(file_in, 28);
423   fread_string_discard(file_in);
424   fread_string_discard(file_in);
425   fread_string_discard(file_in);
426   fread_string_discard(file_in);
427 
428   /* User Grid and Datum */
429   fread_discard(file_in, 34);
430   set_datum(fread_integer(file_in));
431   fread_discard(file_in, 22);
432 }
433 
434 static void
gtm_rd_deinit()435 gtm_rd_deinit()
436 {
437   gbfclose(file_in);
438 }
439 
count_track_styles(const route_head * rte)440 static void count_track_styles(const route_head* rte)
441 {
442   if (rte->rte_waypt_ct > 0) {
443     ts_count++;
444   }
445 }
446 
447 static void
gtm_wr_init(const QString & fname)448 gtm_wr_init(const QString& fname)
449 {
450   // Count the number of track style entires.
451   // We don't output a track style for any track that doesn't have any
452   // waypoints.
453   // Note that it is impossible to store a track without any waypoints
454   // in this format as every tracklog entry represents a waypoint,
455   // and a new track is defined by a tracklog entry with the tracklog flag set.
456   ts_count = 0;
457   track_disp_all(count_track_styles, nullptr, nullptr);
458 
459   file_out = gbfopen_le(fname, "wb", MYNAME);	/* little endian */
460 
461   /* Header */
462   fwrite_integer(file_out, 211);
463   fwrite_fixedstring(file_out, "TrackMaker", 10);
464   fwrite_byte(file_out, 0);
465   fwrite_byte(file_out, 0);
466   fwrite_byte(file_out, 8);
467   fwrite_byte(file_out, 0);
468   fwrite_byte(file_out, 0);
469   fwrite_byte(file_out, 0);
470   fwrite_byte(file_out, 0);
471   fwrite_long(file_out, 0);
472   fwrite_long(file_out, 16777215);
473   fwrite_long(file_out, waypt_count() ? 4 : 0); /* num waypoint styles */
474   fwrite_long(file_out, 0);
475   fwrite_long(file_out, waypt_count()); /* num waypoints */
476   fwrite_long(file_out, track_waypt_count());
477   fwrite_long(file_out, route_waypt_count());
478   fwrite_single(file_out, 0); /* maxlon */
479   fwrite_single(file_out, 0); /* minlon */
480   fwrite_single(file_out, 0); /* maxlat */
481   fwrite_single(file_out, 0); /* minlat */
482   fwrite_long(file_out, 0);
483   fwrite_long(file_out, ts_count); /* num tracklog styles */
484   fwrite_single(file_out, 0);
485   fwrite_single(file_out, 0);
486   fwrite_bool(file_out, 0);
487   fwrite_bool(file_out, 0);
488   fwrite_bool(file_out, 0);
489   fwrite_bool(file_out, 0);
490   fwrite_bool(file_out, 0);
491   fwrite_bool(file_out, 0);
492   fwrite_bool(file_out, 0);
493   fwrite_bool(file_out, 0);
494   fwrite_bool(file_out, 0);
495   fwrite_bool(file_out, 0);
496   fwrite_string(file_out, "Times New Roman");
497   fwrite_string(file_out, "");
498   fwrite_string(file_out, "");
499   fwrite_string(file_out, "");
500 
501   /* User Grid and Datum */
502   fwrite_null(file_out, 34);
503   fwrite_integer(file_out, 217); /* WGS84 */
504   fwrite_null(file_out, 22);
505 }
506 
507 static void
gtm_wr_deinit()508 gtm_wr_deinit()
509 {
510   gbfclose(file_out);
511 }
512 
513 static void
gtm_read()514 gtm_read()
515 {
516   route_head* trk_head = nullptr;
517   route_head* rte_head = nullptr;
518   Waypoint* wpt;
519   QList<route_head*> real_track_list;
520   unsigned int icon;
521   int i;
522 
523   /* Image information */
524   for (i = 0; i != im_count; i++) {
525     fread_string_discard(file_in);
526     fread_string_discard(file_in);
527     fread_discard(file_in, 30);
528   }
529 
530   /* Waypoints */
531   for (i = 0; i != wp_count; i++) {
532     wpt = new Waypoint;
533     wpt->latitude = fread_double(file_in);
534     wpt->longitude = fread_double(file_in);
535     convert_datum(&wpt->latitude, &wpt->longitude);
536     wpt->shortname = fread_fixedstring(file_in, 10);
537     wpt->description = fread_string(file_in);
538     icon = fread_integer(file_in);
539     if (icon < sizeof(icon_descr)/sizeof(char*)) {
540       wpt->icon_descr = icon_descr[icon];
541     }
542     fread_discard(file_in, 1);
543     wpt->SetCreationTime(fread_long(file_in));
544     if (wpt->creation_time.isValid()) {
545       wpt->creation_time = wpt->creation_time.addSecs(EPOCH89DIFF);
546     }
547     fread_discard(file_in, 2);
548     wpt->altitude = fread_single(file_in);
549     if (wpt->altitude == unknown_alt_gtm) {
550       wpt->altitude = unknown_alt;
551     }
552     fread_discard(file_in, 2);
553     waypt_add(wpt);
554   }
555 
556   /* Waypoint Styles */
557   if (wp_count) {
558     for (i = 0; i != ws_count; i++) {
559       fread_discard(file_in, 4);
560       fread_string_discard(file_in);
561       fread_discard(file_in, 24);
562     }
563   }
564 
565   /* Tracklogs */
566   for (i = 0; i != tr_count; i++) {
567     wpt = new Waypoint;
568     wpt->latitude = fread_double(file_in);
569     wpt->longitude = fread_double(file_in);
570     convert_datum(&wpt->latitude, &wpt->longitude);
571     wpt->SetCreationTime(fread_long(file_in));
572     if (wpt->creation_time.isValid()) {
573       wpt->creation_time = wpt->creation_time.addSecs(EPOCH89DIFF);
574     }
575     start_new = fread_byte(file_in);
576     wpt->altitude = fread_single(file_in);
577     if (wpt->altitude == unknown_alt_gtm) {
578       wpt->altitude = unknown_alt;
579     }
580     if (start_new || !trk_head) {
581       trk_head = new route_head;
582       track_add_head(trk_head);
583       real_track_list.append(trk_head);
584     }
585     track_add_wpt(trk_head, wpt);
586   }
587 
588   /* Tracklog styles */
589   // TODO: The format document states there are ts_count tracklog style entries,
590   //       and tr_count tracklog entries.
591   //       Some tracklog entries may be continuation entries, so we turn these
592   //       into real_track_list.size() <= tr_count tracks.
593   //       If ts_count != real_track_list.size() we don't know how to line up
594   //       the tracklogs, and the real tracks, with the tracklog styles.
595   if (ts_count != real_track_list.size()) {
596     warning(MYNAME ": The number of tracklog entries with the new flag "
597            "set doesn't match the number of tracklog style entries.\n"
598            "  This is unexpected and may indicate a malformed input file.\n"
599            "  As a result the track names may be incorrect.\n");
600   }
601   // Read the entire tracklog styles section whether we use it or not.
602   for (i = 0; i != ts_count; i++) {
603     QString tname = fread_string(file_in);
604     fread_discard(file_in, 12);
605     if (i < real_track_list.size()) {
606       trk_head = real_track_list.at(i);
607       trk_head->rte_name = tname;
608     }
609   }
610 
611   /* Routes */
612   for (i = 0; i != rt_count; i++) {
613     wpt = new Waypoint;
614     wpt->latitude = fread_double(file_in);
615     wpt->longitude = fread_double(file_in);
616     convert_datum(&wpt->latitude, &wpt->longitude);
617     wpt->shortname = fread_fixedstring(file_in, 10);
618     wpt->description = fread_string(file_in);
619     QString route_name = fread_string(file_in);
620     icon = fread_integer(file_in);
621     if (icon < sizeof(icon_descr)/sizeof(char*)) {
622       wpt->icon_descr = icon_descr[icon];
623     }
624     fread_discard(file_in, 1);
625     start_new = fread_byte(file_in);
626     fread_discard(file_in, 6);
627     wpt->altitude = fread_single(file_in);
628     if (wpt->altitude == unknown_alt_gtm) {
629       wpt->altitude = unknown_alt;
630     }
631     fread_discard(file_in, 2);
632 
633     if (start_new || !rte_head) {
634       rte_head = new route_head;
635       rte_head->rte_name = route_name;
636       route_add_head(rte_head);
637     }
638     route_add_wpt(rte_head, wpt);
639   }
640 }
641 
icon_from_descr(const QString & descr)642 static int icon_from_descr(const QString& descr)
643 {
644   for (int i = 0; icon_descr[i]; i++) {
645     if (descr.compare(icon_descr[i]) == 0) {
646       return i;
647     }
648   }
649   return 48;
650 }
651 
write_waypt(const Waypoint * wpt)652 static void write_waypt(const Waypoint* wpt)
653 {
654   fwrite_double(file_out, wpt->latitude);
655   fwrite_double(file_out, wpt->longitude);
656   fwrite_fixedstring(file_out, wpt->shortname, 10);
657   fwrite_string(file_out, wpt->description);
658   fwrite_integer(file_out, icon_from_descr(wpt->icon_descr));
659   fwrite_byte(file_out, 3);
660   if (wpt->creation_time.isValid()) {
661     fwrite_long(file_out, wpt->GetCreationTime().toTime_t()-EPOCH89DIFF);
662   } else {
663     fwrite_long(file_out, 0);
664   }
665   fwrite_integer(file_out, 0);
666   if (wpt->altitude == unknown_alt) {
667     fwrite_single(file_out, unknown_alt_gtm);
668   } else {
669     fwrite_single(file_out, wpt->altitude);
670   }
671   fwrite_integer(file_out, 0);
672 }
673 
start_rte(const route_head * rte)674 static void start_rte(const route_head* rte)
675 {
676   rte_active = rte;
677   start_new = 1;
678 }
679 
write_trk_waypt(const Waypoint * wpt)680 static void write_trk_waypt(const Waypoint* wpt)
681 {
682   fwrite_double(file_out, wpt->latitude);
683   fwrite_double(file_out, wpt->longitude);
684   fwrite_long(file_out, wpt->GetCreationTime().toTime_t()-EPOCH89DIFF);
685   fwrite_byte(file_out, start_new);
686   if (wpt->altitude == unknown_alt) {
687     fwrite_single(file_out, unknown_alt_gtm);
688   } else {
689     fwrite_single(file_out, wpt->altitude);
690   }
691   start_new = 0;
692 }
693 
write_trk_style(const route_head * trk)694 static void write_trk_style(const route_head* trk)
695 {
696   if (trk->rte_waypt_ct > 0) {
697     fwrite_string(file_out, trk->rte_name);
698     fwrite_byte(file_out, 1);
699     fwrite_long(file_out, 0);
700     fwrite_single(file_out, 0);
701     fwrite_byte(file_out, 0);
702     fwrite_integer(file_out, 0);
703   }
704 }
705 
write_rte_waypt(const Waypoint * wpt)706 static void write_rte_waypt(const Waypoint* wpt)
707 {
708   fwrite_double(file_out, wpt->latitude);
709   fwrite_double(file_out, wpt->longitude);
710   fwrite_fixedstring(file_out, wpt->shortname, 10);
711   fwrite_string(file_out, wpt->description);
712   fwrite_string(file_out, rte_active->rte_name);
713   fwrite_integer(file_out, icon_from_descr(wpt->icon_descr));
714   fwrite_byte(file_out, 3);
715   fwrite_byte(file_out, start_new);
716   fwrite_long(file_out, 0);
717   fwrite_integer(file_out, 0);
718   if (wpt->altitude == unknown_alt) {
719     fwrite_single(file_out, unknown_alt_gtm);
720   } else {
721     fwrite_single(file_out, wpt->altitude);
722   }
723   fwrite_integer(file_out, 0);
724   start_new = 0;
725 }
726 
727 static void
gtm_write()728 gtm_write()
729 {
730   waypt_disp_all(write_waypt);
731   if (waypt_count()) {
732     gbfwrite(WAYPOINTSTYLES, 1, sizeof(WAYPOINTSTYLES)-1, file_out);
733   }
734   track_disp_all(start_rte, nullptr, write_trk_waypt);
735   track_disp_all(write_trk_style, nullptr, nullptr);
736   route_disp_all(start_rte, nullptr, write_rte_waypt);
737 }
738 
739 static
740 QVector<arglist_t> gtm_args = {
741 };
742 
743 ff_vecs_t gtm_vecs = {
744   ff_type_file,
745   FF_CAP_RW_ALL,
746   gtm_rd_init,
747   gtm_wr_init,
748   gtm_rd_deinit,
749   gtm_wr_deinit,
750   gtm_read,
751   gtm_write,
752   nullptr,
753   &gtm_args,
754   CET_CHARSET_ASCII, 0, /* CET-REVIEW */
755   NULL_POS_OPS,
756   nullptr
757 };
758