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", ×ynth, "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