1 /*
2     Read and write GPilotS files.
3 
4     Copyright (C) 2003 Robert Lipe, robertlipe@usa.net
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 #include "defs.h"
23 #if PDBFMTS_ENABLED
24 #include "pdbfile.h"
25 #include "garmin_tables.h"
26 
27 #define MYNAME "GPilotS"
28 #define MYWPT  0x57707473  	/* Wpts */
29 #define MYTRK  0x54726b73  	/* Trks */
30 #define MYRTE  0x57707473  	/* Wpts */
31 #define MYCREATOR 0x4750696c 	/* GPil */
32 
33 
34 /*
35  * Structures grafted from http://www.cru.fr/perso/cc/GPilotS/
36  */
37 
38 
39 typedef struct {
40   long lat;			/* latitude in semicircles */
41   long lon;			/* longitude in semicircles */
42 }
43 Semicircle_Type;
44 
45 typedef struct {
46   char ident[6];			/* identifier */
47   unsigned char lat[4];		/* position */
48   unsigned char lon[4];		/* position */
49   unsigned char unused[4];	/* should be set to zero */
50   char cmnt[40];			/* comment */
51   unsigned char smbl;		/* symbol id */
52   unsigned char dspl;		/* display option */
53 } D103_Wpt_Type;
54 
55 typedef union {
56   float f;
57   unsigned int i;
58 } fi_t;
59 
60 typedef struct {		       /*                             size */
61   unsigned char wpt_class;            /* class (see below)            1 */
62   unsigned char color;                /* color (see below)            1 */
63   unsigned char dspl;                 /* display options (see below)  1 */
64   unsigned char attr;                 /* attributes (see below)       1 */
65   unsigned char smbl[2];              /* waypoint symbol              2 */
66   unsigned char subclass[18];         /* subclass                    18 */
67   unsigned char lat[4];		/* position */
68   unsigned char lon[4];		/* position */
69   float alt;			       /* altitude in meters           4 */
70   float dpth;			       /* depth in meters              4 */
71   float dist;			       /* proximity distance in meters 4 */
72   char state[2];		       /* state                        2 */
73   char cc[2];			       /* country code                 2 */
74   char varlenstrs[1];			/* start of variable length strings */
75   /* G_char ident[]; variable length string 1-51       */
76   /* G_char comment[]; waypoint user comment 1-51      */
77   /* G_char facility[]; facility name 1-31             */
78   /* G_char city[]; city name 1-25                     */
79   /* G_char addr[]; address number 1-51                */
80   /* G_char cross_road[]; intersecting road label 1-51 */
81 }
82 D108_Wpt_Type;
83 
84 typedef struct {		       /* structure de waypoint "interne" */
85   unsigned char ident[51];	       /* identifier (50 + '0') */
86   Semicircle_Type posn;	       /* position (common to all Garmin types) */
87   unsigned char cmnt[51];	       /* comment (50 + '0') */
88   float dst;			       /* proximity distance */
89   float alt;			       /* altitude */
90   int smbl;			       /* symbol id */
91   unsigned char dspl;		       /* display option */
92   unsigned char color;	       /* color */
93 }
94 Custom_Wpt_Type;
95 
96 typedef struct {		       /* internal track header */
97   char name[256];		       /* nom du groupe de trackpoints */
98   unsigned char dspl;		       /* display on the map ? */
99   unsigned char color;	       /* color */
100   unsigned char type;		       /* type of following track points */
101   unsigned char unused;              /* type of following track points */
102   unsigned char number[2];	       /* number of track points */
103   unsigned char latmin[4];	       /* latitude min */
104   unsigned char latmax[4];	       /* latitude max */
105   unsigned char lonmin[4];	       /* longitude min */
106   unsigned char lonmax[4];	       /* longitude max */
107   unsigned char unused2[2];          /* type of following track points */
108 }
109 Custom_Trk_Hdr_Type;
110 
111 typedef struct {
112   unsigned char lat[4];		/* position */
113   unsigned char lon[4];		/* position */
114   unsigned char time[4];
115   unsigned char alt[4];
116   unsigned char new_trk;
117   unsigned char unused;
118 } Custom_Trk_Point_Type;
119 
120 typedef struct {		       /* custom compact track point type */
121   unsigned char lat[4];		/* position */
122   unsigned char lon[4];		/* position */
123   unsigned char new_trk;
124   unsigned char unused;
125 } Compact_Trk_Point_Type;                /* size : 10 bytes */
126 
127 struct record {
128   struct {
129     unsigned char type;
130     unsigned short size;
131     unsigned int version;
132   } header;
133   union {
134     D103_Wpt_Type d103;
135     D108_Wpt_Type d108;
136     Custom_Wpt_Type CustWpt;
137     Custom_Trk_Hdr_Type CustTrkHdr;
138 #if LATER
139     Custom_Rte_Hdr_Type CustRteHdr;
140 #endif
141   } wpt;
142 };
143 
144 
145 static pdbfile* file_in, *file_out;
146 static const char* out_fname;
147 static int ct = 0;
148 static char* dbname = NULL;
149 
150 static
151 arglist_t my_args[] = {
152   {"dbname", &dbname, "Database name", NULL, ARGTYPE_STRING, ARG_NOMINMAX},
153   ARG_TERMINATOR
154 };
155 
156 static void
rd_init(const char * fname)157 rd_init(const char* fname)
158 {
159   file_in = pdb_open(fname, MYNAME);
160 }
161 
162 static void
rd_deinit(void)163 rd_deinit(void)
164 {
165   pdb_close(file_in);
166   if (dbname) {
167     xfree(dbname);
168     dbname = NULL;
169   }
170 }
171 
172 static void
wr_init(const char * fname)173 wr_init(const char* fname)
174 {
175   file_out = pdb_create(fname, MYNAME);
176   out_fname = fname;
177 }
178 
179 static void
wr_deinit(void)180 wr_deinit(void)
181 {
182   pdb_close(file_out);
183   if (dbname) {
184     xfree(dbname);
185     dbname = NULL;
186   }
187 }
188 
189 static void
data_read(void)190 data_read(void)
191 {
192   struct record* rec;
193   pdbrec_t* pdb_rec;
194   route_head* track_head = NULL;
195 
196   if (file_in->creator != MYCREATOR) {
197     fatal(MYNAME ": Not a %s file.\n", MYNAME);
198   }
199 
200   switch (file_in->type) {
201   case MYWPT:
202     /* blah */
203     break;
204   case MYTRK:
205     /* blah */
206     break;
207   default:
208     fatal(MYNAME ": Unknown file type 0x%x\n", (int) file_in->type);
209   }
210 
211   for (pdb_rec = file_in->rec_list; pdb_rec; pdb_rec=pdb_rec->next) {
212     waypoint* wpt_tmp;
213     Custom_Trk_Point_Type* tp_cust;
214     Compact_Trk_Point_Type* tp_comp;
215     int lat;
216     int lon;
217     int sz;
218     fi_t fi;
219     int trk_num = 0;
220     int trk_seg_num = 1;
221     char trk_seg_num_buf[10];
222     char* trk_name = "";
223 
224     wpt_tmp = waypt_new();
225 
226     rec = (struct record*) pdb_rec->data;
227     switch (rec->header.type) {
228       /*
229        * G103Type
230        */
231     case 4:
232       wpt_tmp->shortname = xstrndupt(rec->wpt.d103.ident, sizeof(rec->wpt.d103.ident));
233       wpt_tmp->description = xstrndupt(rec->wpt.d103.cmnt, sizeof(rec->wpt.d103.cmnt));
234       /* This is odd.   This is a Palm DB file,
235        * yet the data appears to be little endian,
236        * not appropriate the the actual Palm.
237        */
238       lon = le_read32(&rec->wpt.d103.lon);
239       lat = le_read32(&rec->wpt.d103.lat);
240       wpt_tmp->longitude = lon / 2147483648.0 * 180.0;
241       wpt_tmp->latitude = lat / 2147483648.0 * 180.0;
242       waypt_add(wpt_tmp);
243       break;
244       /*
245        * G108Type
246        */
247     case 9:
248       wpt_tmp->shortname = xstrndupt(rec->wpt.d108.varlenstrs, 50);
249       wpt_tmp->description = xstrndupt(rec->wpt.d108.varlenstrs + strlen(wpt_tmp->shortname) + 1, 50);
250       /* This is odd.   This is a Palm DB file,
251        * yet the data appears to be little endian,
252        * not appropriate the the actual Palm.
253        */
254       lon = le_read32(&rec->wpt.d108.lon);
255       lat = le_read32(&rec->wpt.d108.lat);
256       wpt_tmp->longitude = lon / 2147483648.0 * 180.0;
257       wpt_tmp->latitude = lat / 2147483648.0 * 180.0;
258       fi.i = le_read32(&rec->wpt.d108.alt);
259       wpt_tmp->altitude = fi.f;
260       fi.i = le_read32(&rec->wpt.d108.dpth);
261       WAYPT_SET(wpt_tmp, depth, fi.f);
262       fi.i = le_read32(&rec->wpt.d108.dist);
263       WAYPT_SET(wpt_tmp, proximity, fi.f);
264       wpt_tmp->wpt_flags.icon_descr_is_dynamic = 0;
265       wpt_tmp->icon_descr = gt_find_desc_from_icon_number((rec->wpt.d108.smbl[1] << 8) + rec->wpt.d108.smbl[0], PCX, NULL);
266       waypt_add(wpt_tmp);
267       break;
268 
269       /*
270        * CustomTrkHdr
271        */
272     case 101:
273       trk_name = rec->wpt.CustTrkHdr.name;
274       sz = be_read16(&rec->wpt.CustTrkHdr.number);
275 
276       /* switch between custom track points and compact track points.
277        * (compact points have no altitude and time info.
278        */
279       switch (rec->wpt.CustTrkHdr.type) {
280       case 102:
281         tp_cust = (Custom_Trk_Point_Type*)((char*) pdb_rec->data + sizeof(rec->header) + sizeof(rec->wpt.CustTrkHdr));
282         while (sz--) {
283           if ((int)(tp_cust->new_trk) == 1 || trk_seg_num == 1) {
284             /*
285              * Start a new track segment
286              */
287             track_head = route_head_alloc();
288             if (trk_seg_num == 1) {
289               track_head->rte_name = xstrdup(trk_name);
290             } else {
291               /* name in the form TRACKNAME #n */
292               snprintf(trk_seg_num_buf, sizeof(trk_seg_num_buf), "%d", trk_seg_num);
293               track_head->rte_name = (char*) xmalloc(strlen(trk_name)+strlen(trk_seg_num_buf)+3);
294               sprintf(track_head->rte_name, "%s #%s", trk_name, trk_seg_num_buf);
295             }
296             trk_seg_num++;
297             track_head->rte_num = trk_num;
298             trk_num++;
299             track_add_head(track_head);
300           }
301 
302           wpt_tmp = waypt_new();
303 
304           /* This is even more odd.
305            * Track data is stored as big endian while
306            * waypoint data is little endian!?
307            */
308           lon = be_read32(&tp_cust->lon);
309           lat = be_read32(&tp_cust->lat);
310           wpt_tmp->longitude = lon / 2147483648.0 * 180.0;
311           wpt_tmp->latitude = lat / 2147483648.0 * 180.0;
312           /*
313            * Convert Garmin/GPilotS time format to gpsbabel time format.
314            * Garmin/GPilotS count seconds from "UTC 12:00 AM December 31 1989".
315            * gpsbabel counts seconds from "UTC 12:00 AM January 1 1970".
316            */
317           wpt_tmp->creation_time = be_read32(&tp_cust->time) + 631065600;
318           fi.i = be_read32(&tp_cust->alt);
319           wpt_tmp->altitude = fi.f;
320           track_add_wpt(track_head, wpt_tmp);
321           tp_cust++;
322         }
323         break;
324       case 104:
325         tp_comp = (Compact_Trk_Point_Type*)((char*) pdb_rec->data + sizeof(rec->header) + sizeof(rec->wpt.CustTrkHdr));
326         while (sz--) {
327           if ((int)(tp_comp->new_trk) == 1 || trk_seg_num == 1) {
328             /*
329              * Start a new track segment
330              */
331             track_head = route_head_alloc();
332             if (trk_seg_num == 1) {
333               track_head->rte_name = xstrdup(trk_name);
334             } else {
335               /* name in the form TRACKNAME #n */
336               snprintf(trk_seg_num_buf, sizeof(trk_seg_num_buf), "%d", trk_seg_num);
337               track_head->rte_name = (char*) xmalloc(strlen(trk_name)+strlen(trk_seg_num_buf)+3);
338               sprintf(track_head->rte_name, "%s #%s", trk_name, trk_seg_num_buf);
339             }
340             trk_seg_num++;
341             track_head->rte_num = trk_num;
342             trk_num++;
343             track_add_head(track_head);
344           }
345 
346           wpt_tmp = waypt_new();
347           lon = be_read32(&tp_comp->lon);
348           lat = be_read32(&tp_comp->lat);
349           wpt_tmp->longitude = lon / 2147483648.0 * 180.0;
350           wpt_tmp->latitude = lat / 2147483648.0 * 180.0;
351           track_add_wpt(track_head, wpt_tmp);
352           tp_comp++;
353         }
354         break;
355       default:
356         fatal(MYNAME ": track point type %d not supported.\n", rec->wpt.CustTrkHdr.type);
357       }
358       break;
359     default:
360       fatal(MYNAME ": input record type %d not supported.\n", rec->header.type);
361     }
362 
363   }
364 }
365 
366 
367 struct hdr {
368   char* wpt_name;
369   waypoint* wpt;
370 };
371 
372 static void
my_write_wpt(const waypoint * wpt)373 my_write_wpt(const waypoint* wpt)
374 {
375   struct record* rec;
376   char* vdata;
377   int lat, lon;
378 
379   rec = (struct record*) xcalloc(sizeof *rec, 1);
380   vdata = (char*)rec + sizeof(*rec);
381 
382   rec->header.type = 4;
383   rec->header.size = 5;
384   rec->header.version = 6;
385 
386   strncpy(rec->wpt.d103.ident, wpt->shortname, sizeof(rec->wpt.d103.ident));
387   strncpy(rec->wpt.d103.cmnt, wpt->description, sizeof(rec->wpt.d103.cmnt));
388   lat = wpt->latitude  / 180.0 * 2147483648.0;
389   lon = wpt->longitude  / 180.0 * 2147483648.0;
390   le_write32(&rec->wpt.d103.lat, lat);
391   le_write32(&rec->wpt.d103.lon, lon);
392 
393   pdb_write_rec(file_out, 0, ct, ct+1, rec, (char*)vdata - (char*)rec);
394   ct++;
395   xfree(rec);
396 }
397 
398 static void
data_write(void)399 data_write(void)
400 {
401   if (dbname) {
402     strncpy(file_out->name, dbname, PDB_DBNAMELEN);
403   } else {
404     strncpy(file_out->name, out_fname, PDB_DBNAMELEN);
405   }
406 
407   /*
408    * Populate header.
409    */
410   file_out->name[PDB_DBNAMELEN-1] = 0;
411   file_out->attr = PDB_FLAG_BACKUP;
412   file_out->ctime = file_out->mtime = current_time() + 2082844800U;
413 
414   file_out->type = MYWPT;
415   file_out->creator = MYCREATOR;
416   file_out->version = 1;
417 
418   waypt_disp_all(my_write_wpt);
419 }
420 
421 
422 ff_vecs_t gpilots_vecs = {
423   ff_type_file,
424   { (ff_cap)(ff_cap_read | ff_cap_write), (ff_cap)(ff_cap_read | ff_cap_write), ff_cap_none},
425   rd_init,
426   wr_init,
427   rd_deinit,
428   wr_deinit,
429   data_read,
430   data_write,
431   NULL,
432   my_args,
433   CET_CHARSET_ASCII, 0	/* CET-REVIEW */
434 };
435 #endif
436