1 /*
2     Read various Delorme routes including anr, rte, and rtd.
3 
4     Copyright (C) 2003 Ron Parker and Robert Lipe.
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 
23 #define MYNAME "saroute"
24 #include "defs.h"
25 #include "grtcirc.h"
26 #include <cstddef>
27 
28 static gbfile* infile;
29 
30 static char* turns_important = nullptr;
31 static char* turns_only = nullptr;
32 static char* controls = nullptr;
33 static char* split = nullptr;
34 static char* timesynth = nullptr;
35 
36 static int control = 0;
37 
38 static
39 QVector<arglist_t> saroute_args = {
40   {
41     "turns_important", &turns_important,
42     "Keep turns if simplify filter is used",
43     nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
44   },
45   {
46     "turns_only", &turns_only, "Only read turns; skip all other points",
47     nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
48   },
49   {
50     "split", &split, "Split into multiple routes at turns",
51     nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
52   },
53   {
54     "controls", &controls, "Read control points as waypoint/route/none",
55     "none", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
56   },
57   {
58     "times", &timesynth, "Synthesize track times",
59     nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
60   },
61 };
62 
63 #define ReadShort(f) gbfgetint16(f)
64 #define ReadLong(f) gbfgetint32(f)
65 
66 static unsigned char*
ReadRecord(gbfile * f,gbsize_t size)67 ReadRecord(gbfile* f, gbsize_t size)
68 {
69   auto* result = (unsigned char*) xmalloc(size);
70 
71   (void)gbfread(result, size, 1, f);
72   return result;
73 }
74 
75 static void
Skip(gbfile * f,gbsize_t distance)76 Skip(gbfile* f, gbsize_t distance)
77 {
78   gbfseek(f, distance, SEEK_CUR);
79 }
80 
81 static void
rd_init(const QString & fname)82 rd_init(const QString& fname)
83 {
84   infile = gbfopen(fname, "rb", MYNAME);
85   if (split && (turns_important || turns_only)) {
86     fatal(MYNAME
87           ": turns options are not compatible with split\n");
88   }
89   if (controls) {
90     switch (controls[0]) {
91     case 'n':
92       control = 0;
93       break;
94     case 'r':
95       control = 1;
96       break;
97     case 'w':
98       control = 2;
99       break;
100     default:
101       fatal(MYNAME
102             ": unrecognized value for 'controls'\n");
103       break;
104     }
105   }
106 }
107 
108 static void
rd_deinit()109 rd_deinit()
110 {
111   gbfclose(infile);
112 }
113 
114 static void
my_read()115 my_read()
116 {
117   static int serial = 0;
118   struct ll {
119     int32_t lat;
120     int32_t lon;
121   } *latlon;
122   struct ll mylatlon;
123   route_head* track_head = nullptr;
124   Waypoint* wpt_tmp;
125   char* routename = nullptr;
126   double seglen = 0.0;
127   int32_t  starttime = 0;
128   int32_t  transittime = 0;
129   double totaldist = 0.0;
130   double oldlat = 0;
131   double oldlon = 0;
132 
133   ReadShort(infile);		/* magic */
134   uint16_t version = ReadShort(infile);
135 
136   ReadLong(infile);
137   if (version >= 6) {
138     ReadLong(infile);
139     ReadLong(infile);
140   }
141 
142   /*
143    * end of header
144    */
145 
146   ReadShort(infile);
147   uint32_t recsize = ReadLong(infile);
148   /*
149    * the first recsize, oddly, doesn't include the filename string
150    * but it does include the header.
151    */
152   unsigned char* record = ReadRecord(infile, recsize);
153 
154   uint16_t stringlen = le_read16((uint16_t*)(record + 0x1a));
155   if (stringlen) {
156     routename = (char*)xmalloc(stringlen + 1);
157     routename[stringlen] = '\0';
158     memcpy(routename, record+0x1c, stringlen);
159   }
160   Skip(infile, stringlen - 4);
161   xfree(record);
162 
163   /*
164    * end of filename record
165    */
166 
167   /*
168    * here lie the route description records
169    */
170   if (version < 6 || (control == 1)) {
171     track_head = new route_head;
172     route_add_head(track_head);
173     if (control) {
174       track_head->rte_name = "control points";
175     } else {
176       track_head->rte_name = routename;
177     }
178   }
179   uint32_t count = ReadLong(infile);
180   while (count) {
181     ReadShort(infile);
182     recsize = ReadLong(infile);
183     if (version < 6 || control) {
184       record = ReadRecord(infile, recsize);
185       latlon = (struct ll*)(record);
186 
187       /* These records are backwards for some reason */
188       double lat = (0x80000000UL -
189         le_read32(&latlon->lon)) / (double)(0x800000);
190       double lon = (0x80000000UL -
191         le_read32(&latlon->lat)) / (double)(0x800000);
192 
193       wpt_tmp = new Waypoint;
194       wpt_tmp->latitude = lat;
195       wpt_tmp->longitude = -lon;
196       if (control) {
197         int obase;
198 
199         /* Somewhere around TopoUSA 6.0, these moved  */
200         /* This block also seems to get miscompiled
201          * at -O0 on Linux.  I tried rewriting it to
202          * reduce/eliminate some of the really funky
203          * pointer math and casting that was here.
204          */
205         if (version >= 11) {
206           obase = 20;
207         } else {
208           obase = 18;
209         }
210 
211         int addrlen = le_read16(&record[obase]);
212         int cmtlen = le_read16(&record[obase+2+addrlen]);
213         (void) cmtlen;
214         // That we've had no bugreports on this strongly indicates this code
215         // is never used... Look in revision history if anyone cares.
216         wpt_tmp->shortname = "booger";
217         wpt_tmp->notes = "goober";
218       } else {
219         wpt_tmp->shortname = QString::asprintf("\\%5.5x", serial++);
220       }
221       if (control == 2) {
222         waypt_add(wpt_tmp);
223       } else {
224         route_add_wpt(track_head, wpt_tmp);
225       }
226       xfree(record);
227       if (version >= 6) {
228         /*
229              * two longs of scrap after each record, don't know why
230          */
231         ReadLong(infile);
232         ReadLong(infile);
233       }
234     } else {
235       Skip(infile, recsize);
236       /*
237        * two longs of scrap after each record, don't know why
238        */
239       ReadLong(infile);
240       ReadLong(infile);
241     }
242     count--;
243   }
244   /*
245    * end of route desc records
246    */
247 
248   /*
249    * outercount is the number of route segments (start+end+stops+vias-1)
250    */
251 
252   uint32_t outercount = ReadLong(infile);
253   while (outercount) {
254 
255     /*
256      * unknown record (route params?) lives here
257      */
258     ReadShort(infile);
259     recsize = ReadLong(infile);
260     Skip(infile, recsize);
261 
262     /*
263      * end of unknown record
264      */
265 
266     /*
267      * routing begins here
268      */
269     count = ReadLong(infile);
270     if (count) {
271       track_head = new route_head;
272       if (timesynth) {
273         track_add_head(track_head);
274       } else {
275         route_add_head(track_head);
276       }
277       if (routename && !split) {
278         track_head->rte_name = routename;
279       }
280     }
281     while (count) {
282       route_head* old_track_head = nullptr;
283       ReadShort(infile);
284       recsize = ReadLong(infile);
285       record = ReadRecord(infile, recsize);
286       stringlen = le_read16((uint16_t*)record);
287       if (split && stringlen) {
288         if (track_head->rte_waypt_ct) {
289           old_track_head = track_head;
290           track_head = new route_head;
291           if (timesynth) {
292             track_add_head(track_head);
293           } else {
294             route_add_head(track_head);
295           }
296         } // end if
297         if (track_head->rte_name.isEmpty()) {
298           track_head->rte_name = "Track";
299         }
300       }
301 
302       if (timesynth) {
303         seglen = le_read_double(
304                    record + 2 + stringlen + 0x08);
305         starttime = le_read32((uint32_t*)
306                               (record + 2 + stringlen + 0x30));
307         transittime = le_read32((uint32_t*)
308                                 (record + 2 + stringlen + 0x10));
309         seglen *= kMilesPerKilometer; /* to miles */
310       }
311 
312       uint16_t coordcount = le_read16((uint16_t*)
313         (record + 2 + stringlen + 0x3c));
314       latlon = (struct ll*)(record + 2 + stringlen + 0x3c + 2);
315       count--;
316       if (count) {
317         coordcount--;
318       }
319 
320       int first = 1;
321 
322       while (coordcount) {
323         wpt_tmp = new Waypoint;
324 
325         // copy to make sure we don't violate alignment restrictions.
326         memcpy(&mylatlon,latlon,sizeof(mylatlon));
327         double lat = (0x80000000UL -
328             le_read32(&mylatlon.lat)) /
329           (double)(0x800000);
330         double lon = (0x80000000UL -
331             le_read32(&mylatlon.lon)) /
332           (double)(0x800000);
333 
334         wpt_tmp->latitude = lat;
335         wpt_tmp->longitude = -lon;
336         if (stringlen && ((coordcount>1) || count)) {
337           wpt_tmp->shortname = QString(((char*)record)+2);
338         } else {
339           wpt_tmp->shortname = QString::asprintf("\\%5.5x", serial++);
340         }
341         if (timesynth) {
342           if (!first) {
343             double dist = radtomiles(gcdist(
344                                        RAD(lat), RAD(-lon),
345                                        RAD(oldlat),
346                                        RAD(-oldlon)));
347             totaldist += dist;
348             if (totaldist > seglen) {
349               totaldist = seglen;
350             }
351             wpt_tmp->SetCreationTime(
352               gpsbabel_time+starttime+
353               transittime * totaldist/seglen);
354           } else {
355             wpt_tmp->SetCreationTime(gpsbabel_time+starttime);
356             totaldist = 0;
357           }
358           oldlat = lat;
359           oldlon = lon;
360         }
361         if (turns_important && stringlen) {
362           wpt_tmp->route_priority=1;
363         }
364         if (!turns_only || stringlen) {
365           if (timesynth) {
366             track_add_wpt(track_head,wpt_tmp);
367           } else {
368             route_add_wpt(track_head, wpt_tmp);
369           }
370           if (old_track_head) {
371             if (timesynth) {
372               track_add_wpt(old_track_head,
373                             new Waypoint(*wpt_tmp));
374             } else {
375               route_add_wpt(old_track_head,
376                             new Waypoint(*wpt_tmp));
377             }
378             old_track_head = nullptr;
379           }
380         }
381 
382         latlon++;
383         coordcount--;
384         stringlen = 0;
385         /* the stop point is a "turn" */
386         if (coordcount == 1 && count == 0) {
387           stringlen = 1;
388         }
389         first = 0;
390       }
391       if (version > 10) {
392         Skip(infile,2*sizeof(uint32_t));
393       }
394       xfree(record);
395     }
396     /*
397      * end of routing
398      */
399     outercount--;
400   }
401   if (routename) {
402     xfree(routename);
403   }
404 
405 }
406 
407 ff_vecs_t saroute_vecs = {
408   ff_type_file,
409   { ff_cap_none, ff_cap_read, ff_cap_none},
410   rd_init,
411   nullptr,
412   rd_deinit,
413   nullptr,
414   my_read,
415   nullptr,
416   nullptr,
417   &saroute_args,
418   CET_CHARSET_UTF8, 1	/* do nothing | CET-REVIEW */
419   , NULL_POS_OPS,
420   nullptr
421 };
422