1 /*
2
3 Support for G7ToWin data files (.g7t),
4 Copyright (C) 2007 Olaf Klein, o.b.klein@gpsbabel.org
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 History:
23 04/07/2007: start programming
24 04/15/2007: added to gpsbabel
25 */
26
27 #include "defs.h"
28 #include "cet_util.h"
29 #include "csv_util.h"
30 #include "garmin_fs.h"
31 #include "garmin_tables.h"
32 #include "jeeps/gpsmath.h"
33 #include "strptime.h"
34
35 #include <cstdio>
36 #include <cstdlib>
37 #include <ctime>
38
39 #if CSVFMTS_ENABLED
40
41 #define MYNAME "g7towin"
42
43 #define G7T_HEADER "Version 2:G7T"
44
45 static gbfile* fin;
46 static grid_type grid;
47 static int datum;
48 static gpsdata_type mode;
49 static double altf;
50 static int gardown;
51 static int event_ct;
52
53 static
54 QVector<arglist_t> g7towin_args = {
55 };
56
57 #define WAYPT__OFS 0x00000
58 #define TRKPT__OFS 0x01000
59
60 #define WPT_c0_OFS 0x0c000
61 #define WPT_c1_OFS 0x0c100
62 #define WPT_c2_OFS 0x0c200
63 #define WPT_c3_OFS 0x0c300
64 #define WPT_c4_OFS 0x0c400
65 #define WPT_c5_OFS 0x0c500
66 #define WPT_c6_OFS 0x0c600
67 #define WPT_c7_OFS 0x0c700
68 #define WPT_c8_OFS 0x0c800
69 #define WPT_cA_OFS 0x0cA00
70 #define WPT_cB_OFS 0x0cB00
71 #define WPT_cC_OFS 0x0cC00
72 #define WPT_cD_OFS 0x0cD00
73
74 static void
parse_line(char * buff,int index,const char * delimiter,Waypoint * wpt)75 parse_line(char* buff, int index, const char* delimiter, Waypoint* wpt)
76 {
77 char* cin;
78 garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
79
80 while ((cin = csv_lineparse(buff, delimiter, "", index++))) {
81
82 buff = nullptr;
83 cin = lrtrim(cin);
84
85 if ((*cin == '\0') ||
86 (strcmp(cin, "INF") == 0) ||
87 (strcmp(cin, "1e25") == 0) ||
88 (strcmp(cin, "1.0e25") == 0)) {
89 continue;
90 }
91
92 switch (index) {
93
94 int categories;
95 struct tm tm;
96 char* cerr;
97
98 case TRKPT__OFS + 1:
99 cin += parse_coordinates(cin, datum, grid,
100 &wpt->latitude, &wpt->longitude, MYNAME);
101 while (isspace(*cin)) {
102 cin++;
103 }
104
105 memset(&tm, 0, sizeof(tm));
106 cerr = strptime(cin, "%a %b %d %H:%M:%S %Y", &tm);
107 if (cerr == nullptr) {
108 fatal(MYNAME ": Unable to convert date (%s)!\n", cin);
109 }
110 wpt->SetCreationTime(mkgmtime(&tm));
111 break;
112
113 case WAYPT__OFS + 1:
114 wpt->description = (cin);
115 break;
116
117 case WAYPT__OFS + 2:
118 wpt->icon_descr = gt_find_desc_from_icon_number(
119 atoi(cin), PCX);
120 break;
121
122 case WAYPT__OFS + 4:
123 if (strcmp(cin, "S+C") == 0) {
124 garmin_fs_t::set_display(gmsd, gt_display_mode_symbol_and_comment);
125 } else if (strcmp(cin, "S") == 0) {
126 garmin_fs_t::set_display(gmsd, gt_display_mode_symbol);
127 } else if (strcmp(cin, "S+N") == 0) {
128 garmin_fs_t::set_display(gmsd, gt_display_mode_symbol_and_name);
129 }
130 break;
131
132 case WPT_cA_OFS + 1:
133 case WPT_c1_OFS + 1:
134 wpt->shortname = cin;
135 break;
136
137 case WPT_cA_OFS + 4:
138 case WPT_c4_OFS + 2:
139 garmin_fs_t::set_city(gmsd, cin);
140 break;
141
142 case WPT_cA_OFS + 5:
143 case WPT_c4_OFS + 3:
144 garmin_fs_t::set_state(gmsd, cin);
145 break;
146
147 case WPT_cA_OFS + 6:
148 case WPT_c4_OFS + 4:
149 garmin_fs_t::set_cc(gmsd, cin);
150 break;
151
152 case WPT_cB_OFS + 1:
153 case WPT_c6_OFS + 2:
154 garmin_fs_t::set_facility(gmsd, cin);
155 break;
156
157 case WPT_cB_OFS + 2:
158 case WPT_c6_OFS + 3:
159 garmin_fs_t::set_addr(gmsd, cin);
160 break;
161
162 case WPT_cB_OFS + 3: /*cross road */
163 case WPT_c6_OFS + 4:
164 garmin_fs_t::set_cross_road(gmsd, cin);
165 break;
166
167 case TRKPT__OFS + 2: /* altitude */
168 case WPT_cC_OFS + 1:
169 case WPT_c5_OFS + 1:
170 case WPT_c8_OFS + 1:
171 wpt->altitude = altf * atof(cin);
172 break;
173
174 case TRKPT__OFS + 3: /* depth */
175 case WPT_cC_OFS + 2:
176 case WPT_c5_OFS + 2:
177 case WPT_c8_OFS + 2:
178 WAYPT_SET(wpt, depth, altf * atof(cin));
179 break;
180
181 case TRKPT__OFS + 10: /* temperature */
182 if (*cin == '|') {
183 cin++; /* in track points */
184 }
185 if (strcmp(cin, "1e25") == 0) {
186 break;
187 }
188 if (strcmp(cin, "1.0e25") == 0) {
189 break;
190 }
191 /* fallthrough */
192 case WPT_cD_OFS + 1:
193 case WPT_cB_OFS + 6:
194 WAYPT_SET(wpt, temperature, atof(cin));
195 break;
196
197 case WAYPT__OFS + 6: /* proximity */
198 case WPT_cD_OFS + 2:
199 WAYPT_SET(wpt, proximity, atof(cin));
200 break;
201
202 case WPT_cB_OFS + 5:
203 case WPT_cD_OFS + 3:
204 categories = atoi(cin);
205 if (categories != 0) {
206 garmin_fs_t::set_category(gmsd, atoi(cin));
207 }
208 break;
209
210 #if 0
211
212 /* currently unused */
213
214 case TRKPT__OFS + 5: /* distance from previous point */
215 case TRKPT__OFS + 6: /* distance from segment start */
216 case TRKPT__OFS + 7: /* distance from start */
217 case TRKPT__OFS + 8: /* velocity from previous point */
218 case TRKPT__OFS + 9: /* time (in seconds) from previous point */
219 break;
220
221 case WAYPT__OFS + 3: /* ignore color */
222 break;
223
224 case WAYPT__OFS + 5: /* always '0' */
225 break;
226
227 case TRKPT__OFS + 4:
228 if (case_ignore_strcmp(cin, "FT") == 0) ;
229 else if (case_ignore_strcmp(cin, "M") == 0) ;
230 else if (case_ignore_strcmp(cin, "SM") == 0) ;
231 else if (case_ignore_strcmp(cin, "NM") == 0) ;
232 else if (case_ignore_strcmp(cin, "KM") == 0) ;
233 break;
234
235 case WPT_cB_OFS + 4: /* unknown (datatype) */
236 break;
237
238 case WPT_cC_OFS + 3: /* waypt_class (always FF) */
239 break;
240
241 case WPT_cC_OFS + 4: /* class & subclass */
242 case WPT_cC_OFS + 5:
243 case WPT_cC_OFS + 6:
244 case WPT_cC_OFS + 7:
245 case WPT_cC_OFS + 8:
246 case WPT_cC_OFS + 9:
247 case WPT_cC_OFS + 10:
248 case WPT_cC_OFS + 11:
249 case WPT_cC_OFS + 12:
250 case WPT_cC_OFS + 13:
251 case WPT_cC_OFS + 14:
252 case WPT_cC_OFS + 15:
253 case WPT_cC_OFS + 16:
254 case WPT_cC_OFS + 17:
255 case WPT_cC_OFS + 18:
256 case WPT_cC_OFS + 19:
257 case WPT_cC_OFS + 20:
258 case WPT_cC_OFS + 21:
259 break;
260
261 case WPT_cC_OFS + 22:
262 /* distance */
263 break;
264 #endif
265 }
266 }
267 }
268
269 static Waypoint*
parse_waypt(char * buff)270 parse_waypt(char* buff)
271 {
272 char* cin;
273 struct tm tm;
274
275 auto* wpt = new Waypoint;
276 garmin_fs_t* gmsd = garmin_fs_alloc(-1);
277 wpt->fs.FsChainAdd(gmsd);
278
279 if (gardown) {
280 cin = buff + 6;
281 } else {
282 /* We've seen waypoints with length of 14 and 15 !!! */
283 cin = buff + 15;
284 while ((cin > buff) && (! isspace(*cin))) {
285 cin--;
286 }
287 }
288
289 while (isspace(*cin)) {
290 cin--;
291 }
292 if (cin >= buff) {
293 char*s = xstrndup(buff, cin - buff + 1);
294 wpt->shortname = s;
295 xfree(s);
296 }
297
298 if (gardown) {
299 buff += 6;
300 } else {
301 buff += 15;
302 }
303 while (isspace(*buff)) {
304 buff++;
305 }
306
307 buff += parse_coordinates(buff, datum, grid,
308 &wpt->latitude, &wpt->longitude, MYNAME);
309 while (isspace(*buff)) {
310 buff++;
311 }
312
313 memset(&tm, 0, sizeof(tm));
314 char* cerr = strptime(buff, "%a %b %d %H:%M:%S %Y", &tm);
315 if (cerr == nullptr) {
316 fatal(MYNAME ": Unable to convert date (%s)!\n", buff);
317 }
318 wpt->SetCreationTime(mkgmtime(&tm));
319
320 /* go over time stamp */
321 int i = 5;
322 while (buff && i) {
323 i--;
324 buff = strchr(buff, ' ');
325 if (buff) {
326 buff++;
327 }
328 }
329 if (gardown && (buff == nullptr)) {
330 return wpt;
331 }
332 is_fatal((buff == nullptr), MYNAME ": Incomplete waypoint line!");
333
334 while (isspace(*buff)) {
335 buff++;
336 }
337
338 parse_line(buff, WAYPT__OFS, "^", wpt);
339
340 return wpt;
341 }
342
343 static Waypoint*
parse_trkpt(char * buff)344 parse_trkpt(char* buff)
345 {
346 auto* wpt = new Waypoint;
347 garmin_fs_t* gmsd = garmin_fs_alloc(-1);
348 wpt->fs.FsChainAdd(gmsd);
349
350 parse_line(buff, TRKPT__OFS, ";", wpt);
351
352 return wpt;
353 }
354
355 /*
356 * parse_categories is currently only a dummy procedure.
357 * w'll need a central storage with binding to the module
358 * which has established a list of category names.
359 */
360
361 static void
parse_categories(char * buff)362 parse_categories(char* buff)
363 {
364 char* cin;
365 int cat = 0;
366
367 while ((cin = csv_lineparse(buff, ",", "", cat++))) {
368 uint16_t cx;
369
370 buff = nullptr;
371
372 cin = lrtrim(cin);
373 if (*cin == 0) {
374 continue;
375 }
376
377 garmin_fs_convert_category(cin, &cx);
378 }
379 }
380
381
382 /* main functions */
383
384 static void
rd_init(const QString & fname)385 rd_init(const QString& fname)
386 {
387 fin = gbfopen(fname, "rb", MYNAME);
388
389 gardown = 1;
390 mode = wptdata;
391 grid = grid_lat_lon_dmm;
392 datum = DATUM_WGS84;
393 altf = 1;
394 event_ct = 0;
395 }
396
397 static void
rd_deinit()398 rd_deinit()
399 {
400 gbfclose(fin);
401 }
402
403 static void
data_read()404 data_read()
405 {
406 char* buff;
407 int line = 0;
408 Waypoint* wpt = nullptr;
409 route_head* head = nullptr;
410
411 while ((buff = gbfgetstr(fin))) {
412 if ((line++ == 0) && fin->unicode) {
413 cet_convert_init(CET_CHARSET_UTF8, 1);
414 }
415
416 char* cin = lrtrim(buff);
417 if (!*cin) {
418 continue;
419 }
420
421 char* cdata = cin+1;
422 while (! isspace(*cdata)) {
423 cdata++;
424 }
425 while (isspace(*cdata)) {
426 cdata++;
427 }
428 if (! *cdata) {
429 continue;
430 }
431
432 switch (*cin) {
433
434 case '#': /* comment */
435 break;
436
437 case 'A':
438 if (case_ignore_strncmp(cdata, "Meter", 5) == 0) {
439 altf = 1.0;
440 } else if (case_ignore_strncmp(cdata, "Feet", 4) == 0) {
441 altf = FEET_TO_METERS(1.0);
442 }
443 break;
444
445 case 'C': /* categories */
446 parse_categories(cdata);
447 break;
448
449 case 'D':
450 datum = gt_lookup_datum_index(cdata, MYNAME);
451 break;
452
453 case 'I': /* event point */
454 wpt = new Waypoint;
455 cdata += parse_coordinates(cdata, datum, grid,
456 &wpt->latitude, &wpt->longitude, MYNAME);
457 wpt->shortname = QString("Event%1").arg(++event_ct);
458 while (isspace(*cdata)) {
459 cdata++;
460 }
461 if (*cdata == ';') {
462 cdata++;
463 wpt->icon_descr = gt_find_desc_from_icon_number(
464 atoi(cdata), PCX);
465 }
466 waypt_add(wpt);
467 break;
468
469 case 'M':
470 grid = gt_lookup_grid_type(cdata, MYNAME);
471 break;
472
473 case 'P': /* proximity waypoint */
474 case 'W': /* normal waypoint */
475 wpt = parse_waypt(cin + 3);
476 if (wpt) {
477 if (mode == rtedata) {
478 route_add_wpt(head, wpt);
479 } else {
480 waypt_add(wpt);
481 }
482 }
483 break;
484
485 case 'c': /* additional lines */
486 switch (*(cin+1)) {
487 int index;
488
489 case 'A':
490 case 'B':
491 case 'C':
492 case 'D':
493
494 index = WPT_cA_OFS + ((*(cin+1) - 'A') * 256);
495 parse_line(cdata, index, "|", wpt);
496 break;
497
498 case '1':
499 case '2':
500 case '3':
501 case '4':
502 case '5':
503 case '6':
504 case '7':
505 case '8':
506
507 index = WPT_c0_OFS + ((*(cin+1) - '0') * 256);
508 parse_line(cdata, index, ";", wpt);
509 break;
510
511 case 'L':
512 waypt_add_url(wpt, cdata, nullptr);
513 break;
514
515 default:
516 break;
517 }
518 break;
519
520 case 'N': /* track log header */
521 mode = trkdata;
522 head = new route_head;
523 cdata = strchr(cdata, '-');
524 if (cdata) {
525 while (isspace(*cdata)) {
526 cdata++;
527 }
528 if (*cdata) {
529 char* s = strrchr(cdata, ',');
530 if (s) {
531 *s = '\0';
532 s = strrchr(cdata, ',');
533 if (s) {
534 *s = '\0';
535 head->rte_name = cdata;
536 }
537 }
538 }
539 }
540 track_add_head(head);
541 break;
542
543 case 'R': /* route header */
544 mode = rtedata;
545 head = new route_head;
546 cdata += 3; /*skip route number */
547 if (*cdata) {
548 head->rte_name = cdata;
549 }
550 route_add_head(head);
551 break;
552
553 case 'T':
554 wpt = parse_trkpt(cdata);
555 if (wpt) {
556 track_add_wpt(head, wpt);
557 }
558 break;
559
560 case 'V':
561 if (strcmp(cin, G7T_HEADER) != 0) {
562 fatal(MYNAME ": Invalid version or invalid file!\n");
563 }
564 gardown = 0;
565 break;
566
567 default:
568 break;
569 }
570 }
571 }
572
573 /* --------------------------------------------------------------------------- */
574
575 ff_vecs_t g7towin_vecs = {
576 ff_type_file,
577 { ff_cap_read, ff_cap_read, ff_cap_read },
578 rd_init,
579 nullptr,
580 rd_deinit,
581 nullptr,
582 data_read,
583 nullptr,
584 nullptr,
585 &g7towin_args,
586 CET_CHARSET_MS_ANSI, 0
587 , NULL_POS_OPS,
588 nullptr
589 };
590
591 #endif /* CSVFMTS_ENABLED */
592