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