1 /*
2 
3     Support for Destinator POI's, Itineraries and Tracklogs.
4     ( as described at "http://mozoft.com/d3log.html" )
5 
6     Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org
7 
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
22 
23  */
24 
25 #include "defs.h"
26 #include "cet.h"
27 #include "cet_util.h"
28 #include "garmin_fs.h"
29 #include "strptime.h"
30 #include <ctype.h>
31 #include <time.h>
32 
33 #define MYNAME 		"destinator"
34 #define DST_DYN_POI 	"Dynamic POI"
35 #define DST_ITINERARY 	"City->Street"
36 
37 static
38 arglist_t destinator_args[] = {
39   ARG_TERMINATOR
40 };
41 
42 static gbfile* fin, *fout;
43 static gpsdata_type data_type;
44 
45 
46 /*******************************************************************************/
47 /*                                   READER                                    */
48 /*-----------------------------------------------------------------------------*/
49 
50 static garmin_fs_t*
gmsd_init(waypoint * wpt)51 gmsd_init(waypoint* wpt)
52 {
53   garmin_fs_t* gmsd = GMSD_FIND(wpt);
54   if (gmsd == NULL) {
55     gmsd = garmin_fs_alloc(-1);
56     fs_chain_add(&wpt->fs, (format_specific_data*) gmsd);
57   }
58   return gmsd;
59 }
60 
61 static char*
read_wcstr(const int discard)62 read_wcstr(const int discard)
63 {
64   gbint16* buff = NULL, c;
65   int size = 0, pos = 0;
66 
67   while (gbfread(&c, sizeof(c), 1, fin) && (c != 0)) {
68     if (size == 0) {
69       size = 16;
70       buff = (gbint16*) xmalloc(size * sizeof(*buff));
71     } else if (pos == size) {
72       size += 16;
73       buff = (gbint16*) xrealloc(buff, size * sizeof(*buff));
74     }
75     buff[pos] = c;
76     pos += 1;
77   }
78 
79   if (pos != 0) {
80     char* res;
81     if (discard) {
82       res = NULL;
83     } else {
84       res = cet_str_uni_to_utf8(buff, pos);
85       res = lrtrim(res);
86       if (*res == '\0') {
87         xfree(res);
88         res = NULL;
89       }
90     }
91     xfree(buff);
92     return res;
93   } else {
94     return NULL;
95   }
96 }
97 
98 static void
write_wcstr(const char * str)99 write_wcstr(const char* str)
100 {
101   int len;
102   short* unicode;
103 
104   unicode = cet_str_utf8_to_uni(str, &len);
105   gbfwrite((void*)unicode, 2, len + 1, fout);
106   xfree(unicode);
107 }
108 
109 static int
read_until_wcstr(const char * str)110 read_until_wcstr(const char* str)
111 {
112   char* buff;
113   int len, sz;
114   int eos = 0, res = 0;
115 
116   len = strlen(str);
117   sz = (len + 1) * 2;
118   buff = (char*) xcalloc(sz, 1);
119 
120   while (! gbfeof(fin)) {
121 
122     char c = gbfgetc(fin);
123     memmove(buff, buff + 1, sz - 1);
124     buff[sz - 1] = c;
125 
126     if (c == 0) {
127       eos++;
128       if (eos >= 2) {	/* two or more zero bytes => end of string */
129         char* test = cet_str_uni_to_utf8((short*)buff, len);
130         if (test) {
131           res = (strcmp(str, test) == 0);
132           xfree(test);
133           if (res) {
134             break;
135           }
136         }
137       }
138     } else {
139       eos = 0;
140     }
141   }
142   xfree(buff);
143   return res;
144 }
145 
146 static void
destinator_read_poi(void)147 destinator_read_poi(void)
148 {
149   waypoint* wpt;
150   int count = 0;
151 
152   gbfrewind(fin);
153 
154   while (!(gbfeof(fin))) {
155     char* str, *hnum;
156     double ll;
157     garmin_fs_t* gmsd;
158 
159     if (count == 0) {
160       str = read_wcstr(0);
161       if ((str == NULL) || (strcmp(str, DST_DYN_POI) != 0)) {
162         fatal(MYNAME "_poi: Invalid record header!\n");
163       }
164       xfree(str);
165     } else if (! read_until_wcstr(DST_DYN_POI)) {
166       break;
167     }
168 
169     count++;
170 
171     wpt = waypt_new();
172 
173     wpt->shortname = read_wcstr(0);
174     wpt->notes = read_wcstr(0);		/* comment */
175 
176     hnum = read_wcstr(0);			/* house number */
177 
178     str = read_wcstr(0); 			/* street */
179     if (!str) {
180       str = hnum;
181       hnum = NULL;
182     }
183     if (str) {
184       gmsd = gmsd_init(wpt);
185       if (hnum) {
186         str = xstrappend(str, " ");
187         str = xstrappend(str, hnum);
188       }
189       GMSD_SET(addr, str);
190     }
191 
192     if ((str = read_wcstr(0))) {		/* city */
193       gmsd = gmsd_init(wpt);
194       GMSD_SET(city, str);
195     }
196 
197     if (hnum) {
198       xfree(hnum);
199     }
200 
201     (void) read_wcstr(1);			/* unknown */
202 
203     if ((str = read_wcstr(0))) {		/* postcode */
204       gmsd = gmsd_init(wpt);
205       GMSD_SET(postal_code, str);
206     }
207 
208     (void) read_wcstr(1);			/* unknown */
209 
210     (void) gbfgetdbl(fin);
211 
212     wpt->longitude = gbfgetdbl(fin);
213     wpt->latitude = gbfgetdbl(fin);
214     ll = gbfgetdbl(fin);
215     if (ll != wpt->longitude) {
216       fatal(MYNAME "_poi: Invalid file!\n");
217     }
218     ll = gbfgetdbl(fin);
219     if (ll != wpt->latitude) {
220       fatal(MYNAME "_poi: Invalid file!\n");
221     }
222 
223     waypt_add(wpt);
224   }
225 }
226 
227 static void
destinator_read_rte(void)228 destinator_read_rte(void)
229 {
230   int count = 0;
231   route_head* rte = NULL;
232 
233   gbfrewind(fin);
234 
235   while (!(gbfeof(fin))) {
236     char* str;
237     waypoint* wpt;
238 
239     if (count == 0) {
240       str = read_wcstr(0);
241       if ((str == NULL) || (strcmp(str, DST_ITINERARY) != 0)) {
242         fatal(MYNAME "_itn: Invalid record header!\n");
243       }
244       xfree(str);
245     } else if (! read_until_wcstr(DST_ITINERARY)) {
246       break;
247     }
248 
249     count++;
250 
251     wpt = waypt_new();
252 
253     wpt->shortname = read_wcstr(0);
254     wpt->notes = read_wcstr(0);
255 
256     (void) gbfgetint32(fin);
257     (void) gbfgetdbl(fin);
258     (void) gbfgetdbl(fin);
259 
260     wpt->longitude = gbfgetdbl(fin);
261     wpt->latitude = gbfgetdbl(fin);
262     if (gbfgetdbl(fin) != wpt->longitude) {
263       fatal(MYNAME "_itn: Invalid file!\n");
264     }
265     if (gbfgetdbl(fin) != wpt->latitude) {
266       fatal(MYNAME "_itn: Invalid file!\n");
267     }
268 
269     if (! rte) {
270       rte = route_head_alloc();
271       route_add_head(rte);
272     }
273     route_add_wpt(rte, wpt);
274 
275     (void) gbfgetdbl(fin);
276     (void) gbfgetdbl(fin);
277   }
278 }
279 
280 static void
destinator_read_trk(void)281 destinator_read_trk(void)
282 {
283   char TXT[4] = "TXT";
284   int recno = -1;
285   route_head* trk = NULL;
286 
287   gbfrewind(fin);
288 
289   while (!(gbfeof(fin))) {
290     waypoint* wpt;
291     struct tm tm;
292     char buff[20];
293     int date;
294     double time;
295 
296     recno++;
297 
298     if (gbfeof(fin)) {
299       break;
300     }
301 
302     wpt = waypt_new();
303 
304     wpt->longitude = gbfgetdbl(fin);
305     wpt->latitude = gbfgetdbl(fin);
306     wpt->altitude = gbfgetdbl(fin);
307 
308     (void) gbfgetdbl(fin);				/* unknown */
309     (void) gbfgetdbl(fin);				/* unknown */
310     (void) gbfgetdbl(fin);				/* unknown */
311 
312     wpt->fix = (fix_type) gbfgetint32(fin);
313     wpt->sat = gbfgetint32(fin);
314 
315     gbfseek(fin, 12 * sizeof(gbint32), SEEK_CUR);	/* SAT info */
316 
317     date = gbfgetint32(fin);
318     time = gbfgetflt(fin);
319 
320     gbfseek(fin, 2 * 12, SEEK_CUR);			/* SAT info */
321 
322     gbfread(TXT, 1, 3, fin);
323     if (strcmp(TXT, "TXT") != 0) {
324       fatal(MYNAME "_trk: No (or unknown) file!\n");
325     }
326 
327     gbfseek(fin, 13, SEEK_CUR);			/* unknown */
328 
329     memset(&tm, 0, sizeof(tm));
330 
331     snprintf(buff, sizeof(buff), "%06d%.f", date, time);
332     strptime(buff, "%d%m%y%H%M%S", &tm);
333     wpt->creation_time = mkgmtime(&tm);
334     wpt->microseconds = ((int)time % 1000) * 1000;
335 
336     if (wpt->fix > 0) {
337       wpt->fix = (fix_type)(wpt->fix + 1);
338     }
339 
340     if (! trk) {
341       trk = route_head_alloc();
342       track_add_head(trk);
343     }
344     track_add_wpt(trk, wpt);
345   }
346 }
347 
348 static void
destinator_read(void)349 destinator_read(void)
350 {
351   int i0, i1;
352   double d0, d1;
353   char buff[16];
354 
355   if (! gbfread(buff, 1, sizeof(buff), fin)) {
356     fatal(MYNAME ": Unexpected EOF (end of file)!\n");
357   }
358 
359   i0 = le_read32(&buff[0]);
360   i1 = le_read32(&buff[4]);
361 
362   if ((i0 == 0x690043) && (i1 == 0x790074)) {
363     if (data_type != rtedata) {
364       warning(MYNAME ": Using Destinator Itinerary Format!\n");
365     }
366     destinator_read_rte();
367   } else if ((i0 == 0x790044) && (i1 == 0x61006e)) {
368     if (data_type != wptdata) {
369       warning(MYNAME ": Using Destinator POI Format!\n");
370     }
371     destinator_read_poi();
372   } else {
373     if (data_type != trkdata) {
374       warning(MYNAME ": Using Destinator Tracklog Format!\n");
375     }
376 
377     le_read64(&d0, &buff[0]);
378     le_read64(&d1, &buff[8]);
379     if ((fabs(d0) > 180) || (fabs(d1) > 90)) {
380       fatal(MYNAME ": No Destinator (.dat) file!\n");
381     }
382     destinator_read_trk();
383   }
384 }
385 
386 /*******************************************************************************/
387 /*                                   WRITER                                    */
388 /*-----------------------------------------------------------------------------*/
389 
390 static void
destinator_wpt_disp(const waypoint * wpt)391 destinator_wpt_disp(const waypoint* wpt)
392 {
393   garmin_fs_t* gmsd = GMSD_FIND(wpt);
394 
395   write_wcstr(DST_DYN_POI);
396   write_wcstr((wpt->shortname) ? wpt->shortname : "WPT");
397   write_wcstr((wpt->notes) ? wpt->notes : wpt->description);
398 
399   write_wcstr(NULL);				/* house number */
400   write_wcstr(GMSD_GET(addr, NULL));		/* street */
401   write_wcstr(GMSD_GET(city, NULL));		/* city */
402   write_wcstr(NULL);				/* unknown */
403   write_wcstr(GMSD_GET(postal_code, NULL));	/* postcode */
404   write_wcstr(NULL);				/* unknown */
405 
406   gbfputint32(0, fout);
407   gbfputint32(0, fout);
408 
409   gbfputdbl(wpt->longitude, fout);
410   gbfputdbl(wpt->latitude, fout);
411   gbfputdbl(wpt->longitude, fout);
412   gbfputdbl(wpt->latitude, fout);
413 
414   gbfputdbl(0, fout);
415   gbfputdbl(0, fout);
416 }
417 
418 static void
destinator_trkpt_disp(const waypoint * wpt)419 destinator_trkpt_disp(const waypoint* wpt)
420 {
421   int i;
422 
423   gbfputdbl(wpt->longitude, fout);
424   gbfputdbl(wpt->latitude, fout);
425   gbfputdbl(wpt->altitude, fout);
426   gbfputdbl(0, fout);
427   gbfputdbl(0, fout);
428   gbfputdbl(0, fout);
429   gbfputint32(wpt->fix > fix_unknown ? wpt->fix - 1 : 0, fout);
430   gbfputint32(wpt->sat, fout);
431   for (i = 0; i < 12; i++) {
432     gbfputint32(0, fout);
433   }
434 
435   if (wpt->creation_time) {
436     struct tm tm;
437     double time;
438     int date;
439 
440     tm = *gmtime(&wpt->creation_time);
441     tm.tm_mon += 1;
442     tm.tm_year -= 100;
443     date = ((int)tm.tm_mday * 10000) + ((int)tm.tm_mon * 100) + tm.tm_year;
444     gbfputint32(date, fout);
445 
446     time = ((int)tm.tm_hour * 10000) + ((int)tm.tm_min * 100) + tm.tm_sec;
447     time = (time * 1000) + (wpt->microseconds / 1000);
448     gbfputflt(time, fout);
449   } else {
450     gbfputint32(0, fout);	/* Is this invalid ? */
451     gbfputflt(0, fout);
452   }
453 
454   for (i = 0; i < 12; i++) {
455     gbfputint16(0, fout);
456   }
457   gbfputcstr("TXT", fout);
458   for (i = 0; i < 12; i++) {
459     gbfputc(0, fout);
460   }
461 }
462 
463 static void
destinator_rtept_disp(const waypoint * wpt)464 destinator_rtept_disp(const waypoint* wpt)
465 {
466   write_wcstr(DST_ITINERARY);
467   write_wcstr((wpt->shortname) ? wpt->shortname : "RTEPT");
468   write_wcstr((wpt->notes) ? wpt->notes : wpt->description);
469 
470   gbfputint32(0, fout);
471   gbfputdbl(0, fout);
472   gbfputdbl(0, fout);
473 
474   gbfputdbl(wpt->longitude, fout);
475   gbfputdbl(wpt->latitude, fout);
476   gbfputdbl(wpt->longitude, fout);
477   gbfputdbl(wpt->latitude, fout);
478 
479   gbfputdbl(0, fout);
480   gbfputdbl(0, fout);
481 }
482 
483 /*******************************************************************************
484 * %%%        global callbacks called by gpsbabel main process              %%% *
485 *******************************************************************************/
486 
487 static void
destinator_rd_init(const char * fname)488 destinator_rd_init(const char* fname)
489 {
490   fin = gbfopen_le(fname, "rb", MYNAME);
491 }
492 
493 static void
destinator_rd_deinit(void)494 destinator_rd_deinit(void)
495 {
496   gbfclose(fin);
497 }
498 
499 static void
destinator_read_poi_wrapper(void)500 destinator_read_poi_wrapper(void)
501 {
502   data_type = wptdata;
503   destinator_read();
504 }
505 
506 static void
destinator_read_rte_wrapper(void)507 destinator_read_rte_wrapper(void)
508 {
509   data_type = rtedata;
510   destinator_read();
511 }
512 
513 static void
destinator_read_trk_wrapper(void)514 destinator_read_trk_wrapper(void)
515 {
516   data_type = trkdata;
517   destinator_read();
518 }
519 
520 static void
destinator_wr_init(const char * fname)521 destinator_wr_init(const char* fname)
522 {
523   fout = gbfopen_le(fname, "wb", MYNAME);
524 }
525 
526 static void
destinator_wr_deinit(void)527 destinator_wr_deinit(void)
528 {
529   gbfclose(fout);
530 }
531 
532 static void
destinator_write_poi(void)533 destinator_write_poi(void)
534 {
535   waypt_disp_all(destinator_wpt_disp);
536 }
537 
538 static void
destinator_write_rte(void)539 destinator_write_rte(void)
540 {
541   route_disp_all(NULL, NULL, destinator_rtept_disp);
542 }
543 
544 static void
destinator_write_trk(void)545 destinator_write_trk(void)
546 {
547   track_disp_all(NULL, NULL, destinator_trkpt_disp);
548 }
549 
550 /**************************************************************************/
551 
552 ff_vecs_t destinator_poi_vecs = {
553   ff_type_file,
554   {
555     (ff_cap)(ff_cap_read | ff_cap_write)	/* waypoints */,
556     ff_cap_none		 	/* tracks */,
557     ff_cap_none 			/* routes */
558   },
559   destinator_rd_init,
560   destinator_wr_init,
561   destinator_rd_deinit,
562   destinator_wr_deinit,
563   destinator_read_poi_wrapper,
564   destinator_write_poi,
565   NULL,
566   destinator_args,
567   CET_CHARSET_UTF8, 1			/* fixed */
568 };
569 
570 ff_vecs_t destinator_itn_vecs = {
571   ff_type_file,
572   {
573     ff_cap_none 			/* waypoints */,
574     ff_cap_none		 	/* tracks */,
575     (ff_cap)(ff_cap_read | ff_cap_write)	/* routes */
576   },
577   destinator_rd_init,
578   destinator_wr_init,
579   destinator_rd_deinit,
580   destinator_wr_deinit,
581   destinator_read_rte_wrapper,
582   destinator_write_rte,
583   NULL,
584   destinator_args,
585   CET_CHARSET_UTF8, 1			/* fixed */
586 };
587 
588 ff_vecs_t destinator_trl_vecs = {
589   ff_type_file,
590   {
591     ff_cap_none 			/* waypoints */,
592     (ff_cap)(ff_cap_read | ff_cap_write)	/* tracks */,
593     ff_cap_none 			/* routes */
594   },
595   destinator_rd_init,
596   destinator_wr_init,
597   destinator_rd_deinit,
598   destinator_wr_deinit,
599   destinator_read_trk_wrapper,
600   destinator_write_trk,
601   NULL,
602   destinator_args,
603   CET_CHARSET_UTF8, 1			/* fixed */
604 };
605 
606 /**************************************************************************/
607