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