1 /*
2 	DeLorme PN-20/40 USB "DeLBin" protocol
3 
4     Copyright (C) 2009 Paul Cornett, pc-gpsb at bullseye.com
5     Copyright (C) 2005, 2009  Robert Lipe, robertlipe@gpsbabel.org
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
20 
21  */
22 
23 #include "defs.h"
24 #include <assert.h>
25 
26 #define MYNAME "delbin"
27 static short_handle mkshort_handle;
28 
29 /*
30 Device documentation:
31 "DeLorme Binary GPS Format", delbin_user_interface_format_176.pdf
32 obtained here: http://forum.delorme.com/viewtopic.php?t=13846
33 
34 Notes:
35 Initial development was done with a PN-40, firmware 2.4.123299. The test
36 device was upgraded to firmware 2.5.165506 during development.
37 
38 The "data size" in the message header includes the 4 trailer bytes, so it
39 is really the size of the whole message minus the header.
40 
41 Messages do not always start at the beginning of a packet. Every once in a
42 while, the start of the next message directly follows the end of the previous
43 one, in the same packet.
44 
45 The time before an unacknowledged message will be retransmitted by the
46 device is on the order of 2 to 4 seconds.
47 
48 Retrieving all tracks at once (using code 0 in message 0xb031) does not
49 seem to work, it hangs after the first track, maybe waiting for some
50 undocumented response message.
51 
52 Character encoding is not documented, appears to be 8859-1.
53 
54 The undocumented messages 0xaa01, 0xb015, 0xb016 and the use of the
55 "reserved" byte in message 0xb012 were discovered by examining the data
56 transferred between the device and DeLorme Topo 8.0. They may have been
57 added in the PN-40 2.5 firmware.
58 */
59 
60 //-----------------------------------------------------------------------------
61 // interface to platform-specific device I/O
62 typedef struct {
63   void (*init)(const char* name);
64   void (*deinit)(void);
65   unsigned(*packet_read)(void*);
66   unsigned(*packet_write)(const void*, unsigned);
67 } delbin_os_ops_t;
68 
69 // really static, only extern so it can be forward declared
70 extern delbin_os_ops_t delbin_os_ops;
71 
72 static unsigned delbin_os_packet_size;
73 //-----------------------------------------------------------------------------
74 
75 // number of times to attempt a transfer before giving up
76 #define ATTEMPT_MAX 2
77 // seconds to wait for expected message (actual time will be somewhat
78 // indeterminate, but at least READ_TIMEOUT - 1)
79 #define READ_TIMEOUT 6
80 
81 // debug output: low, medium, high, higher
82 #define DBGLVL_L 1
83 #define DBGLVL_M 2
84 #define DBGLVL_H 3
85 #define DBGLVL_H2 4
86 
87 // Multiple unit support.
88 #define DELBIN_MAX_UNITS 32
89 static struct {
90   unsigned int unit_number;
91   const char* unit_serial_number;
92   const char* unit_name;
93 } delbin_unit_info[DELBIN_MAX_UNITS];
94 static int n_delbin_units;
95 
96 #define UNKNOWN_ELEV -2000000
97 
98 #define sizeofarray(x) (sizeof(x) / sizeof(x[0]))
99 
100 static char* opt_getposn = NULL;
101 static char* opt_logs = NULL;
102 static char* opt_long_notes = NULL;
103 static char* opt_nuke_wpt = NULL;
104 static char* opt_nuke_trk = NULL;
105 static char* opt_nuke_rte = NULL;
106 /* If true, Order hint to match Cache Register and Topo 7 */
107 static char* opt_hint_at_end = NULL;
108 static char* opt_gcsym = NULL;
109 
110 
111 static arglist_t delbin_args[] = {
112   {
113     "get_posn", &opt_getposn, "Return current position as a waypoint",
114     NULL, ARGTYPE_BOOL, ARG_NOMINMAX
115   },
116   {
117     "logs", &opt_logs, "Include groundspeak logs when writing",
118     NULL, ARGTYPE_BOOL, ARG_NOMINMAX
119   },
120   {
121     "long_notes", &opt_long_notes, "Use long waypoint notes regardless of PN version",
122     NULL, ARGTYPE_BOOL, ARG_NOMINMAX
123   },
124   {
125     "nukewpt", &opt_nuke_wpt, "Delete all waypoints before sending", NULL, ARGTYPE_BOOL,
126     ARG_NOMINMAX
127   },
128   {
129     "nuketrk", &opt_nuke_trk, "Delete all tracks before sending", NULL, ARGTYPE_BOOL,
130     ARG_NOMINMAX
131   },
132   {
133     "nukerte", &opt_nuke_rte, "Delete all routes before sending", NULL, ARGTYPE_BOOL,
134     ARG_NOMINMAX
135   },
136   {"hint_at_end", &opt_hint_at_end, "If true, geocache hint at end of text", NULL, ARGTYPE_BOOL, ARG_NOMINMAX },
137   {"gcsym", &opt_gcsym, "If set to 0, prefer user-provided symbols over Groundspeaks ones for geocaches", NULL, ARGTYPE_BOOL, ARG_NOMINMAX, "1" },
138   ARG_TERMINATOR
139 };
140 
141 // Whether device understands message 0xb016
142 static int use_extended_notes;
143 
144 // Device capabilities
145 static unsigned device_max_waypoint;
146 
147 static const char* waypoint_symbol(unsigned index);
148 static unsigned waypoint_symbol_index(const char* name);
149 static int track_color(unsigned index);
150 static unsigned track_color_index(int bgr);
151 
152 static unsigned waypoint_i;
153 static unsigned waypoint_n;
154 static waypoint** wp_array;
155 
156 //-----------------------------------------------------------------------------
157 // Message ids and sizes. Only the needed ones are here.
158 // Note that "in" and "out" ids are named as in the device documentation,
159 // so "in" means to the device, "out" means from.
160 #define MSG_ACK 0xaa00
161 #define MSG_BREAK 0xaa02
162 #define MSG_BREAK_SIZE 33
163 #define MSG_CAPABILITIES 0xb001
164 #define MSG_DELETE 0xb005
165 #define MSG_DELETE_SIZE 67
166 #define MSG_ERROR 0xa003
167 #define MSG_NAVIGATION 0xa010
168 #define MSG_REQUEST_ROUTES 0xb051
169 #define MSG_REQUEST_ROUTES_SIZE 65
170 #define MSG_REQUEST_TRACKS 0xb031
171 #define MSG_REQUEST_TRACKS_SIZE 33
172 #define MSG_REQUEST_WAYPOINTS 0xb012
173 #define MSG_REQUEST_WAYPOINTS_SIZE 15
174 #define MSG_ROUTE_COUNT 0xb050
175 #define MSG_ROUTE_HEADER_IN 0xb055
176 #define MSG_ROUTE_HEADER_OUT 0xb052
177 #define MSG_ROUTE_POINT_IN 0xb056
178 #define MSG_ROUTE_POINT_OUT 0xb053
179 #define MSG_ROUTE_SHAPE_IN 0xb057
180 #define MSG_ROUTE_SHAPE_OUT 0xb054
181 #define MSG_SATELLITE_INFO 0xa020
182 #define MSG_TRACK_COUNT 0xb030
183 #define MSG_TRACK_HEADER_IN 0xb035
184 #define MSG_TRACK_HEADER_OUT 0xb032
185 #define MSG_TRACK_POINT_IN 0xb036
186 #define MSG_TRACK_POINT_OUT 0xb033
187 #define MSG_TRANSFER_COMPLETE 0xaa04
188 #define MSG_VERSION 0xa001
189 #define MSG_WAYPOINT_COUNT 0xb010
190 #define MSG_WAYPOINT_IN 0xb014
191 #define MSG_WAYPOINT_OUT 0xb013
192 // Undocumented:
193 // This one looks like MSG_ACK, except it also has a string in it that says
194 // something like "device is busy". The expected MSG_ACK usually immediately
195 // follows it, so the point of this one is unclear.
196 #define MSG_NACK 0xaa01
197 // Long waypoint notes
198 #define MSG_WAYPOINT_NOTE_IN 0xb016
199 #define MSG_WAYPOINT_NOTE_OUT 0xb015
200 
201 //-----------------------------------------------------------------------------
202 // Message structures
203 
204 // Input Delete Message
205 // Message ID: 0xB005
206 typedef enum {
207   nuke_type_wpt = 0,
208   nuke_type_trk = 1,
209   nuke_type_rte = 2,
210   // int nuke_map = 3;
211 } nuke_type;
212 
213 typedef enum {
214   nuke_mode_all = 0,
215   nuke_mode_single = 1
216 } nuke_mode;
217 
218 typedef enum {
219   nuke_dest_internal = 0,
220   nuke_dest_sd = 1
221 } nuke_dest;
222 
223 typedef struct {
224   gbuint8 type;
225   gbuint8 mode;
226   gbuint8 location;
227   char object_name[64];
228 } msg_delete_t;
229 
230 // Output Waypoint Message
231 // Message ID: 0xB013
232 // Input Waypoint Message
233 // Message ID: 0xB014
234 typedef struct {
235   gbuint8 total[4]; // U32
236   gbuint8 index[4]; // U32
237   gbuint8 year;
238   gbuint8 month;
239   gbuint8 day;
240   gbuint8 hour;
241   gbuint8 minute;
242   gbuint8 second;
243   gbuint8 latitude[4]; // S32 rad * 100000000
244   gbuint8 longitude[4]; // S32 rad * 100000000
245   gbuint8 elevation[4]; // F32 meters
246   gbuint8 color;
247   gbuint8 symbol;
248   gbuint8 name_size;
249   char name[1];
250   // note_size[2] U16
251   // note[note_size]
252 } msg_waypoint_t;
253 
254 // undocumented, seen with PN-40 2.5 firmware
255 // output waypoint note
256 // Message ID: 0xB015
257 // input waypoint note
258 // Message ID: 0xB016
259 typedef struct {
260   gbuint8 index[2];
261   gbuint8 total[2];
262   gbuint8 name_size;
263   char name[1];
264   // note_size[2]
265   // note[note_size]
266 } msg_waypoint_note_t;
267 
268 // Output Track Point Message
269 // Message ID: 0xB033
270 // Input Track Point Message
271 // Message ID: 0xB036
272 typedef struct {
273   gbuint8 total[4]; // U32
274   gbuint8 index[4]; // U32
275   gbuint8 number;
276   struct {
277     gbuint8 year;
278     gbuint8 month;
279     gbuint8 day;
280     gbuint8 hour;
281     gbuint8 minute;
282     gbuint8 second;
283     gbuint8 latitude[4]; // S32 rad * 100000000
284     gbuint8 longitude[4]; // S32 rad * 100000000
285     gbuint8 elevation[4]; // F32 meters
286     gbuint8 speed[2]; // U16 km/h * 10
287     gbuint8 heading[2]; // U16 deg * 100
288     gbuint8 status;
289   } point[1];
290 } msg_track_point_t;
291 
292 // Output Track Header (Name) Message
293 // Message ID: 0xB032
294 typedef struct {
295   gbuint8 total_tracks[2]; // U16
296   gbuint8 number[2]; // U16
297   char name[32];
298   gbuint8 total_points[4]; // U32
299   gbuint8 year;
300   gbuint8 month;
301   gbuint8 day;
302   gbuint8 hour;
303   gbuint8 minute;
304   gbuint8 second;
305   gbuint8 color[2]; // U16
306   gbuint8 distance[4]; // U32 m
307   gbuint8 duration[4]; // U32 sec
308   gbuint8 comment_size[2]; // U16
309   char comment[1];
310 } msg_track_header_t;
311 
312 // Input Upload Track Header Message
313 // Message ID: 0xB035
314 typedef struct {
315   char name[32];
316   gbuint8 total_points[4]; // U32
317   gbuint8 year;
318   gbuint8 month;
319   gbuint8 day;
320   gbuint8 hour;
321   gbuint8 minute;
322   gbuint8 second;
323   gbuint8 color[2]; // U16
324   gbuint8 comment_size[2]; // U16
325   char comment[1];
326 } msg_track_header_in_t;
327 
328 // Output Route Shape Message
329 // Message ID: 0xB054
330 typedef struct {
331   gbuint8 total[4]; // U32
332   gbuint8 index[4]; // U32
333   gbuint8 number;
334   gbuint8 reserved;
335   struct {
336     gbuint8 latitude[4]; // S32 rad * 100000000
337     gbuint8 longitude[4]; // S32 rad * 100000000
338   } point[1];
339 } msg_route_shape_t;
340 
341 // Output Route Point Message
342 // Message ID: 0xB053
343 // Input Route Itin Point Message
344 // Message ID: 0xB056
345 typedef struct {
346   gbuint8 total[4]; // U32
347   gbuint8 index[4]; // U32
348   char name[32];
349   gbuint8 latitude[4]; // S32 rad * 100000000
350   gbuint8 longitude[4]; // S32 rad * 100000000
351   gbuint8 time_from_start[4]; // U32 sec
352   gbuint8 distance_from_start[4]; // F32 km
353   gbuint8 bearing_in[2]; // U16 deg * 100
354   gbuint8 bearing_out[2]; // U16 deg * 100
355   gbuint8 bearing_next[2]; // U16 deg * 100
356   gbuint8 itinerary_type;
357   gbuint8 turn_type;
358   gbuint8 road_class[2]; // U16
359   gbuint8 feature_code[4]; // U32
360   gbuint8 exit_label_size;
361   char exit_label[1];
362   // comment_size U8
363   // comment[comment_size]
364   // shape_pt_count U32
365 } msg_route_point_t;
366 
367 // Output Route Header (Name) Message
368 // Message ID: 0xB052
369 typedef struct {
370   gbuint8 total[2]; // U16
371   gbuint8 index[2]; // U16
372   char name[64];
373   gbuint8 type;
374   gbuint8 total_route_point[4]; // U32
375   gbuint8 total_shape_point[4]; // U32
376 } msg_route_header_t;
377 
378 // Input Upload Route Header Message
379 // Message ID: 0xB055
380 typedef struct {
381   char name[64];
382   gbuint8 type;
383   gbuint8 total_route_point[4]; // U32
384   gbuint8 total_shape_point[4]; // U32
385 } msg_route_header_in_t;
386 
387 // Output Navigation Message
388 // Message ID: 0xA010
389 typedef struct {
390   gbuint8 gps_week[2]; // U16
391   gbuint8 time_of_week[8]; // D64 sec
392   gbuint8 year[2]; // U16
393   gbuint8 month;
394   gbuint8 day;
395   gbuint8 hour;
396   gbuint8 minute;
397   gbuint8 second;
398   gbuint8 satellites;
399   gbuint8 latitude[8]; // D64 deg
400   gbuint8 longitude[8]; // D64 deg
401   gbuint8 elevation[8]; // D64 meters
402   gbuint8 geoid_offset[2]; // S16 meters * 10
403   gbuint8 speed[4]; // F32 km/h
404   gbuint8 heading[2]; // U16 deg * 100
405   gbuint8 magnetic_variation[2]; // S16 deg * 100
406   gbuint8 fix_status;
407 } msg_navigation_t;
408 
409 // Output Satellite Info Message
410 // Message ID: 0xA020
411 typedef struct {
412   gbuint8 gps_week[2]; // U16
413   gbuint8 time_of_week[8]; // D64 sec
414   gbuint8 hdop[2]; // U16
415   gbuint8 vdop[2]; // U16
416   gbuint8 pdop[2]; // U16
417   gbuint8 number;
418   struct {
419     gbuint8 prn;
420     gbuint8 azimuth[2]; // S16 deg? * 100
421     gbuint8 elevation[2]; // S16 deg? * 100
422     gbuint8 Cn0[2]; // U16 snr * 100
423     gbuint8 status;
424   } sat[1];
425 } msg_satellite_t;
426 
427 // Output Version Message
428 // Message ID: 0xA001
429 typedef struct {
430   gbuint8 firmware_version[4];
431   char company[32];
432   char product[32];
433   char firmware[32];
434   char gps_firmware[48];
435   char serial[16];
436   char extra[16];
437 } msg_version_t;
438 
439 // Output Device Capabilities Message
440 // Message ID: 0xB001
441 typedef struct {
442   gbuint8 max_waypoints[4]; // U32
443   gbuint8 max_tracks[2]; // U16
444   gbuint8 max_track_points[4]; // U32
445   gbuint8 max_routes[2]; // U16
446   gbuint8 max_route_points[4]; // U32
447   gbuint8 max_route_shape_points[4]; // U32
448   gbuint8 max_maps[2]; // U16
449   gbuint8 min_map_version[2]; // U16
450   gbuint8 max_map_version[2]; // U16
451   gbuint8 total_internal_file_memory[4]; // U32
452   gbuint8 avail_internal_file_memory[4]; // U32
453   gbuint8 total_external_file_memory[4]; // U32
454   gbuint8 avail_external_file_memory[4]; // U32
455 } msg_capabilities_t;
456 
457 //-----------------------------------------------------------------------------
458 
459 #if __APPLE__ || __linux
460 #include <sys/time.h>
461 #endif
462 
463 static void
debug_out(const char * fmt,...)464 debug_out(const char* fmt, ...)
465 {
466   va_list ap;
467   va_start(ap, fmt);
468   fputs(MYNAME ": ", stderr);
469   vfprintf(stderr, fmt, ap);
470   va_end(ap);
471 }
472 
473 static void
debug_out_time(const char * s)474 debug_out_time(const char* s)
475 {
476 #if __APPLE__ || __linux
477   struct timeval tv;
478   gettimeofday(&tv, NULL);
479   debug_out("%u.%03u %s", (unsigned)tv.tv_sec & 0xf, (unsigned)tv.tv_usec / 1000, s);
480 #else
481   debug_out("%u %s", (unsigned)time(NULL) & 0xf, s);
482 #endif
483 }
484 
485 //-----------------------------------------------------------------------------
486 
487 static gbuint16
checksum(const gbuint8 * p,unsigned n)488 checksum(const gbuint8* p, unsigned n)
489 {
490   int x = 0;
491   unsigned i;
492   for (i = n / 2; i > 0; i--) {
493     x += *p++;
494     x += *p++ << 8;
495   }
496   if (n & 1) {
497     x += *p;
498   }
499   return (gbuint16)-x;
500 }
501 
502 //-----------------------------------------------------------------------------
503 // OS packet read/write wrappers
504 
505 static unsigned
packet_read(void * buf)506 packet_read(void* buf)
507 {
508   unsigned n = delbin_os_ops.packet_read(buf);
509   if (n == 0) {
510     fatal(MYNAME ": read 0\n");
511   }
512   if (global_opts.debug_level >= DBGLVL_H) {
513     unsigned j;
514     const gbuint8* p = (const gbuint8*) buf;
515 
516     debug_out_time("pcktrd");
517     for (j = 0; j < n; j++) {
518       warning(" %02x", p[j]);
519     }
520     if (global_opts.debug_level >= DBGLVL_H2) {
521       warning("  ");
522       for (j = 0; j < n; j++) {
523         int c = p[j];
524         warning("%c", isprint(c) ? c : '.');
525       }
526     }
527     warning("\n");
528   }
529   return n;
530 }
531 
532 static void
packet_write(const void * buf,unsigned size)533 packet_write(const void* buf, unsigned size)
534 {
535   unsigned n;
536   if (global_opts.debug_level >= DBGLVL_H) {
537     unsigned j;
538     const gbuint8* p = (const gbuint8*) buf;
539 
540     debug_out_time("pcktwr");
541     for (j = 0; j < size; j++) {
542       warning(" %02x", p[j]);
543     }
544     if (global_opts.debug_level >= DBGLVL_H2) {
545       warning("  ");
546       for (j = 0; j < size; j++) {
547         int c = p[j];
548         warning("%c", isprint(c) ? c : '.');
549       }
550     }
551     warning("\n");
552   }
553   n = delbin_os_ops.packet_write(buf, size);
554   if (n != size) {
555     fatal(MYNAME ": short write %u %u\n", size, n);
556   }
557 }
558 
559 //-----------------------------------------------------------------------------
560 
561 // dynamically sized buffer with space reserved for message header and trailer
562 typedef struct {
563   // message data size
564   unsigned size;
565   // buffer size
566   unsigned capacity;
567   gbuint8* buf;
568   // convenience pointer to message data area
569   void* data;
570 } message_t;
571 
572 static void
message_init(message_t * m)573 message_init(message_t* m)
574 {
575   m->capacity = 100;
576   m->buf = (gbuint8*)xmalloc(m->capacity);
577   m->data = m->buf + 2 + 8;
578 }
579 
580 static void
message_init_size(message_t * m,unsigned size)581 message_init_size(message_t* m, unsigned size)
582 {
583   m->size = size;
584   m->capacity = 2 + 8 + size + 4;
585   m->buf = (gbuint8*)xmalloc(m->capacity);
586   m->data = m->buf + 2 + 8;
587 }
588 
589 static void
message_free(message_t * m)590 message_free(message_t* m)
591 {
592   xfree(m->buf);
593   m->buf = NULL;
594   m->data = NULL;
595 }
596 
597 static void
message_ensure_size(message_t * m,unsigned size)598 message_ensure_size(message_t* m, unsigned size)
599 {
600   m->size = size;
601   if (m->capacity < 2 + 8 + size + 4) {
602     m->capacity = 2 + 8 + size + 4;
603     xfree(m->buf);
604     m->buf = (gbuint8*)xmalloc(m->capacity);
605     m->data = m->buf + 2 + 8;
606   }
607 }
608 
609 static unsigned
message_get_id(const message_t * m)610 message_get_id(const message_t* m)
611 {
612   return le_readu16(m->buf + 4);
613 }
614 
615 //-----------------------------------------------------------------------------
616 
617 static void
message_write(unsigned msg_id,message_t * m)618 message_write(unsigned msg_id, message_t* m)
619 {
620   unsigned chksum;
621   unsigned count;
622   unsigned n;
623   gbuint8* p = m->buf;
624 
625   // header (2 start bytes filled in later)
626   p[2] = 0xdb;
627   p[3] = 0xfe;
628   le_write16(p + 4, msg_id);
629   // "data size" includes 4 trailer bytes
630   le_write16(p + 6, m->size + 4);
631   chksum = checksum(p + 2, 6);
632   le_write16(p + 8, chksum);
633   // message data (filled in by caller)
634   chksum = checksum((gbuint8*) m->data, m->size);
635   n = 2 + 8 + m->size;
636   // trailer (checksum and marker bytes)
637   le_write16(p + n, chksum);
638   p[n + 2] = 0xad;
639   p[n + 3] = 0xbc;
640   // size of message not counting packet start bytes
641   count = 8 + m->size + 4;
642   do {
643     const gbuint8 save0 = p[0];
644     const gbuint8 save1 = p[1];
645     n = delbin_os_packet_size - 2;
646     if (n > count) {
647       n = count;
648     }
649     // doc. says 0x20, device sends 0, probably ignored
650     p[0] = 0x20;
651     // valid bytes in packet after first 2
652     p[1] = n;
653     packet_write(p, 2 + n);
654     p[0] = save0;
655     p[1] = save1;
656     p += n;
657     count -= n;
658   } while (count != 0);
659   if (global_opts.debug_level >= DBGLVL_M) {
660     warning(MYNAME ": sent %x\n", msg_id);
661   }
662 }
663 
664 // read from the payload of a single packet
665 static unsigned
read_depacketize_1(gbuint8 ** p,unsigned n,int new_packet)666 read_depacketize_1(gbuint8** p, unsigned n, int new_packet)
667 {
668   static gbuint8 buf[256];
669   static unsigned buf_i, buf_n;
670   if (new_packet) {
671     buf_n = 0;
672   }
673   while (buf_n == 0) {
674     packet_read(buf);
675     if (buf[1] <= delbin_os_packet_size - 2) {
676       buf_n = buf[1];
677       buf_i = 2;
678     }
679   }
680   *p = buf + buf_i;
681   if (n > buf_n) {
682     n = buf_n;
683   }
684   buf_n -= n;
685   buf_i += n;
686   return n;
687 }
688 
689 // read from packet payloads until request is fulfilled
690 static void
read_depacketize(gbuint8 * buf,unsigned n)691 read_depacketize(gbuint8* buf, unsigned n)
692 {
693   while (n) {
694     gbuint8* p;
695     unsigned nn = read_depacketize_1(&p, n, FALSE);
696     memcpy(buf, p, nn);
697     n -= nn;
698     buf += nn;
699   }
700 }
701 
702 // Get one valid message.
703 // If a corrupted message with the right id is seen, return failure (0).
704 static unsigned
message_read_1(unsigned msg_id,message_t * m)705 message_read_1(unsigned msg_id, message_t* m)
706 {
707   unsigned id;
708   for (;;) {
709     unsigned total;
710     unsigned n;
711     gbuint8 buf[8];
712     gbuint8* p;
713 
714     n = read_depacketize_1(&p, 8, FALSE);
715     memset(buf, 0, 8);
716     memcpy(buf, p, n);
717     while (buf[0] != 0xdb || buf[1] != 0xfe || checksum(buf, 6) != le_readu16(buf + 6)) {
718       // try for a message start at the beginning of next packet
719       n = read_depacketize_1(&p, 8, TRUE);
720       memset(buf, 0, 8);
721       memcpy(buf, p, n);
722     }
723     id = le_readu16(buf + 2);
724     total = le_readu16(buf + 4);
725     message_ensure_size(m, total - 4);
726     // copy in message head, really only need id field, do the rest for debugging
727     m->buf[0] = m->buf[1] = 0;
728     memcpy(m->buf + 2, buf, 8);
729     // read message body and trailer
730     read_depacketize((gbuint8*) m->data, total);
731     p = (gbuint8*)m->data + m->size;
732     if (checksum((gbuint8*) m->data, m->size) == le_readu16(p) &&
733         p[2] == 0xad && p[3] == 0xbc) {
734       if (global_opts.debug_level >= DBGLVL_M) {
735         warning(MYNAME ": received %x\n", id);
736       }
737       break;
738     }
739     if (global_opts.debug_level >= DBGLVL_L) {
740       warning(MYNAME ": corrupted message %x\n", id);
741     }
742     if (id == msg_id) {
743       id = 0;
744       break;
745     }
746   }
747   return id;
748 }
749 
750 // Send MSG_ACK for given message
751 static void
message_ack(unsigned id,const message_t * m)752 message_ack(unsigned id, const message_t* m)
753 {
754   message_t ack;
755   char* p1;
756   const char* p2 = (const char*) m->data;
757   switch (id) {
758   case MSG_ACK:
759   case MSG_NACK:
760   case MSG_NAVIGATION:
761   case MSG_SATELLITE_INFO:
762     // don't ack these
763     return;
764   }
765   message_init_size(&ack, 4);
766   p1 = (char*) ack.data;
767   // ack payload is id and body checksum of acked message
768   le_write16(p1, id);
769   p1[2] = p2[m->size];
770   p1[3] = p2[m->size + 1];
771   message_write(MSG_ACK, &ack);
772   message_free(&ack);
773 }
774 
775 // Get specific message, ignoring others. Sends ACK for non-interval messages.
776 // Gives up after at least READ_TIMEOUT-1 seconds have passed.
777 static int
message_read(unsigned msg_id,message_t * m)778 message_read(unsigned msg_id, message_t* m)
779 {
780   unsigned id;
781   time_t time_start = time(NULL);
782 
783   if (global_opts.debug_level >= DBGLVL_M) {
784     warning(MYNAME ": looking for %x\n", msg_id);
785   }
786   for (;;) {
787     id = message_read_1(msg_id, m);
788     if (id == 0) {
789       break;
790     }
791     if (id == MSG_ERROR) {
792       const gbuint8* p = (const gbuint8*) m->data;
793       fatal(MYNAME ": device error %u: \"%s\"\n", *p, p + 1);
794     }
795     message_ack(id, m);
796     if (id == msg_id || time(NULL) - time_start >= READ_TIMEOUT) {
797       break;
798     }
799   }
800   return id == msg_id;
801 }
802 
803 // Read a sequence of messages, up to a MSG_TRANSFER_COMPLETE
804 static int
get_batch(message_t ** array,unsigned * n)805 get_batch(message_t** array, unsigned* n)
806 {
807   int success = 1;
808   unsigned array_max = 100;
809   message_t* a = (message_t*) xmalloc(array_max * sizeof(message_t));
810   unsigned i = 0;
811   unsigned id;
812   if (global_opts.debug_level >= DBGLVL_M) {
813     warning(MYNAME ": begin get_batch\n");
814   }
815   do {
816     time_t time_start = time(NULL);
817     if (i == array_max) {
818       message_t* old_a = a;
819       array_max += array_max;
820       a = (message_t*) xmalloc(array_max * sizeof(message_t));
821       memcpy(a, old_a, i * sizeof(message_t));
822       xfree(old_a);
823     }
824     message_init(&a[i]);
825     for (;;) {
826       id = message_read_1(0, &a[i]);
827       switch (id) {
828       case MSG_NAVIGATION:
829         if (time(NULL) - time_start >= READ_TIMEOUT) {
830           success = 0;
831           break;
832         }
833         // fall through
834       case MSG_ACK:
835       case MSG_NACK:
836       case MSG_SATELLITE_INFO:
837         continue;
838       }
839       break;
840     }
841     message_ack(id, &a[i]);
842     i++;
843   } while (success && id != MSG_TRANSFER_COMPLETE);
844   if (success) {
845     *array = a;
846     *n = i - 1;
847     message_free(&a[*n]);
848     if (global_opts.debug_level >= DBGLVL_M) {
849       warning(MYNAME ": end get_batch, %u messages\n", *n);
850     }
851   } else {
852     while (i--) {
853       message_free(&a[i]);
854     }
855     xfree(a);
856     *array = NULL;
857     *n = 0;
858     if (global_opts.debug_level >= DBGLVL_M) {
859       warning(MYNAME ": end get_batch, failed\n");
860     }
861   }
862   return success;
863 }
864 
865 typedef struct {
866   unsigned msg_id;
867   message_t msg;
868 } batch_array_t;
869 
870 static batch_array_t* batch_array;
871 
872 static unsigned batch_array_max;
873 static unsigned batch_array_i;
874 
875 // add a message to sequence that will later be sent all at once
876 static void
add_to_batch(unsigned id,message_t * m)877 add_to_batch(unsigned id, message_t* m)
878 {
879   if (batch_array_i == batch_array_max) {
880     char* old = (char*) batch_array;
881     if (batch_array_max == 0) {
882       batch_array_max = 50;
883     }
884     batch_array_max += batch_array_max;
885     batch_array = (batch_array_t*) xmalloc(batch_array_max * sizeof(*batch_array));
886     if (batch_array_i) {
887       memcpy(batch_array, old, batch_array_i * sizeof(*batch_array));
888       xfree(old);
889     }
890   }
891   batch_array[batch_array_i].msg_id = id;
892   batch_array[batch_array_i].msg = *m;
893   batch_array_i++;
894   memset(m, 0, sizeof(*m));
895 }
896 
897 // send an accumulated sequence of messages
898 static void
send_batch(void)899 send_batch(void)
900 {
901   message_t m;
902   const unsigned n = batch_array_i;
903   unsigned i;
904   unsigned progress = 0;
905 
906   message_init(&m);
907   if (global_opts.debug_level >= DBGLVL_M) {
908     warning(MYNAME ": begin send_batch, %u messages\n", n);
909   }
910   for (i = 0; i < n; i++) {
911     unsigned timeout_count = 0;
912     time_t time_start = time(NULL);
913 
914     // Can't really trigger this off either i or n as we don't
915     // know how the various packets map to actual waypts.
916     if (global_opts.verbose_status &&
917         (batch_array[i].msg_id == MSG_WAYPOINT_IN)) {
918       waypt_status_disp(waypoint_n, ++progress);
919     }
920 
921     message_write(batch_array[i].msg_id, &batch_array[i].msg);
922     for (;;) {
923       unsigned id = message_read_1(0, &m);
924       switch (id) {
925       case MSG_ACK:
926         break;
927       case MSG_NAVIGATION:
928         if (time(NULL) - time_start >= 2) {
929           if (timeout_count) {
930             fatal(MYNAME ": send_batch timed out\n");
931           }
932           timeout_count++;
933           if (global_opts.debug_level >= DBGLVL_M) {
934             warning(MYNAME ": re-sending %x\n", batch_array[i].msg_id);
935           }
936           message_write(batch_array[i].msg_id, &batch_array[i].msg);
937           time_start = time(NULL);
938         }
939         // fall through
940       case MSG_NACK:
941       case MSG_SATELLITE_INFO:
942         continue;
943       default:
944         warning(MYNAME ": unexpected response message %x during send_batch\n", id);
945         continue;
946       }
947       break;
948     }
949   }
950   message_read(MSG_TRANSFER_COMPLETE, &m);
951   if (global_opts.debug_level >= DBGLVL_M) {
952     warning(MYNAME ": end send_batch\n");
953   }
954   for (i = n; i--;) {
955     message_free(&batch_array[i].msg);
956   }
957   xfree(batch_array);
958   message_free(&m);
959   batch_array_i = batch_array_max = 0;
960 }
961 
962 //-----------------------------------------------------------------------------
963 // Coordinate conversion
964 
965 static double
delbin_rad2deg(gbint32 x)966 delbin_rad2deg(gbint32 x)
967 {
968   return x * ((180 / M_PI) / 100000000);
969 }
970 
971 static gbint32
delbin_deg2rad(double x)972 delbin_deg2rad(double x)
973 {
974   return (gbint32)(x * ((M_PI / 180) * 100000000));
975 }
976 
977 //-----------------------------------------------------------------------------
978 // Waypoint reading
979 
980 static time_t
decode_time(const gbuint8 * p)981 decode_time(const gbuint8* p)
982 {
983   struct tm t;
984   t.tm_year = p[0];
985   t.tm_mon  = p[1] - 1;
986   t.tm_mday = p[2];
987   t.tm_hour = p[3];
988   t.tm_min  = p[4];
989   t.tm_sec  = p[5];
990   return mkgmtime(&t);
991 }
992 
993 static waypoint*
decode_waypoint(const void * data)994 decode_waypoint(const void* data)
995 {
996   waypoint* wp = waypt_new();
997   const msg_waypoint_t* p = (const msg_waypoint_t*)data;
998   const char* s;
999   float f;
1000 
1001   wp->creation_time = decode_time(&p->year);
1002   wp->latitude = delbin_rad2deg(le_read32(p->latitude));
1003   wp->longitude = delbin_rad2deg(le_read32(p->longitude));
1004   f = le_read_float(p->elevation);
1005   if (f > UNKNOWN_ELEV) {
1006     wp->altitude = f;
1007   }
1008   wp->icon_descr = waypoint_symbol(p->symbol);
1009   if (wp->icon_descr) {
1010     wp->icon_descr = xstrdup(wp->icon_descr);
1011   }
1012   if (p->name_size && p->name[0]) {
1013     wp->description = xstrdup(p->name);
1014   }
1015   s = p->name + p->name_size;
1016   if (le_readu16(s) &&  s[2]) {
1017     wp->notes = xstrdup(s + 2);
1018   }
1019   return wp;
1020 }
1021 
1022 static void
read_waypoints(void)1023 read_waypoints(void)
1024 {
1025   message_t m;
1026   message_t* msg_array;
1027   unsigned msg_array_n;
1028   waypoint* wp = NULL;
1029   unsigned n_point;
1030   unsigned notes_i = 0;
1031   unsigned notes_max = 0;
1032   unsigned i;
1033   int attempt = ATTEMPT_MAX;
1034 
1035   message_init(&m);
1036   // get number of waypoints
1037   for (;;) {
1038     m.size = 0;
1039     message_write(MSG_WAYPOINT_COUNT, &m);
1040     if (message_read(MSG_WAYPOINT_COUNT, &m)) {
1041       break;
1042     }
1043     if (--attempt == 0) {
1044       fatal(MYNAME ": reading waypoint count failed\n");
1045     }
1046   }
1047   n_point = le_readu32(m.data);
1048   if (global_opts.debug_level >= DBGLVL_L) {
1049     warning(MYNAME ": %u waypoints\n", n_point);
1050   }
1051   if (n_point == 0) {
1052     message_free(&m);
1053     return;
1054   }
1055   // get waypoint messages
1056   attempt = ATTEMPT_MAX;
1057   for (;;) {
1058     m.size = MSG_REQUEST_WAYPOINTS_SIZE;
1059     memset(m.data, 0, m.size);
1060     // This byte is documented as reserved. Setting it to 3 is required to get
1061     // extended notes (message 0xb015) with PN-40 firmware 2.5.
1062     // Whether it has any effect with earlier firmware or the PN-20 is unknown.
1063     ((char*)m.data)[1] = 3;
1064     message_write(MSG_REQUEST_WAYPOINTS, &m);
1065     if (get_batch(&msg_array, &msg_array_n)) {
1066       break;
1067     }
1068     if (--attempt == 0) {
1069       fatal(MYNAME ": reading waypoints failed\n");
1070     }
1071     if (global_opts.debug_level >= DBGLVL_M) {
1072       warning(MYNAME ": timed out reading waypoints, retrying\n");
1073     }
1074     m.size = MSG_BREAK_SIZE;
1075     memset(m.data, 0, m.size);
1076     message_write(MSG_BREAK, &m);
1077   }
1078   message_free(&m);
1079   // process waypoint messages
1080   for (i = 0; i < msg_array_n; i++) {
1081     unsigned id = message_get_id(&msg_array[i]);
1082     if (id == MSG_WAYPOINT_OUT) {
1083       wp = decode_waypoint(msg_array[i].data);
1084       waypt_add(wp);
1085       notes_i = 0;
1086       notes_max = 0;
1087       if (global_opts.debug_level >= DBGLVL_L) {
1088         warning(MYNAME ": read waypoint '%s'\n", wp->description);
1089       }
1090     } else if (wp && id == MSG_WAYPOINT_NOTE_OUT) {
1091       const msg_waypoint_note_t* p = (const msg_waypoint_note_t*) msg_array[i].data;
1092       const char* s = p->name + p->name_size;
1093       unsigned nn = le_readu16(s);
1094       if (notes_max < notes_i + nn) {
1095         char* old = wp->notes;
1096         if (notes_max == 0) {
1097           notes_max = nn;
1098         }
1099         do {
1100           notes_max += notes_max;
1101         } while (notes_max < notes_i + nn);
1102         wp->notes = (char*) xmalloc(notes_max);
1103         if (old) {
1104           memcpy(wp->notes, old, notes_i);
1105           xfree(old);
1106         }
1107       }
1108       if (nn) {
1109         memcpy(wp->notes + notes_i, s + 2, nn);
1110         notes_i += nn;
1111         if (wp->notes[notes_i - 1] == 0) {
1112           notes_i--;
1113         }
1114       }
1115     } else {
1116       fatal(MYNAME ": unexpected message %x while reading waypoints\n", id);
1117     }
1118     message_free(&msg_array[i]);
1119   }
1120   xfree(msg_array);
1121 }
1122 
1123 //-----------------------------------------------------------------------------
1124 // Waypoint writing
1125 
1126 static void
encode_time(time_t time_,gbuint8 * p)1127 encode_time(time_t time_, gbuint8* p)
1128 {
1129   const struct tm* t = gmtime(&time_);
1130   p[0] = t->tm_year;
1131   p[1] = t->tm_mon + 1;
1132   p[2] = t->tm_mday;
1133   p[3] = t->tm_hour;
1134   p[4] = t->tm_min;
1135   p[5] = t->tm_sec;
1136 }
1137 
1138 static void
get_gc_notes(const waypoint * wp,int * symbol,char ** notes,unsigned * notes_size)1139 get_gc_notes(const waypoint* wp, int* symbol, char** notes, unsigned* notes_size)
1140 {
1141   fs_xml* fs_gpx;
1142   xml_tag* root = NULL;
1143   gbfile* fd = gbfopen(NULL, "w", MYNAME);
1144   const char* size = NULL;
1145   int gc_sym = 0;
1146 
1147   switch (wp->gc_data->type) {
1148   case gt_traditional:
1149     gc_sym = 160;
1150     break;
1151   case gt_multi:
1152     gc_sym = 161;
1153     break;
1154   case gt_virtual:
1155     gc_sym = 169;
1156     break;
1157   case gt_letterbox:
1158     gc_sym = 163;
1159     break;
1160   case gt_event:
1161     gc_sym = 165;
1162     break;
1163   case gt_suprise:
1164     gc_sym = 162;
1165     break;
1166   case gt_webcam:
1167     gc_sym = 170;
1168     break;
1169   case gt_earth:
1170     gc_sym = 168;
1171     break;
1172   case gt_benchmark:
1173     gc_sym = 172;
1174     break;
1175   case gt_cito:
1176     gc_sym = 167;
1177     break;
1178   case gt_mega:
1179     gc_sym = 166;
1180     break;
1181   case gt_wherigo:
1182     gc_sym = 164;
1183     break;
1184   case gt_unknown:
1185   case gt_locationless:
1186   case gt_ape:
1187     break;
1188   }
1189   if (0 == strcmp(wp->icon_descr, "Geocache Found")) {
1190     gc_sym = 124;
1191   }
1192   if (wp->description) {
1193     gbfputs(wp->description, fd);
1194     if (wp->gc_data->placer) {
1195       gbfprintf(fd, " by %s", wp->gc_data->placer);
1196     }
1197     gbfputc('\n', fd);
1198   }
1199 
1200   gbfprintf(fd, "Cache ID: %s\n", wp->shortname);
1201   if (gc_sym && opt_gcsym && atoi(opt_gcsym)) {
1202     gbfprintf(fd, "%s\n", waypoint_symbol(gc_sym));
1203     *symbol = gc_sym;
1204   } else if (wp->icon_descr) {
1205     gbfprintf(fd, "%s\n", wp->icon_descr);
1206   }
1207   switch (wp->gc_data->container) {
1208   case gc_micro:
1209     size = "Micro";
1210     break;
1211   case gc_small:
1212     size = "Small";
1213     break;
1214   case gc_regular:
1215     size = "Regular";
1216     break;
1217   case gc_large:
1218     size = "Large";
1219     break;
1220   case gc_unknown:
1221     size = "Not Chosen" ;
1222     break;
1223   case gc_other:
1224     size = "Other";
1225     break;
1226     // Device has no symbol for this, but this is what Topo sends.
1227   case gc_virtual:
1228     size = "Virtual";
1229     break;
1230   default:
1231     break;
1232   }
1233   if (size) {
1234     gbfprintf(fd, "SIZE: %s\n", size);
1235   }
1236   if (wp->gc_data->diff % 10) {
1237     gbfprintf(fd, "D%.1f", wp->gc_data->diff / 10.0);
1238   } else {
1239     gbfprintf(fd, "D%u", wp->gc_data->diff / 10);
1240   }
1241   if (wp->gc_data->terr % 10) {
1242     gbfprintf(fd, "/T%.1f\n", wp->gc_data->terr / 10.0);
1243   } else {
1244     gbfprintf(fd, "/T%u\n", wp->gc_data->terr / 10);
1245   }
1246   if (wp->gc_data->hint && !opt_hint_at_end) {
1247     gbfprintf(fd, "HINT: %s\n", wp->gc_data->hint);
1248   }
1249   if (wp->gc_data->desc_short.utfstring || wp->gc_data->desc_long.utfstring) {
1250     gbfputs("DESC: ", fd);
1251     if (wp->gc_data->desc_short.utfstring) {
1252       char* s1 = strip_html(&wp->gc_data->desc_short);
1253       char* s2 = cet_str_utf8_to_any(s1, global_opts.charset);
1254       gbfprintf(fd, "%s\n", s2);
1255       xfree(s2);
1256       xfree(s1);
1257     }
1258     if (wp->gc_data->desc_long.utfstring) {
1259       char* s1 = strip_html(&wp->gc_data->desc_long);
1260       char* s2 = cet_str_utf8_to_any(s1, global_opts.charset);
1261       gbfputs(s2, fd);
1262       xfree(s2);
1263       xfree(s1);
1264     }
1265   }
1266   fs_gpx = (fs_xml*)fs_chain_find(wp->fs, FS_GPX);
1267   if (opt_logs && fs_gpx && fs_gpx->tag) {
1268     root = xml_findfirst(fs_gpx->tag, "groundspeak:logs");
1269   }
1270   if (root) {
1271     xml_tag* curlog = xml_findfirst(root, "groundspeak:log");
1272     if (curlog) {
1273       gbfputs("\nLOG:\n", fd);
1274     }
1275     for (; curlog; curlog = xml_findnext(root, curlog, "groundspeak:log")) {
1276       xml_tag* logpart = xml_findfirst(curlog, "groundspeak:type");
1277       if (logpart) {
1278         gbfprintf(fd, "%s\n", logpart->cdata);
1279       }
1280       logpart = xml_findfirst(curlog, "groundspeak:date");
1281       if (logpart) {
1282         time_t logtime = xml_parse_time(logpart->cdata, NULL);
1283         const struct tm* logtm = gmtime(&logtime);
1284         gbfprintf(fd, "%d-%02d-%02d ", logtm->tm_year + 1900, logtm->tm_mon + 1, logtm->tm_mday);
1285       }
1286       logpart = xml_findfirst(curlog, "groundspeak:finder");
1287       if (logpart) {
1288         char* s = cet_str_utf8_to_any(logpart->cdata, global_opts.charset);
1289         gbfputs(s, fd);
1290         xfree(s);
1291       }
1292       logpart = xml_findfirst(curlog, "groundspeak:text");
1293       if (logpart) {
1294         char* s = cet_str_utf8_to_any(logpart->cdata, global_opts.charset);
1295         gbfprintf(fd, ", %s", s);
1296         xfree(s);
1297       }
1298       gbfputc('\n', fd);
1299     }
1300   }
1301   if (wp->gc_data->hint && opt_hint_at_end) {
1302     gbfprintf(fd, "\nHINT: %s\n", wp->gc_data->hint);
1303   }
1304   gbfputc(0, fd);
1305   *notes_size = fd->memlen;
1306   *notes = (char*) xmalloc(*notes_size);
1307   memcpy(*notes, fd->handle.mem, *notes_size);
1308   gbfclose(fd);
1309 }
1310 
1311 static void
write_waypoint_notes(const char * notes,unsigned size,const char * name)1312 write_waypoint_notes(const char* notes, unsigned size, const char* name)
1313 {
1314   message_t m;
1315   const unsigned name_size = strlen(name) + 1;
1316   const unsigned bytes_per_msg = (10 * (delbin_os_packet_size - 2)) - name_size - 20;
1317   const unsigned msg_count = (size + (bytes_per_msg - 1)) / bytes_per_msg;
1318   unsigned i = 1;
1319 
1320   do {
1321     char* pp;
1322     unsigned n = bytes_per_msg;
1323     msg_waypoint_note_t* p;
1324     message_init_size(&m, 2 + 2 + 1 + name_size + 2 + bytes_per_msg);
1325     p = (msg_waypoint_note_t*) m.data;
1326     le_write16(p->index, i++);
1327     le_write16(p->total, msg_count);
1328     p->name_size = name_size;
1329     memcpy(p->name, name, p->name_size);
1330     pp = p->name + p->name_size;
1331     if (n > size) {
1332       n = size;
1333     }
1334     le_write16(pp, n);
1335     pp += 2;
1336     memcpy(pp, notes, n);
1337     pp += n;
1338     if (*(pp - 1)) {
1339       *pp++ = 0;
1340     }
1341     notes += n;
1342     size -= n;
1343     m.size = pp - (char*)p;
1344     add_to_batch(MSG_WAYPOINT_NOTE_IN, &m);
1345   } while (size != 0);
1346 }
1347 
1348 static void
add_nuke(nuke_type type)1349 add_nuke(nuke_type type)
1350 {
1351   message_t m;
1352   msg_delete_t* p;
1353 
1354   message_init_size(&m, MSG_DELETE_SIZE);
1355   p = (msg_delete_t*) m.data;
1356   p->type = type;
1357   p->mode = nuke_mode_all;
1358   p->location = nuke_dest_internal;
1359   memset(p->object_name, 0, sizeof(p->object_name));
1360 
1361   // MSG_DELETE generates a MSG_TRANSFER_COMPLETE,
1362   // so use the batch facility to wait for it
1363   add_to_batch(MSG_DELETE, &m);
1364   send_batch();
1365 }
1366 
1367 static void
write_waypoint(const waypoint * wp)1368 write_waypoint(const waypoint* wp)
1369 {
1370   message_t m;
1371   msg_waypoint_t* p;
1372   const char* name = wp->shortname;
1373   unsigned name_size;
1374   char* notes;
1375   unsigned notes_size = 0;
1376   unsigned extended_notes_size = 0;
1377   char* notes_freeable = NULL;
1378   int symbol = -1;
1379   float elev = UNKNOWN_ELEV;
1380   char* pp;
1381 
1382   if (waypt_empty_gc_data(wp)) {
1383     notes = wp->notes;
1384     if (notes == NULL && wp->description && strcmp(wp->shortname, wp->description)) {
1385       notes = wp->description;
1386     }
1387     if (notes) {
1388       notes_size = strlen(notes) + 1;
1389     }
1390   } else {
1391     get_gc_notes(wp, &symbol, &notes, &notes_size);
1392     notes_freeable = notes;
1393     if (wp->description) {
1394       name = mkshort(mkshort_handle, wp->description);
1395     }
1396   }
1397 
1398   if (notes_size > 800) {
1399     if (use_extended_notes) {
1400       extended_notes_size = notes_size;
1401       notes_size = 1;
1402     } else {
1403       notes_size = 800;
1404     }
1405   }
1406 
1407   name_size = strlen(name) + 1;
1408   if (name_size > 255) {
1409     name_size = 255;
1410   }
1411   message_init_size(&m, 31 + name_size + notes_size);
1412   p = (msg_waypoint_t*) m.data;
1413 
1414   waypoint_i++;
1415   le_write32(p->total, waypoint_n);
1416   le_write32(p->index, waypoint_i);
1417   encode_time(wp->creation_time, &p->year);
1418   le_write32(p->latitude, delbin_deg2rad(wp->latitude));
1419   le_write32(p->longitude, delbin_deg2rad(wp->longitude));
1420   if (wp->altitude > unknown_alt) {
1421     elev = wp->altitude;
1422   }
1423   le_write_float(p->elevation, elev);
1424   if (symbol < 0) {
1425     symbol = 0;
1426     if (wp->icon_descr) {
1427       symbol = waypoint_symbol_index(wp->icon_descr);
1428     }
1429   }
1430   p->symbol = symbol;
1431   p->name_size = name_size;
1432   memcpy(p->name, name, name_size - 1);
1433   p->name[name_size - 1] = 0;
1434   pp = p->name + name_size;
1435   m.size = (pp + 2 + notes_size) - (char*)p;
1436   if (extended_notes_size) {
1437     le_write16(pp, 0xffff);
1438     pp[2] = 0;
1439   } else {
1440     le_write16(pp, notes_size);
1441     if (notes) {
1442       memcpy(pp + 2, notes, notes_size - 1);
1443       pp[2 + notes_size - 1] = 0;
1444     }
1445   }
1446 
1447   add_to_batch(MSG_WAYPOINT_IN, &m);
1448 
1449   if (extended_notes_size) {
1450     write_waypoint_notes(notes, extended_notes_size, name);
1451   }
1452   if (notes_freeable) {
1453     xfree(notes_freeable);
1454   }
1455   if (global_opts.debug_level >= DBGLVL_L) {
1456     warning(MYNAME ": wrote waypoint %u '%s'\n", waypoint_i, name);
1457   }
1458 }
1459 
1460 static void
write_waypoints(void)1461 write_waypoints(void)
1462 {
1463   message_t m;
1464   unsigned device_n = 0;
1465 
1466   waypoint_i = 0;
1467   waypoint_n = waypt_count();
1468   if (waypoint_n > device_max_waypoint) {
1469     fatal(MYNAME ": waypoint count (%u) exceeds device limit (%u)\n",
1470           waypoint_n, device_max_waypoint);
1471   }
1472 
1473   message_init_size(&m, 0);
1474   message_write(MSG_WAYPOINT_COUNT, &m);
1475   if (message_read(MSG_WAYPOINT_COUNT, &m)) {
1476     device_n = le_readu32(m.data);
1477   }
1478 
1479   waypt_disp_all(write_waypoint);
1480   send_batch();
1481 
1482   if (device_n + waypoint_n > device_max_waypoint) {
1483     m.size = 0;
1484     message_write(MSG_WAYPOINT_COUNT, &m);
1485     if (message_read(MSG_WAYPOINT_COUNT, &m) &&
1486         le_readu32(m.data) == device_max_waypoint) {
1487       warning(MYNAME ": waypoint count (%u already on device + %u added = %u)"
1488               " exceeds device limit (%u), some may have been discarded\n",
1489               device_n, waypoint_n, device_n + waypoint_n, device_max_waypoint);
1490     }
1491   }
1492   message_free(&m);
1493 }
1494 
1495 //-----------------------------------------------------------------------------
1496 // Track reading
1497 
1498 static void
decode_sat_fix(waypoint * wp,const gbuint8 status)1499 decode_sat_fix(waypoint* wp, const gbuint8 status)
1500 {
1501   switch (status & 3) {
1502   case 1:
1503     wp->fix = fix_none;
1504     break;
1505   case 2:
1506     wp->fix = fix_2d;
1507     break;
1508   case 3:
1509     wp->fix = fix_3d;
1510     if (status & 4) {
1511       wp->fix = fix_dgps;
1512     }
1513     break;
1514   }
1515 }
1516 
1517 static void
decode_track_point(const void * data,unsigned * wp_array_i,unsigned max_point)1518 decode_track_point(const void* data, unsigned* wp_array_i, unsigned max_point)
1519 {
1520   const msg_track_point_t* p = (const msg_track_point_t*) data;
1521   const unsigned n = p->number;
1522   unsigned i;
1523   unsigned j = *wp_array_i;
1524 
1525   if (j + n > max_point) {
1526     fatal(MYNAME ": read too many track points\n");
1527   }
1528   for (i = 0; i < n; i++, j++) {
1529     waypoint* wp = waypt_new();
1530     float elev = le_read_float(p->point[i].elevation);
1531     wp_array[j] = wp;
1532     wp->creation_time = decode_time(&p->point[i].year);
1533     wp->latitude = delbin_rad2deg(le_read32(p->point[i].latitude));
1534     wp->longitude = delbin_rad2deg(le_read32(p->point[i].longitude));
1535     if (elev > UNKNOWN_ELEV) {
1536       wp->altitude = elev;
1537     }
1538     wp->speed = le_readu16(p->point[i].speed);
1539     wp->speed *= (100.0f / (60 * 60));
1540     wp->wpt_flags.speed = 1;
1541     decode_sat_fix(wp, p->point[i].status);
1542     wp->wpt_flags.new_trkseg = (p->point[i].status & 0x10) != 0;
1543   }
1544   *wp_array_i = j;
1545 }
1546 
1547 static void
read_track(route_head * track)1548 read_track(route_head* track)
1549 {
1550   message_t m;
1551   message_t* msg_array;
1552   const msg_track_header_t* p;
1553   unsigned msg_array_n;
1554   unsigned wp_array_i = 0;
1555   unsigned n_point;
1556   unsigned i;
1557   int attempt = ATTEMPT_MAX;
1558 
1559   message_init(&m);
1560   // read track messages
1561   for (;;) {
1562     m.size = MSG_REQUEST_TRACKS_SIZE;
1563     memset(m.data, 0, m.size);
1564     ((char*)m.data)[0] = 1;  // Download single track
1565     strcpy((char*)m.data + 1, track->rte_name);
1566     message_write(MSG_REQUEST_TRACKS, &m);
1567     if (get_batch(&msg_array, &msg_array_n)) {
1568       break;
1569     }
1570     if (--attempt == 0) {
1571       fatal(MYNAME ": reading track '%s' failed\n", track->rte_name);
1572     }
1573     if (global_opts.debug_level >= DBGLVL_M) {
1574       warning(MYNAME ": timed out reading track '%s', retrying\n", track->rte_name);
1575     }
1576     m.size = MSG_BREAK_SIZE;
1577     memset(m.data, 0, m.size);
1578     message_write(MSG_BREAK, &m);
1579   }
1580   message_free(&m);
1581   if (msg_array_n == 0 || message_get_id(&msg_array[0]) != MSG_TRACK_HEADER_OUT) {
1582     fatal(MYNAME ": reading track '%s' failed (missing track header)\n", track->rte_name);
1583   }
1584   // process track messages
1585   p = (const msg_track_header_t*) msg_array[0].data;
1586   if (le_readu16(p->comment_size)) {
1587     track->rte_desc = xstrdup(p->comment);
1588   }
1589   track->line_color.bbggrr = track_color(p->color[0]);
1590   n_point = le_readu32(p->total_points);
1591   wp_array = (waypoint**) xcalloc(n_point, sizeof(*wp_array));
1592   message_free(&msg_array[0]);
1593   for (i = 1; i < msg_array_n; i++) {
1594     unsigned id = message_get_id(&msg_array[i]);
1595     if (id == MSG_TRACK_POINT_OUT) {
1596       decode_track_point(msg_array[i].data, &wp_array_i, n_point);
1597     } else {
1598       fatal(MYNAME ": unexpected message %x while reading track '%s'\n", id, track->rte_name);
1599     }
1600     message_free(&msg_array[i]);
1601   }
1602   xfree(msg_array);
1603   if (n_point != wp_array_i) {
1604     fatal(MYNAME ": track point count mismatch, expected %u, got %u\n", n_point, wp_array_i);
1605   }
1606   if (global_opts.debug_level >= DBGLVL_L) {
1607     warning(MYNAME ": read track '%s' %u points\n", track->rte_name, n_point);
1608   }
1609   for (i = 0; i < n_point; i++) {
1610     track_add_wpt(track, wp_array[i]);
1611   }
1612   track_add_head(track);
1613   xfree(wp_array);
1614 }
1615 
1616 static void
read_tracks(void)1617 read_tracks(void)
1618 {
1619   message_t m;
1620   message_t* msg_array;
1621   unsigned msg_array_n;
1622   route_head** track_array;
1623   unsigned total;
1624   unsigned i;
1625   int attempt = ATTEMPT_MAX;
1626 
1627   message_init(&m);
1628   // get number of tracks
1629   for (;;) {
1630     m.size = 0;
1631     message_write(MSG_TRACK_COUNT, &m);
1632     if (message_read(MSG_TRACK_COUNT, &m)) {
1633       break;
1634     }
1635     if (--attempt == 0) {
1636       fatal(MYNAME ": reading track count failed\n");
1637     }
1638   }
1639   total = le_readu32(m.data);
1640   if (global_opts.debug_level >= DBGLVL_L) {
1641     warning(MYNAME ": %u tracks\n", total);
1642   }
1643   if (total == 0) {
1644     message_free(&m);
1645     return;
1646   }
1647 
1648   // First get track headers, then request each track with non-zero number of points
1649   attempt = ATTEMPT_MAX;
1650   for (;;) {
1651     m.size = MSG_REQUEST_TRACKS_SIZE;
1652     memset(m.data, 0, m.size);
1653     ((char*)m.data)[0] = 2;  // Download all track headers
1654     message_write(MSG_REQUEST_TRACKS, &m);
1655     if (get_batch(&msg_array, &msg_array_n)) {
1656       break;
1657     }
1658     if (--attempt == 0) {
1659       fatal(MYNAME ": reading track headers failed\n");
1660     }
1661     if (global_opts.debug_level >= DBGLVL_M) {
1662       warning(MYNAME ": timed out reading track headers, retrying\n");
1663     }
1664     m.size = MSG_BREAK_SIZE;
1665     memset(m.data, 0, m.size);
1666     message_write(MSG_BREAK, &m);
1667   }
1668   message_free(&m);
1669   track_array = (route_head**) xcalloc(total, sizeof(*track_array));
1670   for (i = 0; i < msg_array_n; i++) {
1671     unsigned id = message_get_id(&msg_array[i]);
1672     if (id == MSG_TRACK_HEADER_OUT) {
1673       const msg_track_header_t* p = (msg_track_header_t*) msg_array[i].data;
1674       if (le_readu32(p->total_points)) {
1675         track_array[i] = route_head_alloc();
1676         track_array[i]->rte_name = xstrdup(p->name);
1677       }
1678     } else {
1679       fatal(MYNAME ": unexpected message %x while reading track headers\n", id);
1680     }
1681     message_free(&msg_array[i]);
1682   }
1683   xfree(msg_array);
1684   // get each track
1685   for (i = 0; i < total; i++) {
1686     if (track_array[i]) {
1687       read_track(track_array[i]);
1688     }
1689   }
1690   xfree(track_array);
1691 }
1692 
1693 //-----------------------------------------------------------------------------
1694 // Track writing
1695 
1696 static void
write_track_points(void)1697 write_track_points(void)
1698 {
1699   message_t m;
1700   const unsigned pt_per_msg = 10;
1701   msg_track_point_t* p = NULL;
1702   unsigned i = 0;
1703   unsigned j = 0;
1704 
1705   do {
1706     const waypoint* wp = wp_array[i];
1707     float f;
1708 
1709     if (j == 0) {
1710       message_init_size(&m, 9 + 23 * pt_per_msg);
1711       p =(msg_track_point_t*) m.data;
1712       le_write32(p->total, waypoint_n);
1713       le_write32(p->index, i + 1);
1714     }
1715     assert(p);
1716     encode_time(wp->creation_time, &p->point[j].year);
1717     le_write32(p->point[j].latitude, delbin_deg2rad(wp->latitude));
1718     le_write32(p->point[j].longitude, delbin_deg2rad(wp->longitude));
1719     f = UNKNOWN_ELEV;
1720     if (wp->altitude > unknown_alt) {
1721       f = wp->altitude;
1722     }
1723     le_write_float(p->point[j].elevation, f);
1724     f = WAYPT_GET(wp, speed, 0);
1725     f *= (60 * 60) / 100;
1726     le_write16(p->point[j].speed, (gbuint16)f);
1727     f = WAYPT_GET(wp, course, 0);
1728     f *= 100;
1729     le_write16(p->point[j].heading, (gbuint16)f);
1730     switch (wp->fix) {
1731     default:
1732       p->point[j].status = 0;
1733       break;
1734     case fix_none:
1735       p->point[j].status = 1;
1736       break;
1737     case fix_2d:
1738       p->point[j].status = 2;
1739       break;
1740     case fix_3d:
1741       p->point[j].status = 3;
1742       break;
1743     case fix_dgps:
1744       p->point[j].status = 4 | 3;
1745       break;
1746     }
1747     if (wp->wpt_flags.new_trkseg) {
1748       p->point[j].status |= 0x10;
1749     }
1750     i++;
1751     j++;
1752     if (j == pt_per_msg || i == waypoint_n) {
1753       p->number = j;
1754       m.size = 9 + 23 * j;
1755       add_to_batch(MSG_TRACK_POINT_IN, &m);
1756       j = 0;
1757     }
1758   } while (i < waypoint_n);
1759 }
1760 
1761 static void
write_track_begin(const route_head * track)1762 write_track_begin(const route_head* track)
1763 {
1764   waypoint_i = 0;
1765   waypoint_n = track->rte_waypt_ct;
1766   if (waypoint_n) {
1767     wp_array = (waypoint**) xmalloc(waypoint_n * sizeof(*wp_array));
1768   }
1769 }
1770 
1771 static void
write_track_point(const waypoint * wp)1772 write_track_point(const waypoint* wp)
1773 {
1774   wp_array[waypoint_i++] = (waypoint*)wp;
1775 }
1776 
1777 static void
write_track_end(const route_head * track)1778 write_track_end(const route_head* track)
1779 {
1780   message_t m;
1781   msg_track_header_in_t* p;
1782   unsigned comment_size = 0;
1783 
1784   if (waypoint_n == 0) {
1785     return;
1786   }
1787   if (track->rte_desc) {
1788     comment_size = strlen(track->rte_desc) + 1;
1789   }
1790   message_init_size(&m, sizeof(msg_track_header_in_t) - 1 + comment_size);
1791   p = (msg_track_header_in_t*) m.data;
1792   memset(p->name, 0, sizeof(p->name));
1793   if (track->rte_name) {
1794     strncpy(p->name, track->rte_name, sizeof(p->name) - 1);
1795   } else {
1796     sprintf(p->name, "%lu", (long)wp_array[0]->creation_time);
1797   }
1798   le_write32(p->total_points, waypoint_n);
1799   encode_time(current_time(), &p->year);
1800   le_write16(p->color, track_color_index(track->line_color.bbggrr));
1801   le_write16(p->comment_size, comment_size);
1802   if (comment_size) {
1803     memcpy(p->comment, track->rte_desc, comment_size);
1804   }
1805   add_to_batch(MSG_TRACK_HEADER_IN, &m);
1806   write_track_points();
1807   send_batch();
1808   xfree(wp_array);
1809 }
1810 
1811 static void
write_tracks(void)1812 write_tracks(void)
1813 {
1814   track_disp_all(write_track_begin, write_track_end, write_track_point);
1815 }
1816 
1817 //-----------------------------------------------------------------------------
1818 // Route reading
1819 
1820 static void
decode_route_shape(const void * data,unsigned * wp_array_i)1821 decode_route_shape(const void* data, unsigned* wp_array_i)
1822 {
1823   const msg_route_shape_t* p = (msg_route_shape_t*) data;
1824   const unsigned n = p->number;
1825   unsigned i;
1826   unsigned j = *wp_array_i;
1827 
1828   for (i = 0; i < n; i++, j++) {
1829     char buf[32];
1830     waypoint* wp = waypt_new();
1831     wp_array[j] = wp;
1832     wp->latitude = delbin_rad2deg(le_read32(p->point[i].latitude));
1833     wp->longitude = delbin_rad2deg(le_read32(p->point[i].longitude));
1834     sprintf(buf, "SHP%03u", j);
1835     wp->shortname = xstrdup(buf);
1836   }
1837   *wp_array_i = j;
1838 }
1839 
1840 static waypoint*
decode_route_point(const void * data)1841 decode_route_point(const void* data)
1842 {
1843   const msg_route_point_t* p = (const msg_route_point_t*) data;
1844   const char* s = NULL;
1845   gbfile* fd = gbfopen(NULL, "w", MYNAME);
1846   waypoint* wp = waypt_new();
1847   if (p->name[0]) {
1848     wp->shortname = xstrdup(p->name);
1849   }
1850   // give these a higher priority than the shape points
1851   wp->route_priority = 1;
1852   wp->latitude = delbin_rad2deg(le_read32(p->latitude));
1853   wp->longitude = delbin_rad2deg(le_read32(p->longitude));
1854   switch (p->itinerary_type) {
1855   case 1:
1856     s = "Start";
1857     break;
1858   case 2:
1859     s = "Stop";
1860     break;
1861   case 3:
1862     s = "Finish";
1863     break;
1864   case 4:
1865     s = "Via";
1866     break;
1867   case 5:
1868     s = "Via Hidden";
1869     break;
1870   case 6:
1871     switch (p->turn_type) {
1872     case 1:
1873       s = "Turn, Straight";
1874       break;
1875     case 2:
1876       s = "Turn, Right";
1877       break;
1878     case 3:
1879       s = "Turn, Bear Right";
1880       break;
1881     case 4:
1882       s = "Turn, Keep Right";
1883       break;
1884     case 5:
1885       s = "Turn, Left";
1886       break;
1887     case 6:
1888       s = "Turn, Bear Left";
1889       break;
1890     case 7:
1891       s = "Turn, Keep Left";
1892       break;
1893     case 8:
1894       s = "Turn, Reverse Direction";
1895       break;
1896     case 9:
1897       s = "Turn, Street Name Change";
1898       break;
1899     }
1900     break;
1901   }
1902   if (s) {
1903     gbfprintf(fd, "Type: %s", s);
1904   }
1905   if (p->exit_label_size && p->exit_label[0]) {
1906     gbfprintf(fd, "\nExit: %s", p->exit_label);
1907   }
1908   s = p->exit_label + p->exit_label_size;
1909   if (s[0] && s[1]) {
1910     gbfprintf(fd, "\n%s", s + 1);
1911   }
1912   if (fd->memlen) {
1913     gbfputc(0, fd);
1914     wp->notes = (char*) xmalloc(fd->memlen);
1915     memcpy(wp->notes, fd->handle.mem, fd->memlen);
1916   }
1917   gbfclose(fd);
1918   return wp;
1919 }
1920 
1921 static void
read_route(route_head * route)1922 read_route(route_head* route)
1923 {
1924   message_t m;
1925   message_t* msg_array;
1926   const msg_route_header_t* p;
1927   unsigned msg_array_n;
1928   unsigned wp_array_i = 0;
1929   unsigned route_total, shape_total, total;
1930   unsigned i;
1931   int attempt = ATTEMPT_MAX;
1932 
1933   message_init(&m);
1934   for (;;) {
1935     m.size = MSG_REQUEST_ROUTES_SIZE;
1936     memset(m.data, 0, m.size);
1937     ((char*)m.data)[0] = 1;  // Download single route
1938     strcpy((char*)m.data + 1, route->rte_name);
1939     message_write(MSG_REQUEST_ROUTES, &m);
1940     if (get_batch(&msg_array, &msg_array_n)) {
1941       break;
1942     }
1943     if (--attempt == 0) {
1944       fatal(MYNAME ": reading route '%s' failed (timed out)\n", route->rte_name);
1945     }
1946     if (global_opts.debug_level >= DBGLVL_M) {
1947       warning(MYNAME ": timed out reading route route '%s', retrying\n", route->rte_name);
1948     }
1949     m.size = MSG_BREAK_SIZE;
1950     memset(m.data, 0, m.size);
1951     message_write(MSG_BREAK, &m);
1952   }
1953   message_free(&m);
1954   if (msg_array_n == 0 || message_get_id(&msg_array[0]) != MSG_ROUTE_HEADER_OUT) {
1955     fatal(MYNAME ": missing route header\n");
1956   }
1957   p = (const msg_route_header_t*) msg_array[0].data;
1958   route_total = le_readu32(p->total_route_point);
1959   shape_total = le_readu32(p->total_shape_point);
1960   total = route_total + shape_total;
1961   wp_array = (waypoint**) xcalloc(total, sizeof(*wp_array));
1962   if (global_opts.debug_level >= DBGLVL_L) {
1963     warning(MYNAME ": route '%s' %u points, %u shape points\n",
1964             route->rte_name, route_total, shape_total);
1965   }
1966   message_free(&msg_array[0]);
1967   for (i = 1; i < msg_array_n; i++) {
1968     unsigned id = message_get_id(&msg_array[i]);
1969     if (id == MSG_ROUTE_POINT_OUT) {
1970       wp_array[wp_array_i] = decode_route_point(msg_array[i].data);
1971       if (global_opts.debug_level >= DBGLVL_L) {
1972         warning(MYNAME ": route point '%s'\n", wp_array[wp_array_i]->shortname);
1973       }
1974       wp_array_i++;
1975     } else if (id == MSG_ROUTE_SHAPE_OUT) {
1976       decode_route_shape(msg_array[i].data, &wp_array_i);
1977     } else {
1978       fatal(MYNAME ": unexpected message %x while reading route '%s'\n", id, route->rte_name);
1979     }
1980     message_free(&msg_array[i]);
1981   }
1982   xfree(msg_array);
1983   if (total != wp_array_i) {
1984     fatal(MYNAME ": route point count mismatch, expected %u, got %u\n", total, wp_array_i);
1985   }
1986   for (i = 0; i < total; i++) {
1987     route_add_wpt(route, wp_array[i]);
1988   }
1989   xfree(wp_array);
1990   route_add_head(route);
1991 }
1992 
1993 static void
read_routes(void)1994 read_routes(void)
1995 {
1996   message_t m;
1997   message_t* msg_array;
1998   unsigned msg_array_n;
1999   route_head** route_array;
2000   unsigned total;
2001   unsigned i;
2002   int attempt = ATTEMPT_MAX;
2003 
2004   message_init(&m);
2005   // get number of routes
2006   for (;;) {
2007     m.size = 0;
2008     message_write(MSG_ROUTE_COUNT, &m);
2009     if (message_read(MSG_ROUTE_COUNT, &m)) {
2010       break;
2011     }
2012     if (--attempt == 0) {
2013       fatal(MYNAME ": reading route count failed\n");
2014     }
2015   }
2016   total = le_readu32(m.data);
2017   if (global_opts.debug_level >= DBGLVL_L) {
2018     warning(MYNAME ": %u routes\n", total);
2019   }
2020   if (total == 0) {
2021     message_free(&m);
2022     return;
2023   }
2024 
2025   // First get route headers, then request each route
2026   attempt = ATTEMPT_MAX;
2027   for (;;) {
2028     m.size = MSG_REQUEST_ROUTES_SIZE;
2029     memset(m.data, 0, m.size);
2030     ((char*)m.data)[0] = 2;  // Download all route headers
2031     message_write(MSG_REQUEST_ROUTES, &m);
2032     if (get_batch(&msg_array, &msg_array_n)) {
2033       break;
2034     }
2035     if (--attempt == 0) {
2036       fatal(MYNAME ": reading route headers failed\n");
2037     }
2038     if (global_opts.debug_level >= DBGLVL_M) {
2039       warning(MYNAME ": timed out reading route headers, retrying\n");
2040     }
2041     m.size = MSG_BREAK_SIZE;
2042     memset(m.data, 0, m.size);
2043     message_write(MSG_BREAK, &m);
2044   }
2045   message_free(&m);
2046   route_array = (route_head**) xcalloc(total, sizeof(*route_array));
2047   for (i = 0; i < msg_array_n; i++) {
2048     unsigned id = message_get_id(&msg_array[i]);
2049     if (id == MSG_ROUTE_HEADER_OUT) {
2050       route_array[i] = route_head_alloc();
2051       route_array[i]->rte_name = xstrdup(((msg_route_header_t*)msg_array[i].data)->name);
2052     } else {
2053       fatal(MYNAME ": unexpected message %x while reading route headers\n", id);
2054     }
2055     message_free(&msg_array[i]);
2056   }
2057   xfree(msg_array);
2058   // get each route
2059   for (i = 0; i < total; i++) {
2060     read_route(route_array[i]);
2061   }
2062   xfree(route_array);
2063 }
2064 
2065 //-----------------------------------------------------------------------------
2066 // Route writing
2067 
2068 static unsigned route_point_n;
2069 static unsigned shape_point_n;
2070 static unsigned* shape_point_counts;
2071 
2072 static void
write_route_shape_points(waypoint ** array,unsigned n)2073 write_route_shape_points(waypoint** array, unsigned n)
2074 {
2075   message_t m;
2076   const unsigned pt_per_msg = 25;
2077   msg_route_shape_t* p = NULL;
2078   unsigned i = 0;
2079   unsigned j = 0;
2080 
2081   do {
2082     if (j == 0) {
2083       message_init_size(&m, 10 + 8 * pt_per_msg);
2084       p = (msg_route_shape_t*) m.data;
2085       le_write32(p->total, n);
2086       le_write32(p->index, i + 1);
2087       p->reserved = 0;
2088     }
2089     assert(p);
2090     le_write32(p->point[j].latitude, delbin_deg2rad(array[i]->latitude));
2091     le_write32(p->point[j].longitude, delbin_deg2rad(array[i]->longitude));
2092     i++;
2093     j++;
2094     if (j == pt_per_msg || i == n) {
2095       p->number = j;
2096       m.size = 10 + 8 * j;
2097       add_to_batch(MSG_ROUTE_SHAPE_IN, &m);
2098       j = 0;
2099     }
2100   } while (i < n);
2101 }
2102 
2103 static void
write_route_points(void)2104 write_route_points(void)
2105 {
2106   unsigned route_point_i = 0;
2107   unsigned i = 0;
2108 
2109   while (i < waypoint_n) {
2110     message_t m;
2111     unsigned shape_n;
2112     const waypoint* wp = wp_array[i];
2113     msg_route_point_t* p;
2114     char* s;
2115 
2116     message_init_size(&m, sizeof(msg_route_point_t) + 1 + 1 + 4);
2117     p = (msg_route_point_t*) m.data;
2118     memset(m.data, 0, m.size);
2119     route_point_i++;
2120     shape_n = shape_point_counts[route_point_i];
2121     le_write32(p->total, route_point_n);
2122     le_write32(p->index, route_point_i);
2123     if (wp->shortname) {
2124       strncpy(p->name, wp->shortname, sizeof(p->name) - 1);
2125     } else {
2126       sprintf(p->name, "RPT%u", route_point_i);
2127     }
2128     le_write32(p->latitude, delbin_deg2rad(wp->latitude));
2129     le_write32(p->longitude, delbin_deg2rad(wp->longitude));
2130     p->exit_label_size = 1;
2131     s = p->exit_label + p->exit_label_size;
2132     s[0] = 1;  // comment size
2133     le_write32(s + 2, shape_n);
2134     if (route_point_i == 1) {
2135       p->itinerary_type = 1; // start
2136     } else if (route_point_i == route_point_n) {
2137       p->itinerary_type = 3; // finish
2138     }
2139     add_to_batch(MSG_ROUTE_POINT_IN, &m);
2140     i++;
2141     if (shape_n) {
2142       write_route_shape_points(&wp_array[i], shape_n);
2143       i += shape_n;
2144     }
2145   }
2146 }
2147 
2148 static void
write_route_begin(const route_head * track)2149 write_route_begin(const route_head* track)
2150 {
2151   waypoint_i = 0;
2152   route_point_n = 0;
2153   shape_point_n = 0;
2154   waypoint_n = track->rte_waypt_ct;
2155   if (waypoint_n) {
2156     wp_array = (waypoint**) xmalloc(waypoint_n * sizeof(*wp_array));
2157     shape_point_counts = (unsigned int*) xcalloc(waypoint_n, sizeof(*shape_point_counts));
2158   }
2159 }
2160 
2161 static void
write_route_point(const waypoint * wp)2162 write_route_point(const waypoint* wp)
2163 {
2164   const char* s = wp->shortname;
2165   wp_array[waypoint_i++] = (waypoint*)wp;
2166   if (s && s[0] == 'S' && s[1] == 'H' && s[2] == 'P' && s[3] >= '0' && s[3] <= '9') {
2167     shape_point_n++;
2168     shape_point_counts[route_point_n]++;
2169   } else {
2170     route_point_n++;
2171   }
2172 }
2173 
2174 static void
write_route_end(const route_head * route)2175 write_route_end(const route_head* route)
2176 {
2177   message_t m;
2178   msg_route_header_in_t* p;
2179 
2180   if (waypoint_n == 0) {
2181     return;
2182   }
2183   message_init_size(&m, sizeof(msg_route_header_in_t));
2184   p = (msg_route_header_in_t*) m.data;
2185   memset(p->name, 0, sizeof(p->name));
2186   if (route->rte_name) {
2187     strncpy(p->name, route->rte_name, sizeof(p->name) - 1);
2188   } else {
2189     sprintf(p->name, "%lu", (long)wp_array[0]->creation_time);
2190   }
2191   p->type = 0;
2192   le_write32(p->total_route_point, route_point_n);
2193   le_write32(p->total_shape_point, shape_point_n);
2194   add_to_batch(MSG_ROUTE_HEADER_IN, &m);
2195   write_route_points();
2196   send_batch();
2197   if (wp_array) {
2198     xfree(wp_array);
2199     xfree(shape_point_counts);
2200   }
2201 }
2202 
2203 static void
write_routes(void)2204 write_routes(void)
2205 {
2206   route_disp_all(write_route_begin, write_route_end, write_route_point);
2207 }
2208 
2209 //-----------------------------------------------------------------------------
2210 // Current position
2211 
2212 static waypoint*
decode_navmsg(const void * data)2213 decode_navmsg(const void* data)
2214 {
2215   waypoint* wp = waypt_new();
2216   const msg_navigation_t* p = (const msg_navigation_t*) data;
2217   struct tm t;
2218 
2219   t.tm_year = le_readu16(p->year) - 1900;
2220   t.tm_mon = p->month - 1;
2221   t.tm_mday = p->day;
2222   t.tm_hour = p->hour;
2223   t.tm_min = p->minute;
2224   t.tm_sec = p->second;
2225   wp->creation_time = mkgmtime(&t);
2226   wp->sat = p->satellites;
2227   wp->latitude = le_read_double(p->latitude);
2228   wp->longitude = le_read_double(p->longitude);
2229   wp->altitude = le_read_double(p->elevation);
2230   wp->speed = le_read_float(p->speed);
2231   wp->speed *= (1000.0f / (60 * 60));
2232   wp->wpt_flags.speed = 1;
2233   wp->course = le_readu16(p->heading);
2234   wp->course /= 100;
2235   wp->wpt_flags.course = 1;
2236   decode_sat_fix(wp, p->fix_status);
2237   wp->shortname = xstrdup("Position");
2238   return wp;
2239 }
2240 
2241 static waypoint*
read_position(void)2242 read_position(void)
2243 {
2244   waypoint* wp;
2245   message_t m;
2246 
2247   message_init(&m);
2248   message_read(MSG_NAVIGATION, &m);
2249   wp = decode_navmsg(m.data);
2250   if (wp->fix > fix_none &&
2251       message_read_1(MSG_SATELLITE_INFO, &m) == MSG_SATELLITE_INFO) {
2252     const msg_satellite_t* p = (const msg_satellite_t*) m.data;
2253     wp->hdop = le_readu16(p->hdop);
2254     wp->hdop /= 100;
2255     wp->vdop = le_readu16(p->vdop);
2256     wp->vdop /= 100;
2257     wp->pdop = le_readu16(p->pdop);
2258     wp->pdop /= 100;
2259   }
2260   message_free(&m);
2261   return wp;
2262 }
2263 
2264 //-----------------------------------------------------------------------------
2265 
2266 static void
delbin_list_units()2267 delbin_list_units()
2268 {
2269   int i;
2270   for (i = 0; i < n_delbin_units; i++) {
2271     printf("%u %s %s\n",
2272            delbin_unit_info[i].unit_number,
2273            delbin_unit_info[i].unit_serial_number,
2274            delbin_unit_info[i].unit_name);
2275   }
2276 }
2277 
2278 static void
delbin_rw_init(const char * fname)2279 delbin_rw_init(const char* fname)
2280 {
2281   message_t m;
2282   char buf[256];
2283 
2284   if (!mkshort_handle) {
2285     mkshort_handle = mkshort_new_handle();
2286   }
2287   //  Contrary to doc, it looks like there's a limit of 32 bytes
2288   // and a null terminator is required, at least in F/W 2.6.210726
2289   // on a PN-40.
2290   setshort_length(mkshort_handle, 31);
2291   setshort_whitespace_ok(mkshort_handle, 1);
2292   setshort_badchars(mkshort_handle, "");
2293   setshort_mustuniq(mkshort_handle, 1);
2294 
2295   delbin_os_ops.init(fname);
2296 
2297   // Often the first packet is part of an old message, sometimes it can
2298   // confuse the first message read if we don't get rid of it
2299   packet_read(buf);
2300   // Send a break to clear any state from a previous failure
2301   message_init_size(&m, MSG_BREAK_SIZE);
2302   memset(m.data, 0, m.size);
2303   message_write(MSG_BREAK, &m);
2304   // get version info
2305   m.size = 0;
2306   message_write(MSG_VERSION, &m);
2307   if (message_read(MSG_VERSION, &m)) {
2308     const msg_version_t* p = (const msg_version_t*) m.data;
2309     if (global_opts.debug_level >= DBGLVL_L) {
2310       warning(MYNAME ": device %s %s\n", p->product, p->firmware);
2311     }
2312     if (opt_long_notes) {
2313       use_extended_notes = TRUE;
2314     } else if (strstr(p->product, "PN-20")) {
2315       use_extended_notes = p->firmware[0] > '1' ||
2316                            (p->firmware[0] == '1' && p->firmware[2] >= '6');
2317     } else if (strstr(p->product, "PN-30") || strstr(p->product, "PN-40")) {
2318       use_extended_notes = p->firmware[0] > '2' ||
2319                            (p->firmware[0] == '2' && p->firmware[2] >= '5');
2320     } else {
2321       // assume PN-60 or later
2322       use_extended_notes = TRUE;
2323     }
2324     delbin_unit_info[n_delbin_units].unit_number = n_delbin_units;
2325     delbin_unit_info[n_delbin_units].unit_serial_number = xstrndup(p->serial, sizeof(p->serial));
2326     delbin_unit_info[n_delbin_units].unit_name = xstrndup(p->product, sizeof(p->product));
2327     n_delbin_units++;
2328   }
2329   message_free(&m);
2330 
2331   if (strlen(fname) > 4) {
2332     if (0 == strcmp(fname+4, "list")) {
2333       delbin_list_units();
2334       exit(1);
2335     }
2336   }
2337 }
2338 
2339 static void
delbin_rw_deinit(void)2340 delbin_rw_deinit(void)
2341 {
2342   if (mkshort_handle) {
2343     mkshort_del_handle(&mkshort_handle);
2344   }
2345   delbin_os_ops.deinit();
2346 }
2347 
2348 static void
delbin_read(void)2349 delbin_read(void)
2350 {
2351   if (doing_wpts) {
2352     if (opt_getposn) {
2353       waypt_add(read_position());
2354     } else {
2355       read_waypoints();
2356     }
2357   }
2358   if (doing_trks) {
2359     read_tracks();
2360   }
2361   if (doing_rtes) {
2362     read_routes();
2363   }
2364 }
2365 
2366 static void
delbin_write(void)2367 delbin_write(void)
2368 {
2369   if (doing_wpts) {
2370     message_t m;
2371     device_max_waypoint = 1000;
2372     message_init_size(&m, 0);
2373     message_write(MSG_CAPABILITIES, &m);
2374     if (message_read(MSG_CAPABILITIES, &m)) {
2375       const msg_capabilities_t* p = (const msg_capabilities_t*) m.data;
2376       device_max_waypoint = le_readu32(p->max_waypoints);
2377     }
2378     message_free(&m);
2379 
2380     if (opt_nuke_wpt) {
2381       add_nuke(nuke_type_wpt);
2382     }
2383     write_waypoints();
2384   }
2385   if (doing_trks) {
2386     if (opt_nuke_trk) {
2387       add_nuke(nuke_type_trk);
2388     }
2389     write_tracks();
2390   }
2391   if (doing_rtes) {
2392     if (opt_nuke_rte) {
2393       add_nuke(nuke_type_rte);
2394     }
2395     write_routes();
2396   }
2397 }
2398 
2399 static waypoint*
delbin_rd_position(posn_status * status)2400 delbin_rd_position(posn_status* status)
2401 {
2402   return read_position();
2403 }
2404 
2405 ff_vecs_t delbin_vecs = {
2406   ff_type_serial,
2407   FF_CAP_RW_ALL,
2408   delbin_rw_init,
2409   delbin_rw_init,
2410   delbin_rw_deinit,
2411   delbin_rw_deinit,
2412   delbin_read,
2413   delbin_write,
2414   NULL,
2415   delbin_args,
2416   CET_CHARSET_LATIN1, 1,
2417   { delbin_rw_init, delbin_rd_position, delbin_rw_deinit }
2418 };
2419 
2420 //=============================================================================
2421 // OS device I/O implementations
2422 
2423 #define VENDOR_ID 0x1163
2424 #define PRODUCT_ID 0x2020
2425 
2426 //-----------------------------------------------------------------------------
2427 // Windows
2428 #ifdef HAVE_WDK
2429 
2430 #undef HAVE_LIBUSB
2431 
2432 #define WIN32_LEAN_AND_MEAN
2433 #include <windows.h>
2434 #include <setupapi.h>
2435 // If hidsdi.h is not found, you need to download the Windows Driver Kit,
2436 // from http://www.microsoft.com/whdc/Devtools/wdk/default.mspx
2437 // You need to install 'build environments' and 'tools' from the SDK and
2438 // follow the instructions in the Install.html to get MSVC to find the right
2439 // headers and libraries.
2440 #include <specstrings.h>
2441 #include <hidsdi.h>
2442 
2443 static HANDLE hid_handle;
2444 
2445 static void
win_os_init(const char * fname)2446 win_os_init(const char* fname)
2447 {
2448   GUID hid_guid;
2449   HDEVINFO dev_info;
2450   SP_DEVICE_INTERFACE_DATA dev_int_data;
2451   PHIDP_PREPARSED_DATA hid_ppd;
2452   HIDP_CAPS hid_caps;
2453   const char* busy = "";
2454   unsigned i;
2455 
2456   hid_handle = INVALID_HANDLE_VALUE;
2457   HidD_GetHidGuid(&hid_guid);
2458   dev_info = SetupDiGetClassDevs(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
2459   if (dev_info == INVALID_HANDLE_VALUE) {
2460     fatal(MYNAME ": SetupDiGetClassDevs failed %u\n", GetLastError());
2461   }
2462   dev_int_data.cbSize = sizeof(dev_int_data);
2463   for (i = 0; SetupDiEnumDeviceInterfaces(dev_info, NULL, &hid_guid, i, &dev_int_data); i++) {
2464     union {
2465       SP_DEVICE_INTERFACE_DETAIL_DATA detail_data;
2466       char buf[300];
2467     } u;
2468     u.detail_data.cbSize = sizeof(u.detail_data);
2469     if (SetupDiGetDeviceInterfaceDetail(dev_info,
2470                                         &dev_int_data, &u.detail_data, sizeof(u.buf), NULL, NULL)) {
2471       HANDLE h = CreateFile(u.detail_data.DevicePath,
2472                             FILE_READ_DATA | FILE_WRITE_DATA, 0, NULL, OPEN_EXISTING, 0, NULL);
2473       if (h != INVALID_HANDLE_VALUE) {
2474         HIDD_ATTRIBUTES hid_attr;
2475         hid_attr.Size = sizeof(hid_attr);
2476         if (HidD_GetAttributes(h, &hid_attr) &&
2477             hid_attr.VendorID == VENDOR_ID && hid_attr.ProductID == PRODUCT_ID) {
2478           hid_handle = h;
2479           break;
2480         }
2481         CloseHandle(h);
2482       } else if (GetLastError() == ERROR_SHARING_VIOLATION &&
2483                  strstr(u.detail_data.DevicePath, "1163") &&
2484                  strstr(u.detail_data.DevicePath, "2020")) {
2485         busy = " (device busy?)";
2486       }
2487     }
2488   }
2489   SetupDiDestroyDeviceInfoList(dev_info);
2490   if (hid_handle == INVALID_HANDLE_VALUE) {
2491     fatal(MYNAME ": no DeLorme PN found%s\n", busy);
2492   }
2493   if (!HidD_GetPreparsedData(hid_handle, &hid_ppd)) {
2494     fatal(MYNAME ": HidD_GetPreparsedData failed %u\n", GetLastError());
2495   }
2496   if (!HidP_GetCaps(hid_ppd, &hid_caps)) {
2497     fatal(MYNAME ": HidP_GetCaps failed %u\n", GetLastError());
2498   }
2499   // report length includes report id
2500   delbin_os_packet_size = hid_caps.InputReportByteLength - 1;
2501   HidD_FreePreparsedData(hid_ppd);
2502 }
2503 
2504 static void
win_os_deinit(void)2505 win_os_deinit(void)
2506 {
2507   CloseHandle(hid_handle);
2508 }
2509 
2510 static unsigned
win_os_packet_read(void * buf)2511 win_os_packet_read(void* buf)
2512 {
2513   DWORD n;
2514   char buf1[257];
2515   // first byte is report id
2516   if (ReadFile(hid_handle, buf1, delbin_os_packet_size + 1, &n, NULL) == 0) {
2517     unsigned err = GetLastError();
2518     fatal(MYNAME ": ReadFile failed %u\n", err);
2519   }
2520   if (n > 0) {
2521     n--;
2522   }
2523   memcpy(buf, buf1 + 1, n);
2524   return n;
2525 }
2526 
2527 static unsigned
win_os_packet_write(const void * buf,unsigned size)2528 win_os_packet_write(const void* buf, unsigned size)
2529 {
2530   DWORD n;
2531   char buf1[257];
2532   // first byte is report id
2533   buf1[0] = 0;
2534   memcpy(buf1 + 1, buf, size);
2535   if (WriteFile(hid_handle, buf1, delbin_os_packet_size + 1, &n, NULL) == 0) {
2536     unsigned err = GetLastError();
2537     fatal(MYNAME ": WriteFile of %u bytes failed with %u.  Size: %u Wrote: %d\n",
2538           delbin_os_packet_size + 1, err, size, (int) n);
2539   }
2540   if (n > size) {
2541     n = size;
2542   }
2543   return n;
2544 }
2545 
2546 delbin_os_ops_t delbin_os_ops = {
2547   win_os_init,
2548   win_os_deinit,
2549   win_os_packet_read,
2550   win_os_packet_write
2551 };
2552 
2553 #endif // HAVE_WDK
2554 
2555 //-----------------------------------------------------------------------------
2556 // MacOS X
2557 #if __APPLE__
2558 
2559 #undef HAVE_LIBUSB
2560 
2561 #include <pthread.h>
2562 #include <IOKit/IOKitLib.h>
2563 #include <IOKit/IOCFPlugIn.h>
2564 #include <IOKit/hid/IOHIDLib.h>
2565 #include <mach/mach_error.h>
2566 
2567 // IOHIDDeviceInterface121::getReport() does not work, it hangs the process
2568 // in some sort of unkillable state.  So reading is done via a separate thread
2569 // with a run loop and interrupt report callback. Yuck.
2570 
2571 static IOHIDDeviceInterface122** device;
2572 static pthread_t thread;
2573 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2574 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2575 static char* report_buf;
2576 static char* packet_array[32];
2577 static unsigned packet_array_head;
2578 static unsigned packet_array_tail;
2579 static CFRunLoopRef run_loop;
2580 
2581 static void*
thread_func(void * run_loop_source)2582 thread_func(void* run_loop_source)
2583 {
2584   run_loop = CFRunLoopGetCurrent();
2585 #if __cplusplus
2586   CFRunLoopAddSource(run_loop, (__CFRunLoopSource*) run_loop_source, kCFRunLoopDefaultMode);
2587 #else
2588   CFRunLoopAddSource(run_loop, run_loop_source, kCFRunLoopDefaultMode);
2589 #endif
2590   CFRunLoopRun();
2591   return NULL;
2592 }
2593 
2594 static void
interrupt_report_cb(void * target,IOReturn result,void * refcon,void * sender,UInt32 bufferSize)2595 interrupt_report_cb(void* target, IOReturn result, void* refcon, void* sender, UInt32 bufferSize)
2596 {
2597   memcpy(packet_array[packet_array_head], report_buf, delbin_os_packet_size);
2598   pthread_mutex_lock(&mutex);
2599   if (packet_array_head == packet_array_tail) {
2600     pthread_cond_signal(&cond);
2601   }
2602   packet_array_head++;
2603   packet_array_head &= sizeofarray(packet_array) - 1;
2604   if (packet_array_head == packet_array_tail && global_opts.debug_level >= DBGLVL_M) {
2605     warning(MYNAME ": packet_array overrun, packets lost\n");
2606   }
2607   pthread_mutex_unlock(&mutex);
2608 }
2609 
2610 static void
mac_os_init(const char * fname)2611 mac_os_init(const char* fname)
2612 {
2613   CFMutableDictionaryRef dict = IOServiceMatching(kIOHIDDeviceKey);
2614   io_service_t service;
2615   IOCFPlugInInterface** plugin;
2616   CFNumberRef cf_num;
2617   CFRunLoopSourceRef run_loop_source;
2618   int i;
2619   kern_return_t kr;
2620   HRESULT hr;
2621   IOReturn ir;
2622   SInt32 unused;
2623 
2624   i = VENDOR_ID;
2625   cf_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i);
2626   CFDictionaryAddValue(dict, CFSTR(kIOHIDVendorIDKey), cf_num);
2627   CFRelease(cf_num);
2628   i = PRODUCT_ID;
2629   cf_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i);
2630   CFDictionaryAddValue(dict, CFSTR(kIOHIDProductIDKey), cf_num);
2631   CFRelease(cf_num);
2632   service = IOServiceGetMatchingService(kIOMasterPortDefault, dict);
2633   if (service == 0) {
2634     fatal(MYNAME ": no DeLorme PN found\n");
2635   }
2636   kr = IOCreatePlugInInterfaceForService(
2637          service, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &unused);
2638   if (kr) {
2639     fatal(MYNAME ": IOCreatePlugInInterfaceForService failed 0x%x\n", (int)kr);
2640   }
2641   IOObjectRelease(service);
2642   hr = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122), (void**)&device);
2643   if (hr) {
2644     fatal(MYNAME ": QueryInterface failed 0x%x\n", (int)hr);
2645   }
2646   (*plugin)->Release(plugin);
2647   ir = (*device)->open(device, kIOHIDOptionsTypeSeizeDevice);
2648   if (ir)
2649     fatal(MYNAME ": device open failed 0x%x - %s\n", (int)ir,
2650           mach_error_string(ir));
2651   ir = (*device)->createAsyncEventSource(device, &run_loop_source);
2652   if (ir) {
2653     fatal(MYNAME ": createAsyncEventSource failed 0x%x\n", (int)ir);
2654   }
2655   delbin_os_packet_size = 64;
2656   report_buf = (char*)xmalloc(delbin_os_packet_size);
2657   for (i = sizeofarray(packet_array); i--;) {
2658     packet_array[i] = (char*)xmalloc(delbin_os_packet_size);
2659   }
2660   ir = (*device)->setInterruptReportHandlerCallback(
2661          device, report_buf, delbin_os_packet_size, interrupt_report_cb, NULL, NULL);
2662   if (ir) {
2663     fatal(MYNAME ": setInterruptReportHandlerCallback failed 0x%x\n", (int)ir);
2664   }
2665   i = pthread_create(&thread, NULL, thread_func, run_loop_source);
2666   if (i) {
2667     fatal(MYNAME ": pthread_create failed %d\n", i);
2668   }
2669 }
2670 
2671 static void
mac_os_deinit(void)2672 mac_os_deinit(void)
2673 {
2674   void* unused;
2675   unsigned i;
2676   CFRunLoopStop(run_loop);
2677   pthread_join(thread, &unused);
2678   (*device)->Release(device);
2679   xfree(report_buf);
2680   for (i = sizeofarray(packet_array); i--;) {
2681     xfree(packet_array[i]);
2682   }
2683 }
2684 
2685 static unsigned
mac_os_packet_read(void * buf)2686 mac_os_packet_read(void* buf)
2687 {
2688   pthread_mutex_lock(&mutex);
2689   while (packet_array_head == packet_array_tail) {
2690     pthread_cond_wait(&cond, &mutex);
2691   }
2692   memcpy(buf, packet_array[packet_array_tail++], delbin_os_packet_size);
2693   packet_array_tail &= sizeofarray(packet_array) - 1;
2694   pthread_mutex_unlock(&mutex);
2695   return delbin_os_packet_size;
2696 }
2697 
2698 static unsigned
mac_os_packet_write(const void * buf,unsigned size)2699 mac_os_packet_write(const void* buf, unsigned size)
2700 {
2701   IOReturn r = (*device)->setReport(
2702                  device, kIOHIDReportTypeOutput, 0, (void*)buf, size, 2000, NULL, NULL, NULL);
2703   if (r) {
2704     fatal("setReport failed 0x%x\n", (int)r);
2705   }
2706   return size;
2707 }
2708 
2709 delbin_os_ops_t delbin_os_ops = {
2710   mac_os_init,
2711   mac_os_deinit,
2712   mac_os_packet_read,
2713   mac_os_packet_write
2714 };
2715 
2716 #endif // __APPLE__
2717 
2718 //-----------------------------------------------------------------------------
2719 // libusb
2720 #if HAVE_LIBUSB
2721 
2722 #include <usb.h>
2723 
2724 static struct usb_device* usb_dev;
2725 static usb_dev_handle* usb_handle;
2726 static int endpoint_in;
2727 static int endpoint_out;
2728 
2729 static void
libusb_os_init(const char * fname)2730 libusb_os_init(const char* fname)
2731 {
2732   struct usb_bus* bus;
2733   const struct usb_endpoint_descriptor* endpoint_desc;
2734 
2735   usb_init();
2736   usb_find_busses();
2737   usb_find_devices();
2738   for (bus = usb_busses; usb_dev == NULL && bus; bus = bus->next) {
2739     struct usb_device* d;
2740     for (d = bus->devices; d; d = d->next) {
2741       if (d->descriptor.idVendor == VENDOR_ID && d->descriptor.idProduct == PRODUCT_ID) {
2742         usb_dev = d;
2743         break;
2744       }
2745     }
2746   }
2747   if (usb_dev == NULL) {
2748     fatal(MYNAME ": no DeLorme PN found\n");
2749   }
2750   usb_handle = usb_open(usb_dev);
2751   if (usb_handle == NULL) {
2752     fatal(MYNAME ": %s\n", usb_strerror());
2753   }
2754 
2755   // Device has 1 configuration, 1 interface, 2 interrupt endpoints
2756   if (usb_claim_interface(usb_handle, 0) < 0) {
2757 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
2758     if (usb_detach_kernel_driver_np(usb_handle, 0) < 0) {
2759       warning(MYNAME ": %s\n", usb_strerror());
2760     }
2761     if (usb_claim_interface(usb_handle, 0) < 0)
2762 #endif
2763     {
2764       const char* s = usb_strerror();
2765       usb_close(usb_handle);
2766       fatal(MYNAME ": %s\n", s);
2767     }
2768   }
2769   endpoint_desc = usb_dev->config[0].interface[0].altsetting[0].endpoint;
2770   delbin_os_packet_size = endpoint_desc[0].wMaxPacketSize;
2771   endpoint_in = endpoint_desc[0].bEndpointAddress;
2772   endpoint_out = endpoint_desc[1].bEndpointAddress;
2773   if ((endpoint_in & USB_ENDPOINT_DIR_MASK) == USB_ENDPOINT_OUT) {
2774     int t = endpoint_in;
2775     endpoint_in = endpoint_out;
2776     endpoint_out = t;
2777   }
2778 }
2779 
2780 static void
libusb_os_deinit(void)2781 libusb_os_deinit(void)
2782 {
2783   usb_release_interface(usb_handle, 0);
2784   usb_close(usb_handle);
2785 }
2786 
2787 static unsigned
libusb_os_packet_read(void * buf)2788 libusb_os_packet_read(void* buf)
2789 {
2790   int n = usb_interrupt_read(usb_handle, endpoint_in, buf, delbin_os_packet_size, 2000);
2791   if (n < 0) {
2792     fatal(MYNAME ": %s\n", usb_strerror());
2793   }
2794   return n;
2795 }
2796 
2797 static unsigned
libusb_os_packet_write(const void * buf,unsigned size)2798 libusb_os_packet_write(const void* buf, unsigned size)
2799 {
2800   int n = usb_interrupt_write(usb_handle, endpoint_out, (char*)buf, size, 2000);
2801   if (n < 0) {
2802     fatal(MYNAME ": %s\n", usb_strerror());
2803   }
2804   return n;
2805 }
2806 
2807 #if HAVE_LINUX_HID
2808 static const delbin_os_ops_t libusb_os_ops =
2809 #else
2810 delbin_os_ops_t delbin_os_ops =
2811 #endif
2812 {
2813   libusb_os_init,
2814   libusb_os_deinit,
2815   libusb_os_packet_read,
2816   libusb_os_packet_write
2817 };
2818 
2819 #endif // HAVE_LIBUSB
2820 
2821 //-----------------------------------------------------------------------------
2822 // Linux
2823 #if HAVE_LINUX_HID
2824 
2825 #include <unistd.h>
2826 #include <fcntl.h>
2827 #include <dirent.h>
2828 #include <errno.h>
2829 #include <sys/ioctl.h>
2830 #include <linux/types.h>
2831 #include <linux/hiddev.h>
2832 #include <linux/hidraw.h>
2833 
2834 // Read from hidraw, write to hiddev. Reading from hiddev does not work,
2835 // and neither does writing to hidraw.
2836 static int fd_hidraw;
2837 static int fd_hiddev;
2838 
2839 static int linuxhid_os_init_status;
2840 
2841 static void
linuxhid_os_init(const char * fname)2842 linuxhid_os_init(const char* fname)
2843 {
2844   struct hidraw_devinfo info;
2845   struct hiddev_field_info finfo;
2846   DIR* dir = NULL;
2847   struct dirent* d;
2848 
2849   fd_hidraw = fd_hiddev = -1;
2850   if (fname && memcmp(fname, "hid:", 4) == 0) {
2851     char* raw_name = xstrdup(fname + 4);
2852     char* dev_name = strchr(raw_name, ',');
2853     if (dev_name == NULL) {
2854       fatal(MYNAME ": missing hiddev path\n");
2855     }
2856     *dev_name++ = 0;
2857     fd_hidraw = open(raw_name, O_RDONLY);
2858     if (fd_hidraw < 0) {
2859       fatal(MYNAME ": %s: %s\n", raw_name, strerror(errno));
2860     }
2861     fd_hiddev = open(dev_name, O_WRONLY);
2862     if (fd_hiddev < 0) {
2863       fatal(MYNAME ": %s: %s\n", dev_name, strerror(errno));
2864     }
2865     xfree(raw_name);
2866   } else {
2867     dir = opendir("/dev");
2868   }
2869   while (dir && (d = readdir(dir)) != NULL) {
2870     if (strncmp(d->d_name, "hidraw", 6) == 0) {
2871       int fd1, fd2;
2872       char raw_name[32];
2873       char dev_name[32];
2874       sprintf(raw_name, "/dev/%s", d->d_name);
2875       fd1 = open(raw_name, O_RDONLY);
2876       if (fd1 < 0) {
2877         if (global_opts.debug_level >= DBGLVL_M) {
2878           warning(MYNAME ": %s: %s\n", raw_name, strerror(errno));
2879         }
2880         continue;
2881       }
2882       sprintf(dev_name, "/dev/usb/hiddev%s", raw_name + sizeof("/dev/hidraw") - 1);
2883       fd2 = open(dev_name, O_WRONLY);
2884       if (fd2 < 0 && errno == ENOENT) {
2885         sprintf(dev_name, "/dev/hiddev%s", raw_name + sizeof("/dev/hidraw") - 1);
2886         fd2 = open(dev_name, O_WRONLY);
2887       }
2888       if (fd2 < 0) {
2889         if (global_opts.debug_level >= DBGLVL_M) {
2890           warning(MYNAME ": %s: %s\n", dev_name, strerror(errno));
2891         }
2892         close(fd1);
2893         continue;
2894       }
2895       if (ioctl(fd1, HIDIOCGRAWINFO, &info) == 0 &&
2896           info.vendor == VENDOR_ID && info.product == PRODUCT_ID) {
2897         fd_hidraw = fd1;
2898         fd_hiddev = fd2;
2899         break;
2900       }
2901       close(fd1);
2902       close(fd2);
2903     }
2904   }
2905   if (dir) {
2906     closedir(dir);
2907   }
2908   if (fd_hidraw < 0) {
2909     if (linuxhid_os_init_status == 0) {
2910       fatal(MYNAME ": no DeLorme PN found\n");
2911     }
2912     return;
2913   }
2914   finfo.report_type = HID_REPORT_TYPE_INPUT;
2915   finfo.report_id = 0;
2916   finfo.field_index = 0;
2917   if (ioctl(fd_hiddev, HIDIOCGFIELDINFO, &finfo) < 0) {
2918     warning(MYNAME ": HIDIOCGFIELDINFO: %s\n", strerror(errno));
2919     if (linuxhid_os_init_status == 0) {
2920       exit(1);
2921     }
2922     return;
2923   }
2924   delbin_os_packet_size = finfo.maxusage;
2925   linuxhid_os_init_status = 0;
2926 }
2927 
2928 static void
linuxhid_os_deinit(void)2929 linuxhid_os_deinit(void)
2930 {
2931   close(fd_hidraw);
2932   close(fd_hiddev);
2933 }
2934 
2935 static unsigned
linuxhid_os_packet_read(void * buf)2936 linuxhid_os_packet_read(void* buf)
2937 {
2938   int n = read(fd_hidraw, buf, delbin_os_packet_size);
2939   if (n < 0) {
2940     fatal(MYNAME ": %s\n", strerror(errno));
2941   }
2942   return n;
2943 }
2944 
2945 static unsigned
linuxhid_os_packet_write(const void * buf,unsigned size)2946 linuxhid_os_packet_write(const void* buf, unsigned size)
2947 {
2948   struct hiddev_usage_ref_multi urefm;
2949   struct hiddev_report_info rinfo;
2950   const gbuint8* p = buf;
2951   unsigned i;
2952 
2953   for (i = 0; i < size; i++) {
2954     urefm.values[i] = p[i];
2955   }
2956   urefm.num_values = size;
2957   memset(&urefm.uref, 0, sizeof(urefm.uref));
2958   urefm.uref.report_type = HID_REPORT_TYPE_OUTPUT;
2959   if (ioctl(fd_hiddev, HIDIOCSUSAGES, &urefm)) {
2960     fatal(MYNAME ": HIDIOCSUSAGES: %s\n", strerror(errno));
2961   }
2962   memset(&rinfo, 0, sizeof(rinfo));
2963   rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
2964   if (ioctl(fd_hiddev, HIDIOCSREPORT, &rinfo)) {
2965     fatal(MYNAME ": HIDIOCSREPORT: %s\n", strerror(errno));
2966   }
2967   return size;
2968 }
2969 
2970 static const delbin_os_ops_t linuxhid_os_ops = {
2971   linuxhid_os_init,
2972   linuxhid_os_deinit,
2973   linuxhid_os_packet_read,
2974   linuxhid_os_packet_write
2975 };
2976 
2977 static void
linux_os_init(const char * fname)2978 linux_os_init(const char* fname)
2979 {
2980   // tell linuxhid_os_init not to exit
2981   linuxhid_os_init_status = 1;
2982   linuxhid_os_init(fname);
2983   if (linuxhid_os_init_status == 0) {
2984     delbin_os_ops = linuxhid_os_ops;
2985   } else {
2986 #if HAVE_LIBUSB
2987     if (global_opts.debug_level >= DBGLVL_M) {
2988       warning(MYNAME ": HID init failed, falling back to libusb\n");
2989     }
2990     delbin_os_ops = libusb_os_ops;
2991     delbin_os_ops.init(fname);
2992 #else
2993     fatal(MYNAME ": no DeLorme PN found\n");
2994 #endif
2995   }
2996 }
2997 
2998 delbin_os_ops_t delbin_os_ops = {
2999   linux_os_init,
3000   NULL,
3001   NULL,
3002   NULL
3003 };
3004 
3005 #endif // HAVE_LINUX_HID
3006 
3007 //-----------------------------------------------------------------------------
3008 // stubs
3009 #if !(HAVE_WDK || __APPLE__ || HAVE_LIBUSB || HAVE_LINUX_HID)
3010 static void
stub_os_init(const char * fname)3011 stub_os_init(const char* fname)
3012 {
3013   fatal(MYNAME ": OS not supported\n");
3014 }
3015 static void
stub_os_deinit(void)3016 stub_os_deinit(void)
3017 {
3018 }
3019 static unsigned
stub_os_packet_read(void * buf)3020 stub_os_packet_read(void* buf)
3021 {
3022   return 0;
3023 }
3024 static unsigned
stub_os_packet_write(const void * buf,unsigned size)3025 stub_os_packet_write(const void* buf, unsigned size)
3026 {
3027   return 0;
3028 }
3029 delbin_os_ops_t delbin_os_ops = {
3030   stub_os_init,
3031   stub_os_deinit,
3032   stub_os_packet_read,
3033   stub_os_packet_write
3034 };
3035 #endif
3036 // end OS device I/O implementations section
3037 //=============================================================================
3038 
3039 static const int track_color_bgr[] = {
3040   0x0000ff, // red
3041   0x00ffff, // yellow
3042   0x008000, // green
3043   0xff0000, // blue
3044   0x808080, // gray
3045   0xffffff, // white
3046   0,        // black
3047   0xffff00, // cyan
3048   0xff00ff, // magenta
3049   0x00a5ff, // orange
3050   0x82004b, // indigo
3051   0xeea5ee  // violet
3052 };
3053 
track_color(unsigned i)3054 static int track_color(unsigned i)
3055 {
3056   int bgr = -1;
3057   if (i < sizeofarray(track_color_bgr)) {
3058     bgr = track_color_bgr[i];
3059   }
3060   return bgr;
3061 }
3062 
track_color_index(int bgr)3063 static unsigned track_color_index(int bgr)
3064 {
3065   unsigned i = sizeofarray(track_color_bgr);
3066   do {
3067     i--;
3068   } while (i != 0 && track_color_bgr[i] != bgr);
3069   return i;
3070 }
3071 
3072 static const char* const waypoint_symbol_name[] = {
3073   // 0
3074   "Red Map Pin",
3075   "Dark Red Map Pin",
3076   "Yellow Map Pin",
3077   "Dark Yellow Map Pin",
3078   "Green Map Pin",
3079   "Dark Green Map Pin",
3080   "Turquoise Map Pin",
3081   "Dark Turquoise Map Pin",
3082   "Blue Map Pin",
3083   "Dark Blue Map Pin",
3084   // 10
3085   "Gray Map Pin",
3086   "Dark Gray Map Pin",
3087   "Red Flag",
3088   "Dark Red Flag",
3089   "Yellow Flag",
3090   "Dark Yellow Flag",
3091   "Green Flag",
3092   "Dark Green Flag",
3093   "Turquoise Flag",
3094   "Dark Turquoise Flag",
3095   // 20
3096   "Blue Flag",
3097   "Dark Blue Flag",
3098   "Gray Flag",
3099   "Dark Gray Flag",
3100   "Red Dot",
3101   "Dark Red Dot",
3102   "Yellow Dot",
3103   "Dark Yellow Dot",
3104   "Green Dot",
3105   "Dark Green Dot",
3106   // 30
3107   "Turquoise Dot",
3108   "Dark Turquoise Dot",
3109   "Blue Dot",
3110   "Dark Blue Dot",
3111   "Gray Dot",
3112   "Dark Gray Dot",
3113   "Small Red Dot",
3114   "Small Dark Red Dot",
3115   "Small Yellow Dot",
3116   "Small Dark Yellow Dot",
3117   // 40
3118   "Small Green Dot",
3119   "Small Dark Green Dot",
3120   "Small Turquoise Dot",
3121   "Small Dark Turquoise Dot",
3122   "Small Blue Dot",
3123   "Small Dark Blue Dot",
3124   "Small Gray Dot",
3125   "Small Dark Gray Dot",
3126   "Arrow Up",
3127   "Arrow Down",
3128   // 50
3129   "Arrow Left",
3130   "Arrow Right",
3131   "Arrow Up Left",
3132   "Arrow Up Right",
3133   "Arrow Down Left",
3134   "Arrow Down Right",
3135   "Green Star",
3136   "Yellow Square",
3137   "Red X",
3138   "Turquoise Circle",
3139   // 60
3140   "Purple Triangle",
3141   "American Flag",
3142   "Stop",
3143   "Parking",
3144   "First Aid",
3145   "Dining",
3146   "Railroad Crossing",
3147   "Heliport",
3148   "Restroom",
3149   "Information",
3150   // 70
3151   "Diver Down",
3152   "Exit",
3153   "Health Facility",
3154   "Police",
3155   "Post Office",
3156   "Mining",
3157   "Danger",
3158   "Money",
3159   "Exclamation",
3160   "Car",
3161   // 80
3162   "Jeep",
3163   "Truck",
3164   "Tow Truck",
3165   "Motor Home",
3166   "School Bus",
3167   "Four-wheeler",
3168   "Snowmobile",
3169   "Sailboat",
3170   "Powerboat",
3171   "Boat Launch",
3172   // 90
3173   "Anchor",
3174   "Buoy",
3175   "Shipwreck",
3176   "Glider Area",
3177   "Private Airport",
3178   "Public Airport",
3179   "Military Airport",
3180   "Military Base",
3181   "House",
3182   "Church",
3183   // 100
3184   "Building",
3185   "School",
3186   "Lighthouse",
3187   "Bridge",
3188   "Radio Tower",
3189   "Dam",
3190   "Tunnel",
3191   "Toll Booth",
3192   "Gas Station",
3193   "Lodging",
3194   // 110
3195   "Telephone",
3196   "Traffic Light",
3197   "Fire Hydrant",
3198   "Cemetery",
3199   "Picnic Table",
3200   "Tent",
3201   "Shelter",
3202   "Camper",
3203   "Fire",
3204   "Shower",
3205   // 120
3206   "Drinking Water",
3207   "Binoculars",
3208   "Camera",
3209   "Geocache",
3210   "Geocache Found",
3211   "Fishing Pole",
3212   "Ice Fishing Trap Set",
3213   "Ice Fishing Trap Up",
3214   "Moose",
3215   "Deer",
3216   // 130
3217   "Bear",
3218   "Bird",
3219   "Duck",
3220   "Fish",
3221   "Deer Tracks",
3222   "Animal Tracks",
3223   "Bird Tracks",
3224   "Birch Tree",
3225   "Evergreen Tree",
3226   "Deciduous Tree",
3227   // 140
3228   "Flower Garden",
3229   "Mountain",
3230   "Cave",
3231   "Beach",
3232   "Hiking",
3233   "Swimming",
3234   "Bicycling",
3235   "Kayaking",
3236   "Canoeing",
3237   "Water Skiing",
3238   // 150
3239   "Cross-country Skiing",
3240   "Downhill Skiing",
3241   "Ice Skating",
3242   "Dogsledding",
3243   "Shooting",
3244   "Golf Course",
3245   "Ballpark",
3246   // 157-182 added in PN-40 2.5 firmware
3247   "Cache Found",
3248   "Didn't Find It",
3249   "My Cache",
3250   // 160
3251   "Traditional Cache",
3252   "Multi-Cache",
3253   "Unknown Cache",
3254   "Letterbox Hybrid",
3255   "Whereigo Cache",
3256   "Event Cache",
3257   "Mega-Event Cache",
3258   "Cache In Trash Out Event",
3259   "EarthCache",
3260   "Virtual Cache",
3261   // 170
3262   "Webcam Cache",
3263   "Waymark",
3264   "NGS Benchmark",
3265   "Write Note",
3266   "Needs Maintenance",
3267   "Final Location",
3268   "Parking Area",
3269   "Question to Answer",
3270   "Reference Point",
3271   "Stages of a Multicache",
3272   // 180
3273   "Trailhead",
3274   "Temporarily Disable Listing",
3275   "Enable Listing",
3276   // 183-222 added in PN-40 2.7 firmware
3277   "Crane Truck",
3278   "Forest Fire",
3279   "Oil Derrick",
3280   "Wind Turbine",
3281   "Letter A",
3282   "Letter B",
3283   "Letter C",
3284   // 190
3285   "Letter D",
3286   "Letter E",
3287   "Letter F",
3288   "Letter G",
3289   "Letter H",
3290   "Letter I",
3291   "Letter J",
3292   "Letter K",
3293   "Letter L",
3294   "Letter M",
3295   // 200
3296   "Letter N",
3297   "Letter O",
3298   "Letter P",
3299   "Letter Q",
3300   "Letter R",
3301   "Letter S",
3302   "Letter T",
3303   "Letter U",
3304   "Letter V",
3305   "Letter W",
3306   // 210
3307   "Letter X",
3308   "Letter Y",
3309   "Letter Z",
3310   "Numeral 0",
3311   "Numeral 1",
3312   "Numeral 2",
3313   "Numeral 3",
3314   "Numeral 4",
3315   "Numeral 5",
3316   "Numeral 6",
3317   // 220
3318   "Numeral 7",
3319   "Numeral 8",
3320   "Numeral 9"
3321 };
3322 
3323 static const char*
waypoint_symbol(unsigned i)3324 waypoint_symbol(unsigned i)
3325 {
3326   const char* p = NULL;
3327   if (i < sizeofarray(waypoint_symbol_name)) {
3328     p = waypoint_symbol_name[i];
3329   }
3330   return p;
3331 }
3332 
3333 static unsigned
waypoint_symbol_index(const char * name)3334 waypoint_symbol_index(const char* name)
3335 {
3336   static unsigned last_result;
3337   static char last_name[32];
3338   unsigned i = last_result;
3339 
3340   if (strncmp(name, last_name, sizeof(last_name)) != 0) {
3341     i = sizeofarray(waypoint_symbol_name);
3342     do {
3343       i--;
3344     } while (i != 0 && case_ignore_strcmp(name, waypoint_symbol_name[i]) != 0);
3345     strncpy(last_name, name, sizeof(last_name));
3346     last_result = i;
3347   }
3348   return i;
3349 }
3350 
3351 // vi: ts=4 sw=4 noexpandtab
3352