1 /*
2     National Geographic Topo! TPO file support.
3     2.x support contributed to gpsbabel by Steve Chamberlin.
4     3.x support contributed to gpsbabel by Curt Mills.
5 
6     Topo! version 2.x:  Tracks are implemented.
7     Topo! version 3.x:  Reading of Tracks/Waypoints/Routes is
8                         implemented.  Also extracts Map Notes/
9                         Symbols/Text Labels as Waypoints.
10 
11     Copyright (C) 2005 Steve Chamberlin, slc at alum.mit.edu
12     Portions Copyright (C) 2006 Curtis E. Mills, archer at eskimo dot com
13 
14     This program is free software; you can redistribute it and/or modify
15     it under the terms of the GNU General Public License as published by
16     the Free Software Foundation; either version 2 of the License, or
17     (at your option) any later version.
18 
19     This program is distributed in the hope that it will be useful,
20     but WITHOUT ANY WARRANTY; without even the implied warranty of
21     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22     GNU General Public License for more details.
23 
24     You should have received a copy of the GNU General Public License
25     along with this program; if not, write to the Free Software
26     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
27 
28  */
29 
30 
31 /*
32  ***** This format has problems and the author hasn't returned emails
33  * on it, so we've set the format type to 'ff_type_internal' to make it
34  * disappear from the various lists...
35  */
36 
37 /*
38  TPO format notes:
39  -----------------
40  Most of the ASCII strings embedded in the text will have a
41  byte-count prepended to the string.  Unknown yet whether other
42  fields have this same byte-count, but so far it doesn't look like
43  it.
44 
45  New format (3.x and later) files begin with a string byte-count
46  byte and then a string starting with "TOPO! Ver. 3.", like "TOPO!
47  Ver.  3.3.4".  Can contain routes/tracks/waypoints, embedded
48  images, Map Notes, Symbols, Text Labels, Compass symbols, and
49  several others.
50 
51  Older (pre-3.0) format does not have the above string.  Contains
52  only tracks.  Waypoints are saved in a separate .TPG file.
53 
54  May contain these strings:
55     Frmt:   String:
56     -----   --------------------------------
57     2.x     "CTopoAzimuth"
58     2.x     "CTopoBookmark"
59     2.x     "CTopoGpsRoute".  Saved in .tpg files (see tpg.c)
60     2.x     "CTopoRoute".  The actual tracks we parse here.
61     2.x     "CTopoSymbol"
62     2.x/3.x "CTopoText"
63     2.x     "CTopoWaypoint".  Saved in .tpg files (see tpg.c)
64     3.x     "Notes"
65     3.x     "PNG."  Embedded PNG image containing 2 rows of 40
66               symbols each.  Starts with signature: 89 50 4e 47 0d
67               0a 1a 0a, ends with signature 49 45 4e 44 ae 42 60 82.
68     3.x     "shapes"
69     3.x     "arrows"
70     3.x     "recreation"
71 */
72 
73 #include "defs.h"
74 #include <string.h>
75 #include <ctype.h>
76 #include "jeeps/gpsmath.h" /* for datum conversions */
77 
78 #define MYNAME	"TPO"
79 
80 static char* dumpheader = NULL;
81 static char* output_state = NULL;
82 
83 /*
84 static
85 arglist_t tpo2_args[] = {
86 	{ "dumpheader", &dumpheader, "Display the file header bytes",
87 		"0", ARGTYPE_BOOL, ARG_NOMINMAX} ,
88 	{ "state", &output_state, "State map format to write, default=CA",
89 	  "CA", ARGTYPE_STRING, ARG_NOMINMAX} ,
90 	ARG_TERMINATOR
91 };
92 */
93 //
94 // Note that we've disabled the write capabilites for the tpo2
95 // format at present.  The "testo" tests were failing on some
96 // platforms and there wasn't anyone willing to work on the problem.
97 // If this is fixed in the future we can go back to the tpo2_args[]
98 // above.
99 //
100 static
101 arglist_t tpo2_args[] = {
102   ARG_TERMINATOR
103 };
104 
105 static
106 arglist_t tpo3_args[] = {
107   ARG_TERMINATOR
108 };
109 
110 
111 static gbfile* tpo_file_in;
112 static gbfile* tpo_file_out;
113 //static short_handle mkshort_handle;
114 
115 static double output_track_lon_scale;
116 static double output_track_lat_scale;
117 
118 static unsigned int track_out_count;
119 static double first_track_waypoint_lat;
120 static double first_track_waypoint_lon;
121 static double track_length;
122 static double last_waypoint_x;
123 static double last_waypoint_y;
124 static double last_waypoint_z;
125 
126 /*******************************************************************************/
127 /*                                      READ                                   */
128 /*******************************************************************************/
129 
130 /* Define a global here that we can query from multiple places */
131 float tpo_version = 0.0;
132 
133 /* tpo_check_version_string()
134    Check the first bytes of the file for a version 3.0 header. */
135 static void
tpo_check_version_string()136 tpo_check_version_string()
137 {
138 
139   unsigned char string_size;
140   char* string_buffer;
141   char* v3_id_string = "TOPO! Ver";
142 
143   /* read the id string */
144   gbfread(&string_size, 1, 1, tpo_file_in);
145   string_buffer = (char*) xmalloc(string_size+1);
146   gbfread(string_buffer, 1, string_size, tpo_file_in);
147 
148   /* terminate the string */
149   string_buffer[string_size] = 0;
150 
151   /* check for the presence of a 3.0-style id string */
152   if (strncmp(v3_id_string, string_buffer, strlen(v3_id_string)) == 0) {
153     /*		fatal(MYNAME ": gpsbabel can only read TPO version 2.7.7 or below; this file is %s\n", string_buffer); */
154 //fprintf(stderr,"gpsbabel can only read TPO version 2.7.7 or below; this file is %s\n", string_buffer);
155 
156     gbfseek(tpo_file_in, -(string_size+1), SEEK_CUR);
157     xfree(string_buffer);
158     tpo_version = 3.0;  /* Really any 3.x version */
159     return;
160 
161   } else {
162     /* We found a version 1.x or 2.x file */
163     /* seek back to the beginning of the file */
164     gbfseek(tpo_file_in, -(string_size+1), SEEK_CUR);
165     xfree(string_buffer);
166     tpo_version = 2.0;  /* Really any 1.x or 2.x version */
167     return;
168   }
169 }
170 
171 static void
172 /* tpo_dump_header_bytes(int header_size)
173    Write the first header_size bytes of the file to standard output
174    as a C array definition. */
tpo_dump_header_bytes(int header_size)175 tpo_dump_header_bytes(int header_size)
176 {
177   int i;
178   unsigned char* buffer = (unsigned char*) xmalloc(header_size);
179 
180   gbfread(buffer, 1, header_size, tpo_file_in);
181 
182   printf("unsigned char header_bytes[] = {\n");
183 
184   for (i=0; i<header_size; i++) {
185     if (i%8 == 0) {
186       printf("    ");
187     }
188     printf("0x%02X", buffer[i]);
189     if (i != header_size-1) {
190       printf(", ");
191     }
192     if (i%8 == 7) {
193       printf("\n");
194     }
195   }
196 
197   printf("};\n");
198 
199   xfree(buffer);
200 }
201 
202 /* tpo_read_until_section()
203    Keep reading bytes from the file until the section name is encountered,
204    then go seek_bytes forwards (+) or backwards (-) to the start of
205    the section data. */
206 static void
tpo_read_until_section(const char * section_name,int seek_bytes)207 tpo_read_until_section(const char* section_name, int seek_bytes)
208 {
209   char byte;
210   unsigned int match_index = 0;
211   int header_size = 0;
212 
213   while (1) {
214     if (gbfread(&byte, 1, 1, tpo_file_in) < 1) {
215       fatal(MYNAME ": malformed input file - attempt to read past end");
216     }
217     header_size++;
218 
219     if (byte == section_name[match_index]) {
220       match_index++;
221       if (match_index == strlen(section_name)) {
222         /*fprintf(stderr,"Found %s\n", section_name);*/
223         gbfseek(tpo_file_in, seek_bytes, SEEK_CUR);
224         header_size += seek_bytes;
225 
226         if (dumpheader && dumpheader[0] == '1') {
227           gbfseek(tpo_file_in, -header_size, SEEK_CUR);
228           tpo_dump_header_bytes(header_size);
229         }
230         return;
231 
232       }
233     } else {
234       match_index = 0;
235     }
236   }
237 }
238 
239 static void
tpo_rd_init(const char * fname)240 tpo_rd_init(const char* fname)
241 {
242 
243   tpo_file_in = gbfopen_le(fname, "rb", MYNAME);
244   tpo_check_version_string();
245 
246   if (tpo_version == 2.0) {
247     if (doing_wpts || doing_rtes) {
248       fatal(MYNAME ": this file format only supports tracks, not waypoints or routes.\n");
249     }
250 
251     /*fprintf(stderr,"Version 2.x, Looking for CTopoRoute\n"); */
252     /* Back up 18 bytes if this section found */
253     tpo_read_until_section("CTopoRoute", -18);
254   } else if (tpo_version == 3.0) {
255     /*fprintf(stderr,"Version 3.x, Looking for 'Red Without Arrow'\n"); */
256     /* Go forward four more bytes if this section found.  "IEND"
257      * plus four bytes is the end of the embedded PNG image */
258     tpo_read_until_section("Red Without Arrow", 17);
259   } else {
260     fatal(MYNAME ": gpsbabel can only read TPO versions through 3.x.x\n");
261   }
262 }
263 
264 static void
tpo_rd_deinit(void)265 tpo_rd_deinit(void)
266 {
267   gbfclose(tpo_file_in);
268 }
269 
270 
271 
272 
273 
274 //-------------------------------------------------------------------
275 //-------------------------------------------------------------------
276 
277 
278 
279 
280 
281 // Decoder for version 2.x files.  This one does tracks only, as
282 // that is the only type of data available in the version 2.x TPO
283 // files.
284 //
tpo_read_2_x(void)285 void tpo_read_2_x(void)
286 {
287   char buff[16];
288   short track_count, waypoint_count;
289   double first_lat, first_lon, lat_scale, lon_scale, amt;
290   short* lon_delta, *lat_delta;
291   int i, j;
292   route_head* track_temp;
293   waypoint* waypoint_temp;
294 
295   /* track count */
296   track_count = gbfgetint16(tpo_file_in);
297 
298   /*fprintf(stderr,"track_count:%d\n", track_count);*/
299 
300   /* 4 unknown bytes */
301   gbfread(&buff[0], 1, 4, tpo_file_in);
302 
303   /* chunk name: "CTopoRoute" */
304   gbfread(&buff[0], 1, 12, tpo_file_in);
305 
306   for (i=0; i<track_count; i++) {
307 
308     track_temp = route_head_alloc();
309     track_add_head(track_temp);
310 
311     /* generate a generic track name */
312     sprintf(buff, "Track %d", i+1);
313     track_temp->rte_name = xstrdup(buff);
314 
315     /* zoom level 1-5 visibility flags */
316     gbfread(&buff[0], 1, 10, tpo_file_in);
317 
318     /* 8 bytes of zeros, meaning unknown */
319     gbfread(&buff[0], 1, 8, tpo_file_in);
320 
321     /* 4 more unknown bytes, possibly sign flags for the longitude and latitude? */
322     gbfread(&buff[0], 1, 4, tpo_file_in);
323 
324     /* read the position of the initial track point */
325     /* for some very odd reason, signs on longitude are swapped */
326     /* coordinates are in NAD27/CONUS datum                     */
327 
328     /* 8 bytes - longitude, sign swapped  */
329     first_lon = gbfgetdbl(tpo_file_in);
330 
331     /* 8 bytes - latitude */
332     first_lat = gbfgetdbl(tpo_file_in);
333 
334     /* swap sign before we do datum conversions */
335     first_lon *= -1.0;
336 
337     /* 8 unknown bytes: seems to be some kind of bounding box info */
338     gbfread(&buff[0], 1, 8, tpo_file_in);
339 
340     /* number of route points */
341     waypoint_count = gbfgetint16(tpo_file_in);
342 
343     /* allocate temporary memory for the waypoint deltas */
344     lon_delta = (short*) xmalloc(waypoint_count * sizeof(short));
345     lat_delta = (short*) xmalloc(waypoint_count * sizeof(short));
346 
347     for (j=0; j<waypoint_count; j++) {
348 
349       /* get this point's longitude delta from the first waypoint */
350       lon_delta[j] = gbfgetint16(tpo_file_in);
351 
352       /* get this point's latitude delta from the first waypoint */
353       lat_delta[j] = gbfgetint16(tpo_file_in);
354     }
355 
356     /* 8 bytes - longitude delta to degrees scale  */
357     lon_scale = gbfgetdbl(tpo_file_in);
358 
359     /* 8 bytes - latitude delta to degrees scale */
360     lat_scale = gbfgetdbl(tpo_file_in);
361 
362     /* 4 bytes: the total length of the route in feet*/
363     gbfread(&buff[0], 1, 4, tpo_file_in);
364 
365     /* 2 unknown bytes */
366     gbfread(&buff[0], 1, 2, tpo_file_in);
367 
368     /* 2 bytes: continuation marker */
369     gbfread(&buff[0], 1, 2, tpo_file_in);
370 
371     /* multiply all the deltas by the scaling factors to determine the waypoint positions */
372     for (j=0; j<waypoint_count; j++) {
373 
374       waypoint_temp = waypt_new();
375 
376       /* convert incoming NAD27/CONUS coordinates to WGS84 */
377       GPS_Math_Known_Datum_To_WGS84_M(
378         first_lat-lat_delta[j]*lat_scale,
379         first_lon+lon_delta[j]*lon_scale,
380         0.0,
381         &waypoint_temp->latitude,
382         &waypoint_temp->longitude,
383         &amt,
384         78);
385 
386       /* there is no elevation data for the waypoints */
387       waypoint_temp->altitude = 0;
388 
389       track_add_wpt(track_temp, waypoint_temp);
390     }
391 
392     /* free temporary memory */
393     xfree(lon_delta);
394     xfree(lat_delta);
395   }
396 }
397 
398 
399 
400 
401 
402 //-------------------------------------------------------------------
403 //-------------------------------------------------------------------
404 
405 // This will read 8/16/32 bits in little-endian format depending
406 // upon the value of the first byte.
407 //
408 // For version 3.x files.
409 //
tpo_read_int()410 int tpo_read_int()
411 {
412   unsigned char val;
413 
414   val = (unsigned char) gbfgetc(tpo_file_in);
415 
416   switch (val) {
417 
418   case 0xff:  // 32-bit value
419 //printf("Found 32-bit value indicator: %x\n", val);
420     return(gbfgetint32(tpo_file_in));
421     break;
422 
423   case 0xfe:  // 16-bit value
424 //printf("Found 16-bit value indicator: %x\n", val);
425     return(gbfgetuint16(tpo_file_in));
426     break;
427 
428   default:    // 8-bit value
429 //printf("Found 8-bit value: %x\n", val);
430     return((int)val);
431     break;
432   }
433 }
434 
435 
436 
437 
438 
439 // Find variable block of interest in the file.  Leaves the file
440 // pointer pointing after the two 4-byte type/pointer bytes.  The
441 // file pointer should be pointing at the first data byte of the
442 // block after calling this, which is normally an 8/16/32-bit value
443 // specifying the number of elements in the block.
444 //
445 // Returns -1 if block not found
446 //          0 if block found
447 //
448 // For version 3.x files.
449 //
tpo_find_block(unsigned int block_desired)450 int tpo_find_block(unsigned int block_desired)
451 {
452   int block_type;
453   int block_offset;
454 
455 
456   // Skip 512 byte fixed-length header
457   block_offset = 512;
458 
459   do {
460 
461     // Seek to offset from start of file
462     gbfseek(tpo_file_in, block_offset, SEEK_SET);
463 
464     // Read record type
465     block_type = gbfgetint32(tpo_file_in);
466 // printf("Block: %08x\tat offset: %08x\n", block_type, block_offset);
467 
468     // Read offset to next record
469     block_offset = gbfgetint32(tpo_file_in);
470   } while (block_type != block_desired && block_offset != 0);
471 
472   if (block_type == block_desired) {
473     return(0);
474   } else {
475     return(-1);
476   }
477 }
478 
479 
480 
481 
482 
483 // Convert lat/long to normal values, save in waypoint struct
484 //
485 // For version 3.x files.
486 //
tpo_convert_ll(int lat,int lon)487 waypoint* tpo_convert_ll(int lat, int lon)
488 {
489   double latitude;
490   double longitude;
491   waypoint* waypoint_temp;
492 
493 
494   waypoint_temp = waypt_new();
495 
496   latitude = (double)lat / 0x800000;
497   longitude = (double)lon / 0x800000;
498 
499 //printf("lat: %f\tlon: %f\n", latitude, longitude);
500 
501   /*
502       // Note:  We shouldn't need this section of code as the version
503       // 3.x files are already in WGS84 datum.
504       //
505       // Convert incoming NAD27/CONUS coordinates to WGS84, Molodensky
506       // transform.
507       //
508       GPS_Math_Known_Datum_To_WGS84_M(
509           latitude,                   // Source latitude
510           longitude,                  // Source longitude
511           0.0,                        // Source height (meters)
512           &waypoint_temp->latitude,   // Dest latitude
513           &waypoint_temp->longitude,  // Dest longitude
514           &height,                    // Dest height (meters)
515           78);
516   */
517 
518   waypoint_temp->latitude = latitude;
519   waypoint_temp->longitude = longitude;
520 
521 //printf("lat: %f\tlon: %f\tNew Height: %f\n", waypoint_temp->latitude, waypoint_temp->longitude, height);
522 
523   return(waypoint_temp);
524 }
525 
526 
527 
528 
529 
530 // Track decoder for version 3.x files.  This block contains tracks
531 // (called "freehand routes" or just "routes" in Topo).
532 //
tpo_process_tracks(void)533 void tpo_process_tracks(void)
534 {
535   unsigned int track_count, track_style_count;
536   unsigned int xx,ii,tmp;
537 #define TRACKNAMELENGTH 256
538 
539   int DEBUG=0;
540 
541   if (DEBUG) {
542     printf("Processing Track Styles... (added in 2012 by SRE)\n");
543   }
544   // Find block 0x050000 (definitions of styles for free-hand routes)
545   if (tpo_find_block(0x050000)) {
546     // printf("Found no track styles, skipping tracks\n");
547     return;
548   }
549   // Read the number of track styles.
550   track_style_count = tpo_read_int(); // 8 bit value
551 
552   if (DEBUG) {
553     printf("Unpacking %d track styles...\n",track_style_count);
554   }
555   char style_name[track_style_count][TRACKNAMELENGTH]; // some huge value
556   int style_color[track_style_count][3];  // keep R/G/B values separate because line_color needs BGR
557   int style_wide[track_style_count],style_dash[track_style_count];
558   for (ii = 0; ii < track_style_count; ii++) {
559 
560     // clumsy way to skip two undefined bytes
561     for (xx = 0; xx < 2; xx++) {
562       tmp = (unsigned char) gbfgetc(tpo_file_in);
563       // printf("Skipping (visibility?) byte 0x%x\n",tmp);
564     }
565 
566     // next three bytes are RGB color, fourth is unknown
567     // Topo and web uses rrggbb, also need line_color.bbggrr for KML
568     for (xx = 0; xx < 3; xx++) {
569       style_color[ii][xx] = (int) gbfgetc(tpo_file_in);
570       if((style_color[ii][xx] < 0) || (style_color[ii][xx] >255)) {
571         style_color[ii][xx] = 0; // assign black if out of range 0x00 to 0xff
572         // used to store strings: sprintf(style_color[ii], "%s%02x",style_color[ii],tmp);
573       }
574     }
575 
576     tmp = (unsigned char) gbfgetc(tpo_file_in);
577     // printf("Skipping unknown byte 0x%x after color\n",tmp);
578 
579     // byte for name length, then name
580     tmp = (unsigned char) gbfgetc(tpo_file_in);
581     // wrong byte order?? tmp = tpo_read_int(); // 16 bit value
582     // printf("Track %d has %d-byte (0x%x) name\n",ii,tmp,tmp);
583     if (tmp >= TRACKNAMELENGTH) {
584       printf("ERROR! Found track style over TRACKNAMELENGTH chars, skipping all tracks!\n");
585       return;
586     }
587     if (tmp) {
588       style_name[ii][0] = '\0';
589       gbfread(style_name[ii], 1, tmp, tpo_file_in);
590       style_name[ii][tmp] = '\0';  // Terminator
591     } else { // Assign a generic style name
592       sprintf(style_name[ii], "STYLE %d", ii);
593     }
594     for (xx = 0; xx < 3; xx++) {
595       if (style_name[ii][xx] == (char) ',') {
596         style_name[ii][xx] = (char) '_';
597       }
598       if (style_name[ii][xx] == (char) '=') {
599         style_name[ii][xx] = (char) '_';
600       }
601     }
602 
603     // one byte for line width (value 1-4), one byte for 'dashed' boolean
604     style_wide[ii] = (unsigned int) gbfgetc(tpo_file_in);
605     style_dash[ii] = (unsigned int) gbfgetc(tpo_file_in);
606 
607     // clumsy way to skip two undefined bytes
608     for (xx = 0; xx < 2; xx++) {
609       tmp = (unsigned char) gbfgetc(tpo_file_in);
610       // printf("Skipping final byte 0x%x\n",tmp);
611     }
612 
613     if (DEBUG) {
614       printf("Track style %d: color=#%02x%02x%02x, width=%d, dashed=%d, name=%s\n",ii,style_color[ii][0],style_color[ii][1],style_color[ii][2],style_wide[ii],style_dash[ii],style_name[ii]);
615     }
616   }
617 
618   if (DEBUG) {
619     printf("Processing Tracks... found %d track styles\n",track_style_count);
620   }
621 
622   // Find block 0x060000 (free-hand routes) (original track code, pre-2012, without styles)
623   if (tpo_find_block(0x060000)) {
624     return;
625   }
626 
627   // Read the number of tracks.  Can be 8/16/32-bit value.
628   track_count = tpo_read_int();
629 
630   if (DEBUG) {
631     printf("Total Tracks: %d\n", track_count);
632   }
633 
634   if (track_count == 0) {
635     return;
636   }
637 
638   // Read/process each track in the file
639   //
640   for (ii = 0; ii < track_count; ii++) {
641     unsigned int line_type;
642     unsigned int track_style;
643     unsigned int track_length;
644     unsigned int name_length;
645     char* track_name = NULL;
646     unsigned int track_byte_count;
647     int llvalid;
648     unsigned char* buf;
649     int lonscale;
650     int latscale;
651     int waypoint_count = 0;
652     int lat = 0;
653     int lon = 0;
654     unsigned int jj;
655     route_head* track_temp;
656     char rgb[7],bgr[7];
657     int bbggrr = 0;
658 
659     // Allocate the track struct
660     track_temp = route_head_alloc();
661     track_add_head(track_temp);
662 
663 //UNKNOWN DATA LENGTH
664     line_type = tpo_read_int(); // always zero??
665 
666     // Can be 8/16/32-bit value (defined in 2012, ignored before then)
667     track_style = tpo_read_int(); // index into freehand route styles defined in this .tpo file
668     track_style -= 1;  // STARTS AT 1, whereas style arrays start at 0
669 
670     // Can be 8/16/32-bit value - never used?
671     track_length = tpo_read_int();
672 
673 //UNKNOWN DATA LENGTH
674     name_length = tpo_read_int();
675 
676     if (name_length) {
677       track_name = (char*) xmalloc(name_length+1);
678       track_name[0] = '\0';
679       gbfread(track_name, 1, name_length, tpo_file_in);
680       track_name[name_length] = '\0';  // Terminator
681     } else { // Assign a generic track name
682       xasprintf(&track_name, "TRK %d", ii+1);
683     }
684     track_temp->rte_name = track_name;
685 
686     // RGB line_color expressed for html=rrggbb and kml=bbggrr - not assigned before 2012
687     sprintf(rgb,"%02x%02x%02x",style_color[track_style][0],style_color[track_style][1],style_color[track_style][2]);
688     sprintf(bgr,"%02x%02x%02x",style_color[track_style][2],style_color[track_style][1],style_color[track_style][0]);
689     sscanf(bgr,"%06x",&bbggrr); // hex string to integer - probably not the best way to do style_color to bbggrr
690     track_temp->line_color.bbggrr = bbggrr;
691 
692     // track texture (dashed=1, solid=0) mapped into opacity - not assigned before 2012
693     track_temp->line_color.opacity = 0xff;   // 255
694     if(style_dash[track_style]) {
695       track_temp->line_color.opacity = 0x50;
696     }
697 
698     // track width, from 1=hairline to 4=thick in Topo - not assigned before 2012
699     //  (what are correct values for KML or other outputs??)
700     track_temp->line_width = style_wide[track_style];
701 
702     if (DEBUG) printf("Track Name: %s, ?Type?: %d, Style Name: %s, Width: %d, Dashed: %d, Color: #%s\n",
703                         track_name, line_type, style_name[track_style], style_wide[track_style], style_dash[track_style],rgb);
704 
705     // Track description
706     // track_temp->rte_desc = NULL; // pre-2012 default, next line from SRE saves track style as track description
707     xasprintf(&track_temp->rte_desc, "Style=%s, Width=%d, Dashed=%d, Color=#%s",
708             style_name[track_style], style_wide[track_style], style_dash[track_style], rgb);
709 
710     // Route number
711     track_temp->rte_num = ii+1;
712 
713 //UNKNOWN DATA LENGTH
714     track_byte_count = tpo_read_int();
715 
716     // Read the number of bytes specified for the track.  These
717     // contain scaling factors, long/lat's, and offsets from
718     // those long/lat's.  First will be a long/lat (8 bytes).
719     // Keep track of the bytes as we go so that we know how many
720     // we've read.  We need to do this so that we start at the
721     // proper place for the next track.
722 
723     // Read the track bytes into a buffer
724     buf = (unsigned char*) xmalloc(track_byte_count);
725     gbfread(buf, 1, track_byte_count, tpo_file_in);
726 
727     latscale=0;
728     lonscale=0;
729 
730     // Process the track bytes
731     llvalid = 0;
732     for (jj = 0; jj < track_byte_count;) {
733       waypoint* waypoint_temp;
734 
735       // Time to read a new latlong?
736       if (!llvalid) {
737 
738         lon = le_read32(buf+jj);
739         jj+=4;
740 
741         lat = le_read32(buf+jj);
742         jj+=4;
743 
744 //printf("L");
745 
746         // Peek to see if next is a lonscale.  Note that it
747         // can begin with 0x88, which is confusing.  Here we
748         // allow up to 16-bits of offset, so two of the
749         // bytes must be 0x00 for us to recognize it.
750         if (jj+3<track_byte_count
751             && !buf[jj+3]
752             && !buf[jj+2]) {
753 
754           lonscale = le_read32(buf+jj);
755 //printf(" LONSCALE:");
756 //printf("%02x%02x%02x%02x", buf[jj], buf[jj+1], buf[jj+2], buf[jj+3]);
757           jj+=4;
758         }
759         // Peek to see if next is a latscale.  Note that it
760         // can begin with 0x88, which is confusing.  Here we
761         // allow up to 16-bits of offset, so two of the
762         // bytes must be 0x00 for us to recognize it.
763         if (jj+3<track_byte_count
764             && !buf[jj+3]
765             && !buf[jj+2]) {
766 
767           latscale = le_read32(buf+jj);
768 //printf(" LATSCALE:");
769 //printf("%02x%02x%02x%02x ", buf[jj], buf[jj+1], buf[jj+2], buf[jj+3]);
770           jj+=4;
771         }
772         llvalid = 1;
773 
774         waypoint_temp = tpo_convert_ll(lat, lon);
775         track_add_wpt(track_temp, waypoint_temp);
776         waypoint_count++;
777       }
778 
779       // Check whether there's a lonlat coming up instead of
780       // offsets.
781       else if (buf[jj] == 0x88) {
782         jj++;
783         llvalid = 0;
784       }
785 
786       // Check whether there's a lonlat + lonscale/latscale
787       // combo embedded in this track next.
788       else if (buf[jj] == 0x00) {
789 //printf(" ZERO ");
790         jj++;
791         llvalid = 0;
792       }
793 
794       // Process the delta
795       else {
796         int scarray[] = {0,1,2,3,4,5,6,7,0,-7,-6,-5,-4,-3,-2,-1};
797 
798 
799         if (buf[jj] == 0) {
800           printf("Found unexpected ZERO\n");
801           exit(1);
802         }
803 
804         if (latscale == 0 || lonscale == 0) {
805           printf("Found bad scales lonscale=0x%x latscale=0x%x\n", lonscale, latscale);
806           exit(1);
807         }
808 
809         lon+=lonscale*scarray[buf[jj]>>4];
810         lat+=latscale*scarray[(buf[jj]&0xf)];
811 //printf(".");
812         jj++;
813 
814         waypoint_temp = tpo_convert_ll(lat, lon);
815         route_add_wpt(track_temp, waypoint_temp);
816         waypoint_count++;
817       }
818     }
819     track_temp->rte_waypt_ct = waypoint_count;
820 
821     xfree(buf);
822   }
823 //printf("\n");
824 }
825 
826 
827 
828 
829 
830 // Global index to waypoints, needed for routes, filled in by
831 // tpo_process_waypoints.
832 //
833 // For version 3.x files.
834 //
835 waypoint** tpo_wp_index;
836 unsigned int tpo_index_ptr = 0;
837 
838 
839 
840 
841 
842 // Waypoint decoder for version 3.x files.
843 //
tpo_process_waypoints(void)844 void tpo_process_waypoints(void)
845 {
846   unsigned int waypoint_count;
847   unsigned int ii;
848 
849 
850 //printf("Processing Waypoints...\n");
851 
852   // Find block 0x0e0000 (GPS-Waypoints)
853   if (tpo_find_block(0x0e0000)) {
854     return;
855   }
856 
857   // Read the number of waypoints.  8/16/32-bit value.
858   waypoint_count = tpo_read_int();
859 
860 //printf("Total Waypoints: %d\n", waypoint_count);
861 
862   // Fetch storage for the waypoint index (needed later for
863   // routes)
864   tpo_wp_index = (waypoint**) xmalloc(sizeof(waypoint*) * waypoint_count);
865 
866   if (waypoint_count == 0) {
867     return;
868   }
869 
870   // Read/process each waypoint in the file
871   for (ii = 0; ii < waypoint_count; ii++) {
872     waypoint* waypoint_temp;
873     waypoint* waypoint_temp2;
874     unsigned int name_length;
875     char* waypoint_name;
876     int lat;
877     int lon;
878     int altitude;
879 
880 
881 //UNKNOWN DATA LENGTH
882     (void)tpo_read_int(); // 0x00
883 
884 //UNKNOWN DATA LENGTH
885     (void)tpo_read_int(); // 0x00
886 
887 //UNKNOWN DATA LENGTH
888     // Fetch name length
889     name_length = tpo_read_int();
890 //printf("\nName Length: %d\n", name_length);
891     if (name_length) {
892       waypoint_name = (char*) xmalloc(name_length+1);
893       waypoint_name[0] = '\0';
894       gbfread(waypoint_name, 1, name_length, tpo_file_in);
895       waypoint_name[name_length] = '\0';  // Terminator
896     } else { // Assign a generic waypoint name
897       xasprintf(&waypoint_name, "WPT %d", ii+1);
898     }
899 //printf("\tWaypoint Name: %s\n", waypoint_name);
900 
901 //UNKNOWN DATA LENGTH
902     (void)tpo_read_int();
903 
904     lon = gbfgetint32(tpo_file_in);
905     lat = gbfgetint32(tpo_file_in);
906 
907     // Allocate space for waypoint and store lat/lon
908     waypoint_temp = tpo_convert_ll(lat, lon);
909 
910     // Assign the waypoint name
911     waypoint_temp->shortname = waypoint_name;
912 
913     // Grab the altitude in meters
914     altitude = gbfgetint32(tpo_file_in);
915     if (altitude == 0xfffd000c) { // Unknown altitude
916       altitude = 0;
917     }
918     waypoint_temp->altitude = altitude / 100;   // Meters
919 //printf("\tAltitude: %1.0f meters\n", waypoint_temp->altitude);
920 
921 //UNKNOWN DATA LENGTH
922     // Fetch comment length
923     name_length = tpo_read_int();
924 //printf("\tComment length: %d\n", name_length);
925     if (name_length) {
926       char* comment;
927 
928       comment = (char*) xmalloc(name_length+1);
929       comment[0] = '\0';
930       gbfread(comment, 1, name_length, tpo_file_in);
931       comment[name_length] = '\0';  // Terminator
932       waypoint_temp->description = comment;
933 //printf("\tComment: %s\n", waypoint_name);
934     } else {
935 //            waypoint_temp->description = NULL;
936     }
937 
938 //        waypoint_temp->notes = NULL;
939 //        waypoint_temp->url = NULL;
940 //        waypoint_temp->url_link_text = NULL;
941 
942     // For routes (later), we need a duplicate of each waypoint
943     // indexed by the order we read them in.
944     waypoint_temp2 = waypt_dupe(waypoint_temp);
945 
946     // Attach the copy to our index
947     tpo_wp_index[tpo_index_ptr++] = waypoint_temp2;
948 
949     // Add the original waypoint to the chain of waypoints
950     waypt_add(waypoint_temp);
951 
952 //UNKNOWN DATA LENGTH
953 //        (void)tpo_read_int();
954     (void) gbfgetc(tpo_file_in);
955 
956 //UNKNOWN DATA LENGTH
957 //        (void)tpo_read_int();
958     (void) gbfgetc(tpo_file_in);
959 
960 //UNKNOWN DATA LENGTH
961 //        (void)tpo_read_int();
962     (void) gbfgetc(tpo_file_in);
963 
964 //UNKNOWN DATA LENGTH
965 //        (void)tpo_read_int();
966     (void) gbfgetc(tpo_file_in);
967   }
968 }
969 
970 
971 
972 
973 
974 // Map Notes decoder for version 3.x files.
975 //
tpo_process_map_notes(void)976 void tpo_process_map_notes(void)
977 {
978   unsigned int waypoint_count;
979   unsigned int ii;
980 
981 
982 //printf("Processing Map Notes...\n");
983 
984   // Find block 0x090000 (Map Notes)
985   if (tpo_find_block(0x090000)) {
986     return;
987   }
988 
989   // Read the number of waypoints.  8/16/32-bit value.
990   waypoint_count = tpo_read_int();
991 
992 //printf("Elements: %d\n", waypoint_count);
993 
994   if (!waypoint_count) {
995     return;
996   }
997 
998   // Process each waypoint
999   for (ii = 0; ii < waypoint_count; ii++) {
1000     int lat;
1001     int lon;
1002     unsigned int name_length;
1003     char* waypoint_name;
1004     waypoint* waypoint_temp;
1005     unsigned int num_bytes;
1006     unsigned int jj;
1007 
1008 
1009 //UNKNOWN DATA LENGTH
1010     (void)tpo_read_int();
1011 
1012     lon = gbfgetint32(tpo_file_in);
1013     lat = gbfgetint32(tpo_file_in);
1014 
1015     // Allocate space for waypoint and store lat/lon
1016     waypoint_temp = tpo_convert_ll(lat, lon);
1017 
1018     // Assign a generic waypoint name
1019     xasprintf(&waypoint_name, "NOTE %d", ii+1);
1020 //printf("Waypoint Name: %s\t\t", waypoint_name);
1021     waypoint_temp->shortname = waypoint_name;
1022 
1023 //UNKNOWN DATA LENGTH
1024     (void)tpo_read_int();
1025 
1026 //UNKNOWN DATA LENGTH
1027     (void)tpo_read_int();
1028 
1029 //UNKNOWN DATA LENGTH
1030     (void)tpo_read_int();
1031 
1032 //UNKNOWN DATA LENGTH
1033     // Fetch comment length
1034     name_length = tpo_read_int();
1035     if (name_length) {
1036       char* comment;
1037 
1038       comment = (char*) xmalloc(name_length+1);
1039       comment[0] = '\0';
1040       gbfread(comment, 1, name_length, tpo_file_in);
1041       comment[name_length] = '\0';  // Terminator
1042       waypoint_temp->description = comment;
1043 //printf("Comment: %s\n", comment);
1044     } else {
1045 //            waypoint_temp->description = NULL;
1046     }
1047 
1048 //        waypoint_temp->url_link_text = NULL;
1049 
1050     // Length of text for external path.  If non-zero, skip past
1051     // the text.
1052 //UNKNOWN DATA LENGTH
1053     name_length = tpo_read_int();
1054 //printf("name_length: %x\n", name_length);
1055     if (name_length) {
1056       char* notes;
1057 
1058       notes = (char*) xmalloc(name_length+1);
1059       notes[0] = '\0';
1060       gbfread(notes, 1, name_length, tpo_file_in);
1061       notes[name_length] = '\0';  // Terminator
1062       waypoint_temp->url = notes;
1063 //printf("Notes: %s\n", notes);
1064     }
1065 
1066     // Length of text for image path.  If non-zero, skip past
1067     // the text.
1068 //UNKNOWN DATA LENGTH
1069     name_length = tpo_read_int();
1070     if (name_length) {
1071       char* notes;
1072 
1073       notes = (char*) xmalloc(name_length+1);
1074       notes[0] = '\0';
1075       gbfread(notes, 1, name_length, tpo_file_in);
1076       notes[name_length] = '\0';  // Terminator
1077       waypoint_temp->url = notes;
1078 //printf("Notes: %s\n", notes);
1079     }
1080 
1081 //UNKNOWN DATA LENGTH
1082     (void)tpo_read_int();
1083 
1084 //UNKNOWN DATA LENGTH
1085     (void)tpo_read_int();
1086 
1087     // Number of bytes to skip until next element or end of
1088     // block.  May be 8/16/32 bits.
1089     num_bytes = tpo_read_int();
1090 //printf("num_bytes: %x\n", num_bytes);
1091     for (jj = 0; jj < num_bytes; jj++) {
1092       (void) gbfgetc(tpo_file_in); // Skip bytes
1093     }
1094 
1095     // Can be 8/16/32 bits
1096     (void)tpo_read_int();
1097 
1098 //UNKNOWN DATA LENGTH
1099     (void)tpo_read_int();
1100 
1101     // Add the waypoint to the chain of waypoints
1102     waypt_add(waypoint_temp);
1103   }
1104 }
1105 
1106 
1107 
1108 
1109 
1110 // Symbols decoder for version 3.x files.
1111 //
tpo_process_symbols(void)1112 void tpo_process_symbols(void)
1113 {
1114   unsigned int waypoint_count;
1115   unsigned int ii;
1116 
1117 
1118 //printf("Processing Symbols...\n");
1119 
1120   // Find block 0x040000 (Symbols)
1121   if (tpo_find_block(0x040000)) {
1122     return;
1123   }
1124 
1125   // Read the number of waypoints.  8/16/32-bit value.
1126   waypoint_count = tpo_read_int();
1127 
1128 //printf("Elements: %d\n", waypoint_count);
1129 
1130   if (!waypoint_count) {
1131     return;
1132   }
1133 
1134   // Process each waypoint
1135   for (ii = 0; ii < waypoint_count; ii++) {
1136     int lat;
1137     int lon;
1138     char* waypoint_name;
1139     waypoint* waypoint_temp;
1140 
1141 
1142 //UNKNOWN DATA LENGTH
1143     (void)tpo_read_int();
1144 
1145 //UNKNOWN DATA LENGTH
1146     (void)tpo_read_int();
1147 
1148     lon = gbfgetint32(tpo_file_in);
1149     lat = gbfgetint32(tpo_file_in);
1150 
1151     // Allocate space for waypoint and store lat/lon
1152     waypoint_temp = tpo_convert_ll(lat, lon);
1153 
1154     // Assign a generic waypoint name
1155     xasprintf(&waypoint_name, "SYM %d", ii+1);
1156 //printf("Waypoint Name: %s\n", waypoint_name);
1157     waypoint_temp->shortname = waypoint_name;
1158 
1159 //        waypoint_temp->description = NULL;
1160 //        waypoint_temp->notes = NULL;
1161 //        waypoint_temp->url = NULL;
1162 //        waypoint_temp->url_link_text = NULL;
1163 
1164     // Add the waypoint to the chain of waypoints
1165     waypt_add(waypoint_temp);
1166   }
1167 }
1168 
1169 
1170 
1171 
1172 
1173 // Text Labels decoder for version 3.x files.
1174 //
tpo_process_text_labels(void)1175 void tpo_process_text_labels(void)
1176 {
1177   unsigned int waypoint_count;
1178   unsigned int ii;
1179 
1180 
1181 //printf("Processing Text Labels...\n");
1182 
1183   // Find block 0x080000 (Text Labels)
1184   if (tpo_find_block(0x080000)) {
1185     return;
1186   }
1187 
1188   // Read the number of waypoints.  8/16/32-bit value.
1189   waypoint_count = tpo_read_int();
1190 
1191 //printf("Elements: %d\n", waypoint_count);
1192 
1193   if (!waypoint_count) {
1194     return;
1195   }
1196 
1197   // Process each waypoint
1198   for (ii = 0; ii < waypoint_count; ii++) {
1199     int jj;
1200     int lat;
1201     int lon;
1202     unsigned int name_length;
1203     char* waypoint_name;
1204     waypoint* waypoint_temp;
1205 
1206 
1207 //UNKNOWN DATA LENGTH
1208     (void)tpo_read_int();
1209 
1210 //UNKNOWN DATA LENGTH
1211     (void)tpo_read_int();
1212 
1213     lon = gbfgetint32(tpo_file_in);
1214     lat = gbfgetint32(tpo_file_in);
1215 
1216     // Allocate space for waypoint and store lat/lon
1217     waypoint_temp = tpo_convert_ll(lat, lon);
1218 
1219     // Assign a generic waypoint name
1220     xasprintf(&waypoint_name, "TXT %d", ii+1);
1221 //printf("Waypoint Name: %s\t\t", waypoint_name);
1222     waypoint_temp->shortname = waypoint_name;
1223 
1224     for (jj = 0; jj < 16; jj++) {
1225 //UNKNOWN DATA LENGTH
1226       (void) gbfgetc(tpo_file_in);
1227     }
1228 
1229     // Fetch comment length
1230 //UNKNOWN DATA LENGTH
1231     name_length = tpo_read_int();
1232     if (name_length) {
1233       char* comment;
1234 
1235       comment = (char*) xmalloc(name_length+1);
1236       comment[0] = '\0';
1237       gbfread(comment, 1, name_length, tpo_file_in);
1238       comment[name_length] = '\0';  // Terminator
1239       waypoint_temp->description = comment;
1240 //printf("Comment: %s\n", comment);
1241     } else {
1242 //            waypoint_temp->description = NULL;
1243     }
1244 
1245 //        waypoint_temp->notes = NULL;
1246 //        waypoint_temp->url = NULL;
1247 //        waypoint_temp->url_link_text = NULL;
1248 
1249     // Add the waypoint to the chain of waypoints
1250     waypt_add(waypoint_temp);
1251   }
1252 }
1253 
1254 
1255 
1256 
1257 
1258 // Route decoder for version 3.x files.
1259 //
1260 // We depend on tpo_wp_index[] having been malloc'ed and filled-in
1261 // with pointers to waypoint objects by tpo_process_waypoints()
1262 // function above.
1263 //
tpo_process_routes(void)1264 void tpo_process_routes(void)
1265 {
1266   unsigned int route_count;
1267   unsigned int ii;
1268 
1269 
1270 //printf("Processing Routes...\n");
1271 
1272   // Find block 0x0f0000 (GPS-Routes)
1273   if (tpo_find_block(0x0f0000)) {
1274     return;
1275   }
1276 
1277   // Read the number of routes.  8/16/32-bit value
1278   route_count = tpo_read_int();
1279 
1280 //printf("Total Routes: %d\n", route_count);
1281 
1282   if (route_count == 0) {
1283     return;
1284   }
1285 
1286   // Read/process each route in the file
1287   //
1288   for (ii = 0; ii < route_count; ii++) {
1289     unsigned int name_length = 0;
1290     char* route_name;
1291     unsigned int jj;
1292     unsigned int waypoint_cnt;
1293     route_head* route_temp;
1294 
1295 
1296     // Allocate the route struct
1297     route_temp = route_head_alloc();
1298     route_add_head(route_temp);
1299 
1300 //UNKNOWN DATA LENGTH
1301     (void)tpo_read_int();
1302 
1303 //UNKNOWN DATA LENGTH
1304     (void)tpo_read_int();
1305 
1306 //UNKNOWN DATA LENGTH
1307     // Fetch name length
1308     name_length = tpo_read_int();
1309     if (name_length) {
1310       route_name = (char*) xmalloc(name_length+1);
1311       route_name[0] = '\0';
1312       gbfread(route_name, 1, name_length, tpo_file_in);
1313       route_name[name_length] = '\0';  // Terminator
1314     } else { // Assign a generic route name
1315       xasprintf(&route_name, "RTE %d", ii+1);
1316     }
1317     route_temp->rte_name = route_name;
1318 //printf("Route Name: %s\n", route_name);
1319 
1320 //UNKNOWN DATA LENGTH
1321     // Comment?
1322     (void)tpo_read_int();
1323 //        gbfgetc(tpo_file_in);
1324 //        route_temp->rte_desc = NULL;
1325 
1326     route_temp->rte_num = ii+1;
1327 
1328     // Fetch the number of waypoints in this route.  8/16/32-bit
1329     // value.
1330     waypoint_cnt = tpo_read_int();
1331 
1332     route_temp->rte_waypt_ct = waypoint_cnt;
1333 
1334     // Run through the list of waypoints, look up each in our
1335     // index, then add the waypoint to this route.
1336     //
1337     for (jj = 0; jj < waypoint_cnt; jj++) {
1338       waypoint* waypoint_temp;
1339       unsigned char val;
1340 
1341 
1342 //UNKNOWN DATA LENGTH
1343       // Fetch the index to the waypoint
1344       val = tpo_read_int();
1345 //printf("val: %x\t\t", val);
1346 
1347       // Duplicate a waypoint from our index of waypoints.
1348       waypoint_temp = waypt_dupe(tpo_wp_index[val-1]);
1349 
1350       // Add the waypoint to the route
1351       route_add_wpt(route_temp, waypoint_temp);
1352     }
1353 //printf("\n");
1354   }
1355 
1356   // Free the waypoint index, we don't need it anymore.
1357   for (ii = 0; ii < tpo_index_ptr; ii++) {
1358     if (tpo_wp_index[ii]->shortname) {
1359       xfree(tpo_wp_index[ii]->shortname);
1360     }
1361     if (tpo_wp_index[ii]->description) {
1362       xfree(tpo_wp_index[ii]->description);
1363     }
1364     xfree(tpo_wp_index[ii]);
1365   }
1366 
1367   // Free the index array itself
1368   xfree(tpo_wp_index);
1369 }
1370 
1371 
1372 
1373 
1374 
1375 // Compass decoder for version 3.x files.
1376 //
tpo_process_compass(void)1377 void tpo_process_compass(void)
1378 {
1379 
1380   // Not implemented yet
1381 }
1382 
1383 
1384 
1385 
1386 
1387 // Decoder for version 3.x files.  These files have "tracks"
1388 // (called "freehand routes" or just "routes" in Topo), "waypoints",
1389 // and "gps-routes".  We intend to read all three types.
1390 //
tpo_read_3_x(void)1391 void tpo_read_3_x(void)
1392 {
1393 
1394   if (doing_trks) {
1395 //printf("Processing Tracks\n");
1396     tpo_process_tracks();
1397   }
1398 
1399   if (doing_wpts || doing_rtes) {
1400 //printf("Processing Waypoints\n");
1401     tpo_process_waypoints();
1402   }
1403 
1404   if (doing_rtes) {
1405     //
1406     // Note:  To process routes we _MUST_ process waypoints
1407     // first!  This creates the index of waypoints that we need
1408     // for routes.
1409     //
1410 //printf("Processing Routes\n");
1411     tpo_process_routes();
1412   }
1413 
1414   if (doing_wpts) {
1415     //
1416     // Other blocks in the file have waypoint-type information
1417     // in them.  Map Notes, Symbols, and Text Labels.  We
1418     // process those here and add them to the end of the
1419     // waypoint list.
1420     //
1421 //printf("Processing Map Notes\n");
1422     tpo_process_map_notes();
1423 
1424 //printf("Processing Symbols\n");
1425     tpo_process_symbols();
1426 
1427 //printf("Processing Text Labels\n");
1428     tpo_process_text_labels();
1429 
1430 //printf("Processing Compass Symbols\n");
1431 //        tpo_process_compass();
1432 
1433   }
1434 }
1435 
1436 
1437 
1438 
1439 
1440 //-------------------------------------------------------------------
1441 //-------------------------------------------------------------------
1442 
1443 
1444 
1445 
1446 
1447 static void
tpo_read(void)1448 tpo_read(void)
1449 {
1450 
1451   if (tpo_version == 2.0) {
1452 //printf("\nFound a version 2.x file\n");
1453     tpo_read_2_x();
1454   } else if (tpo_version == 3.0) {
1455 //printf("\nFound a version 3.x file\n");
1456     tpo_read_3_x();
1457   } else {
1458     fatal(MYNAME ": gpsbabel can only read TPO versions through 3.x.x\n");
1459   }
1460 }
1461 
1462 
1463 
1464 
1465 
1466 /*******************************************************************************/
1467 /*                                     WRITE                                   */
1468 /*******************************************************************************/
1469 
1470 /* tpo_write_file_header()
1471    Write the appropriate header for the desired TOPO! state.
1472 
1473    National Geographic sells about 75 different state and regional software
1474    programs called TOPO! that use the TPO format. Each one uses a different
1475    header data sequence. The header contains the name of the state maps, as well
1476    as some map scaling information and other data. In most cases, you can't open
1477    a TPO file created by a different state/regional version of TOPO! than the one
1478    you're using yourself. When writing a TPO file, it is therefore necessary to
1479    specify what TOPO! state product to create the file for. I believe that the
1480    TOPO! regional products can open TPO files created by the TOPO! state products
1481    as long as the track data is within the area covered by the regional product.
1482    As a result, it's only necessary to decide what state product output format to
1483    use.
1484 
1485    TO ADD SUPPORT FOR ANOTHER STATE:
1486    1. Obtain an example .tpo file generated by the state product for which you wish
1487    to add support. National Geographic MapXchange (http://maps.nationalgeographic.com/topo/search.cfm)
1488    is a good source of .tpo files.
1489    2. Run gpsbabel using the "dumpheader" option of the TPO format converter, and
1490    specifying a dummy output file. For example:
1491      gpsbabel -t -i tpo,dumpheader=1 -f sample_file.tpo -o csv -F dummy.txt
1492    This will write a snippet of C code containing the header bytes to the shell window.
1493    3. Add a new if() clause to tpo_write_file_header(). Copy the header bytes definition
1494    from the previous step.
1495    4. Recompile gpsbabel.
1496    5. You should now be able write TPO ouput in the new state's format. For example, if
1497       you added support for Texas:
1498      gpsbabel -t -i gpx -f input.gpx -o tpo,state="TX" -F output.tpo */
1499 static void
tpo_write_file_header()1500 tpo_write_file_header()
1501 {
1502   /* force upper-case state name */
1503   strupper(output_state);
1504 
1505   if (strncmp("CA", output_state, 2) == 0) {
1506 
1507     unsigned char header_bytes[] = {
1508       0x18, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72,
1509       0x6E, 0x69, 0x61, 0x20, 0x53, 0x68, 0x61, 0x64,
1510       0x65, 0x64, 0x20, 0x52, 0x65, 0x6C, 0x69, 0x65,
1511       0x66, 0x03, 0x43, 0x41, 0x31, 0x05, 0x00, 0x00,
1512       0x00, 0x00, 0x00, 0x00, 0x40, 0x5F, 0x40, 0x00,
1513       0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40, 0x00,
1514       0x00, 0x00, 0x00, 0x00, 0x80, 0x5C, 0x40, 0x00,
1515       0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00,
1516       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1517       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1518       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1519       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1520       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1521       0x00, 0x00, 0x00, 0x00, 0x27, 0x43, 0x3A, 0x5C,
1522       0x50, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20,
1523       0x46, 0x69, 0x6C, 0x65, 0x73, 0x5C, 0x54, 0x4F,
1524       0x50, 0x4F, 0x21, 0x5C, 0x54, 0x50, 0x4F, 0x5F,
1525       0x44, 0x41, 0x54, 0x41, 0x5C, 0x43, 0x41, 0x5F,
1526       0x44, 0x30, 0x31, 0x5C, 0x20, 0x43, 0x3A, 0x5C,
1527       0x50, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20,
1528       0x46, 0x69, 0x6C, 0x65, 0x73, 0x5C, 0x54, 0x4F,
1529       0x50, 0x4F, 0x21, 0x5C, 0x54, 0x50, 0x4F, 0x5F,
1530       0x44, 0x41, 0x54, 0x41, 0x5C, 0x12, 0x43, 0x3A,
1531       0x5C, 0x54, 0x4F, 0x50, 0x4F, 0x21, 0x5C, 0x54,
1532       0x50, 0x4F, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5C,
1533       0x00, 0x00, 0x00, 0xDC, 0x30, 0x32, 0x30, 0x32,
1534       0x30, 0x32, 0x30, 0x33, 0x30, 0x33, 0x30, 0x30,
1535       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1536       0x30, 0x30, 0x30, 0x32, 0x30, 0x32, 0x30, 0x32,
1537       0x30, 0x33, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30,
1538       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1539       0x30, 0x34, 0x30, 0x34, 0x30, 0x34, 0x30, 0x34,
1540       0x30, 0x36, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30,
1541       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1542       0x30, 0x35, 0x30, 0x35, 0x30, 0x34, 0x30, 0x36,
1543       0x30, 0x36, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30,
1544       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35,
1545       0x30, 0x35, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37,
1546       0x30, 0x37, 0x30, 0x38, 0x30, 0x30, 0x30, 0x30,
1547       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35,
1548       0x30, 0x35, 0x30, 0x37, 0x30, 0x37, 0x30, 0x37,
1549       0x30, 0x38, 0x30, 0x38, 0x30, 0x39, 0x30, 0x30,
1550       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35,
1551       0x31, 0x30, 0x30, 0x38, 0x30, 0x38, 0x30, 0x38,
1552       0x30, 0x39, 0x30, 0x39, 0x30, 0x39, 0x30, 0x30,
1553       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30,
1554       0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31,
1555       0x30, 0x39, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30,
1556       0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30,
1557       0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
1558       0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1559       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,
1560       0x31, 0x31, 0x31, 0x31, 0x30, 0x39, 0x30, 0x39,
1561       0x0D, 0x55, 0x6E, 0x69, 0x74, 0x65, 0x64, 0x20,
1562       0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x00, 0x00,
1563       0x00, 0x00, 0x00, 0x40, 0x5F, 0x40, 0xBC, 0x23,
1564       0x63, 0xB5, 0xF9, 0x3A, 0x50, 0x40, 0x00, 0x00,
1565       0x00, 0x00, 0x00, 0x80, 0x50, 0x40, 0x22, 0xE2,
1566       0xE6, 0x54, 0x32, 0x28, 0x22, 0x40, 0x0A, 0x43,
1567       0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72, 0x6E, 0x69,
1568       0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5F,
1569       0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45,
1570       0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x5C,
1571       0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
1572       0x40, 0x16, 0x2A, 0x47, 0x65, 0x6E, 0x65, 0x72,
1573       0x61, 0x6C, 0x20, 0x52, 0x65, 0x66, 0x65, 0x72,
1574       0x65, 0x6E, 0x63, 0x65, 0x20, 0x4D, 0x61, 0x70,
1575       0x00, 0x09, 0x3D, 0x00, 0x0C, 0x43, 0x41, 0x31,
1576       0x5F, 0x4D, 0x41, 0x50, 0x31, 0x5C, 0x53, 0x31,
1577       0x4C, 0xAF, 0x02, 0x15, 0x03, 0x00, 0x00, 0x00,
1578       0x00, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x00,
1579       0x00, 0x00, 0x00, 0x24, 0x40, 0x84, 0x00, 0x78,
1580       0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16,
1581       0x2A, 0x4E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x61,
1582       0x6C, 0x20, 0x41, 0x74, 0x6C, 0x61, 0x73, 0x20,
1583       0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0xE8, 0x32,
1584       0x0D, 0x00, 0x02, 0x44, 0x41, 0x23, 0x01, 0x6C,
1585       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0,
1586       0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0,
1587       0x3F, 0x3C, 0x00, 0x3C, 0x00, 0x01, 0x00, 0x00,
1588       0x00, 0x01, 0x00, 0x10, 0x2A, 0x35, 0x30, 0x30,
1589       0x4B, 0x20, 0x4D, 0x61, 0x70, 0x20, 0x53, 0x65,
1590       0x72, 0x69, 0x65, 0x73, 0xC0, 0xFE, 0x04, 0x00,
1591       0x02, 0x44, 0x46, 0x00, 0x01, 0x40, 0x01, 0xB5,
1592       0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0xB5,
1593       0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0x28,
1594       0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
1595       0x00, 0x10, 0x2A, 0x31, 0x30, 0x30, 0x4B, 0x20,
1596       0x4D, 0x61, 0x70, 0x20, 0x53, 0x65, 0x72, 0x69,
1597       0x65, 0x73, 0x50, 0xC3, 0x00, 0x00, 0x02, 0x44,
1598       0x4B, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x00,
1599       0x00, 0x00, 0x00, 0xB0, 0x3F, 0x00, 0x00, 0x00,
1600       0x00, 0x00, 0x00, 0xB0, 0x3F, 0x2D, 0x00, 0x2D,
1601       0x00, 0x0C, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x10,
1602       0x2A, 0x37, 0x2E, 0x35, 0x27, 0x20, 0x4D, 0x61,
1603       0x70, 0x20, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73,
1604       0x0F, 0x3C, 0x00, 0x00, 0x02, 0x44, 0x51, 0x00,
1605       0x00, 0x00, 0x01, 0x9A, 0x99, 0x99, 0x99, 0x99,
1606       0x99, 0x99, 0x3F, 0x9A, 0x99, 0x99, 0x99, 0x99,
1607       0x99, 0x89, 0x3F, 0x5A, 0x00, 0x2D, 0x00, 0x0D,
1608       0x00, 0x01, 0x00, 0x28, 0x00
1609     };
1610 
1611     gbfwrite(header_bytes, sizeof(header_bytes), 1, tpo_file_out);
1612   } else if (strncmp("CT", output_state, 2) == 0 ||
1613              strncmp("MA", output_state, 2) == 0 ||
1614              strncmp("ME", output_state, 2) == 0 ||
1615              strncmp("NJ", output_state, 2) == 0 ||
1616              strncmp("NH", output_state, 2) == 0 ||
1617              strncmp("NY", output_state, 2) == 0 ||
1618              strncmp("RI", output_state, 2) == 0 ||
1619              strncmp("VT", output_state, 2) == 0) {
1620     /* These 8 states are all covered in a single "Northeast" title */
1621 
1622     unsigned char header_bytes[] = {
1623       0x1E, 0x4E, 0x6F, 0x72, 0x74, 0x68, 0x65, 0x61,
1624       0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x55, 0x53,
1625       0x41, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x64,
1626       0x20, 0x52, 0x65, 0x6C, 0x69, 0x65, 0x66, 0x03,
1627       0x4E, 0x45, 0x31, 0x05, 0x00, 0x00, 0x00, 0x00,
1628       0x00, 0x00, 0x00, 0x54, 0x40, 0x00, 0x00, 0x00,
1629       0x00, 0x00, 0x00, 0x48, 0x40, 0x00, 0x00, 0x00,
1630       0x00, 0x00, 0x80, 0x50, 0x40, 0x00, 0x00, 0x00,
1631       0x00, 0x00, 0x00, 0x43, 0x40, 0x00, 0x00, 0x00,
1632       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1633       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1634       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1635       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1636       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1637       0x00, 0x00, 0x0B, 0x44, 0x3A, 0x5C, 0x4E, 0x45,
1638       0x31, 0x5F, 0x44, 0x30, 0x31, 0x5C, 0x12, 0x43,
1639       0x3A, 0x5C, 0x54, 0x4F, 0x50, 0x4F, 0x21, 0x5C,
1640       0x54, 0x50, 0x4F, 0x5F, 0x44, 0x41, 0x54, 0x41,
1641       0x5C, 0x12, 0x45, 0x3A, 0x5C, 0x54, 0x4F, 0x50,
1642       0x4F, 0x21, 0x5C, 0x54, 0x50, 0x4F, 0x5F, 0x44,
1643       0x41, 0x54, 0x41, 0x5C, 0x00, 0x00, 0x00, 0xFF,
1644       0x18, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1645       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1646       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x37,
1647       0x30, 0x37, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30,
1648       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1649       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1650       0x30, 0x37, 0x30, 0x37, 0x30, 0x37, 0x30, 0x37,
1651       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1652       0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x30, 0x34,
1653       0x30, 0x35, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37,
1654       0x30, 0x37, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30,
1655       0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0x33,
1656       0x30, 0x34, 0x30, 0x34, 0x30, 0x35, 0x30, 0x35,
1657       0x30, 0x36, 0x30, 0x36, 0x30, 0x36, 0x30, 0x36,
1658       0x30, 0x36, 0x30, 0x32, 0x30, 0x32, 0x30, 0x32,
1659       0x30, 0x33, 0x30, 0x33, 0x30, 0x34, 0x30, 0x34,
1660       0x30, 0x35, 0x30, 0x35, 0x30, 0x36, 0x30, 0x36,
1661       0x30, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32,
1662       0x30, 0x32, 0x30, 0x32, 0x30, 0x33, 0x30, 0x33,
1663       0x30, 0x38, 0x30, 0x38, 0x30, 0x38, 0x30, 0x39,
1664       0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1665       0x30, 0x30, 0x30, 0x32, 0x30, 0x32, 0x30, 0x32,
1666       0x30, 0x33, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38,
1667       0x30, 0x39, 0x30, 0x39, 0x30, 0x39, 0x30, 0x39,
1668       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1669       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30,
1670       0x31, 0x30, 0x31, 0x30, 0x30, 0x39, 0x30, 0x30,
1671       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1672       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1673       0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30,
1674       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1675       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1676       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1677       0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1678       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1679       0x30, 0x30, 0x0D, 0x55, 0x6E, 0x69, 0x74, 0x65,
1680       0x64, 0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73,
1681       0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5F, 0x40,
1682       0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x4E, 0x40,
1683       0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x50, 0x40,
1684       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x40,
1685       0x10, 0x4E, 0x6F, 0x72, 0x74, 0x68, 0x65, 0x61,
1686       0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x55, 0x53,
1687       0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54,
1688       0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x48,
1689       0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x50,
1690       0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42,
1691       0x40, 0x16, 0x2A, 0x47, 0x65, 0x6E, 0x65, 0x72,
1692       0x61, 0x6C, 0x20, 0x52, 0x65, 0x66, 0x65, 0x72,
1693       0x65, 0x6E, 0x63, 0x65, 0x20, 0x4D, 0x61, 0x70,
1694       0x00, 0x09, 0x3D, 0x00, 0x0C, 0x4E, 0x45, 0x31,
1695       0x5F, 0x4D, 0x41, 0x50, 0x31, 0x5C, 0x53, 0x31,
1696       0x4C, 0x68, 0x03, 0x16, 0x03, 0x00, 0x00, 0x00,
1697       0x00, 0x00, 0x00, 0x2C, 0x40, 0x00, 0x00, 0x00,
1698       0x00, 0x00, 0x00, 0x24, 0x40, 0x8C, 0x00, 0x64,
1699       0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16,
1700       0x2A, 0x4E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x61,
1701       0x6C, 0x20, 0x41, 0x74, 0x6C, 0x61, 0x73, 0x20,
1702       0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0xE8, 0x32,
1703       0x0D, 0x00, 0x02, 0x44, 0x41, 0x0B, 0x01, 0x6C,
1704       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0,
1705       0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0,
1706       0x3F, 0x3C, 0x00, 0x3C, 0x00, 0x01, 0x00, 0x00,
1707       0x00, 0x01, 0x00, 0x10, 0x2A, 0x35, 0x30, 0x30,
1708       0x4B, 0x20, 0x4D, 0x61, 0x70, 0x20, 0x53, 0x65,
1709       0x72, 0x69, 0x65, 0x73, 0xC0, 0xFE, 0x04, 0x00,
1710       0x02, 0x44, 0x46, 0xEA, 0x00, 0x40, 0x01, 0xB5,
1711       0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0xB5,
1712       0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0x28,
1713       0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
1714       0x00, 0x10, 0x2A, 0x31, 0x30, 0x30, 0x4B, 0x20,
1715       0x4D, 0x61, 0x70, 0x20, 0x53, 0x65, 0x72, 0x69,
1716       0x65, 0x73, 0x50, 0xC3, 0x00, 0x00, 0x02, 0x44,
1717       0x4B, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x00,
1718       0x00, 0x00, 0x00, 0xB0, 0x3F, 0x00, 0x00, 0x00,
1719       0x00, 0x00, 0x00, 0xB0, 0x3F, 0x2D, 0x00, 0x2D,
1720       0x00, 0x0C, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x10,
1721       0x2A, 0x37, 0x2E, 0x35, 0x27, 0x20, 0x4D, 0x61,
1722       0x70, 0x20, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73,
1723       0x0F, 0x3C, 0x00, 0x00, 0x02, 0x44, 0x51, 0x00,
1724       0x00, 0x00, 0x01, 0x9A, 0x99, 0x99, 0x99, 0x99,
1725       0x99, 0x99, 0x3F, 0x9A, 0x99, 0x99, 0x99, 0x99,
1726       0x99, 0x89, 0x3F, 0x5A, 0x00, 0x2D, 0x00, 0x0D,
1727       0x00, 0x01, 0x00, 0x28, 0x00
1728     };
1729 
1730     gbfwrite(header_bytes, sizeof(header_bytes), 1, tpo_file_out);
1731   }
1732 
1733   else {
1734     fatal(MYNAME ": writing ouput for state \"%s\" is not currently supported.\n", output_state);
1735   }
1736 }
1737 
1738 static void
tpo_track_hdr(const route_head * rte)1739 tpo_track_hdr(const route_head* rte)
1740 {
1741   double amt;
1742   unsigned char temp_buffer[8];
1743   unsigned char visibility_flags[] = { 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00 };
1744   unsigned char unknown1[] = { 0xFF, 0x00, 0x00, 0x00 };
1745   unsigned char bounding_box[8] = { 0x00, 0x80, 0x00, 0x80, 0xFF, 0x7F, 0xFF, 0x7F };
1746 
1747   waypoint* first_track_waypoint = (waypoint*) QUEUE_FIRST(&rte->waypoint_list);
1748 
1749   /* zoom level 1-5 visibility flags */
1750   gbfwrite(visibility_flags, 1, sizeof(visibility_flags), tpo_file_out);
1751 
1752   /* 8 bytes of zeros, meaning unknown */
1753   memset(temp_buffer, 0, sizeof(temp_buffer));
1754   gbfwrite(temp_buffer, 1, sizeof(temp_buffer), tpo_file_out);
1755 
1756   /* 4 more unknown bytes, possibly sign flags for the longitude and latitude? */
1757   gbfwrite(unknown1, 1, sizeof(unknown1), tpo_file_out);
1758 
1759   /* the starting point of the route */
1760   /* convert lat/long to NAD27/CONUS datum */
1761   GPS_Math_WGS84_To_Known_Datum_M(
1762     first_track_waypoint->latitude,
1763     first_track_waypoint->longitude,
1764     first_track_waypoint->altitude,
1765     &first_track_waypoint_lat,
1766     &first_track_waypoint_lon,
1767     &amt,
1768     78);
1769 
1770   /* swap the sign back *after* the datum conversion */
1771   first_track_waypoint_lon *= -1.0;
1772 
1773   /* Compute this track's scaling factors: Used for scaling each track point and then
1774      later written out to the track footer. These are approximately the ratios between
1775      pixels and degrees when viewing the 1:24000 map in TOPO!. In practice, it doesn't
1776      appear to be necessary that they be correct, as long as the same values are used
1777      for doing the scaling and for writing into the track footer data. */
1778   output_track_lat_scale = 4.8828125e-005; /* TOPO! appears to use a constant lat scale */
1779   output_track_lon_scale = output_track_lat_scale / cos(GPS_Math_Deg_To_Rad(first_track_waypoint_lat));
1780 
1781   /* 8 bytes - longitude */
1782   gbfputdbl(first_track_waypoint_lon, tpo_file_out);
1783 
1784   /* 8 bytes - latitude */
1785   gbfputdbl(first_track_waypoint_lat, tpo_file_out);
1786 
1787   /* 8 bytes: seems to be bounding box info */
1788   gbfwrite(bounding_box, 1, sizeof(bounding_box), tpo_file_out);
1789 
1790   /* number of route points */
1791   gbfputint16(rte->rte_waypt_ct, tpo_file_out);
1792 
1793   /* initialize the track length computation */
1794   track_length = 0;
1795   GPS_Math_WGS84LatLonH_To_XYZ(
1796     first_track_waypoint->latitude,
1797     first_track_waypoint->longitude,
1798     0.0,
1799     &last_waypoint_x,
1800     &last_waypoint_y,
1801     &last_waypoint_z);
1802 }
1803 
1804 static void
tpo_track_disp(const waypoint * waypointp)1805 tpo_track_disp(const waypoint* waypointp)
1806 {
1807   double lat, lon, amt, x, y, z;
1808   short lat_delta, lon_delta;
1809 
1810   /* fprintf(stderr, "%f/%f\n", waypointp->latitude, waypointp->longitude); */
1811 
1812   /* convert lat/lon position to XYZ meters */
1813   GPS_Math_WGS84LatLonH_To_XYZ(
1814     waypointp->latitude,
1815     waypointp->longitude,
1816     0.0,
1817     &x,
1818     &y,
1819     &z);
1820 
1821   /* increase the track length by the 3D length of last track segment in feet */
1822   track_length += METERS_TO_FEET(sqrt(
1823                                    (x - last_waypoint_x) * (x - last_waypoint_x) +
1824                                    (y - last_waypoint_y) * (y - last_waypoint_y) +
1825                                    (z - last_waypoint_z) * (z - last_waypoint_z)));
1826   last_waypoint_x = x;
1827   last_waypoint_y = y;
1828   last_waypoint_z = z;
1829 
1830   /* convert lat/long to NAD27/CONUS datum */
1831   GPS_Math_WGS84_To_Known_Datum_M(
1832     waypointp->latitude,
1833     waypointp->longitude,
1834     waypointp->altitude,
1835     &lat,
1836     &lon,
1837     &amt,
1838     78);
1839 
1840   /* swap the sign back *after* the datum conversion */
1841   lon *= -1.0;
1842 
1843   /* longitude delta from first route point */
1844   lon_delta = (short)((first_track_waypoint_lon - lon) / output_track_lon_scale);
1845   gbfputint16(lon_delta, tpo_file_out);
1846 
1847   /* latitude delta from first route point */
1848   lat_delta = (short)((first_track_waypoint_lat - lat) / output_track_lat_scale);
1849   gbfputint16(lat_delta, tpo_file_out);
1850 
1851   /*
1852   fprintf(stderr, "%f %f: %x %x - %f %f %f / %f\n", lon, lat, lon_delta, lat_delta, first_track_waypoint_lat, lat, output_track_lat_scale, (first_track_waypoint_lat - lat) );
1853   */
1854 
1855 }
1856 
1857 static void
tpo_track_tlr(const route_head * rte)1858 tpo_track_tlr(const route_head* rte)
1859 {
1860   unsigned char unknown1[] = { 0x06, 0x00 };
1861 
1862   unsigned char continue_marker[] = { 0x01, 0x80 };
1863   unsigned char end_marker[] = { 0x00, 0x00 };
1864 
1865   /* pixel to degree scaling factors */
1866   gbfputdbl(output_track_lon_scale, tpo_file_out);
1867   gbfputdbl(output_track_lat_scale, tpo_file_out);
1868 
1869   /* 4 bytes: the total length of the route */
1870   gbfputint32(track_length, tpo_file_out);
1871 
1872   /* 2 unknown bytes */
1873   gbfwrite(unknown1, 1, sizeof(unknown1), tpo_file_out);
1874 
1875   /* the last track ends with 0x0000 instead of 0x0180 */
1876   track_out_count++;
1877   if (track_out_count == track_count()) {
1878     gbfwrite(end_marker, 1, sizeof(end_marker), tpo_file_out);
1879   } else {
1880     gbfwrite(continue_marker, 1, sizeof(continue_marker), tpo_file_out);
1881   }
1882 }
1883 
1884 static void
tpo_wr_init(const char * fname)1885 tpo_wr_init(const char* fname)
1886 {
1887   if (doing_wpts || doing_rtes) {
1888     fatal(MYNAME ": this file format only supports tracks, not waypoints or routes.\n");
1889   }
1890 
1891   tpo_file_out = gbfopen_le(fname, "wb", MYNAME);
1892   tpo_write_file_header();
1893 }
1894 
1895 static void
tpo_wr_deinit(void)1896 tpo_wr_deinit(void)
1897 {
1898   /* the file footer is six bytes of zeroes */
1899   unsigned char file_footer_bytes[6];
1900   memset(file_footer_bytes, 0, sizeof(file_footer_bytes));
1901   gbfwrite(file_footer_bytes, 1, sizeof(file_footer_bytes), tpo_file_out);
1902 
1903   gbfclose(tpo_file_out);
1904 }
1905 
1906 static void
tpo_write(void)1907 tpo_write(void)
1908 {
1909   unsigned char unknown1[] = { 0xFF, 0xFF, 0x01, 0x00 };
1910 
1911   char* chunk_name = "CTopoRoute";
1912   int chunk_name_length = strlen(chunk_name);
1913 
1914   /* write the total number of tracks */
1915   gbfputint16(track_count(), tpo_file_out);
1916 
1917   /* 4 unknown bytes */
1918   gbfwrite(unknown1, 1, 4, tpo_file_out);
1919 
1920   /* chunk name: "CTopoRoute" */
1921   gbfputint16(chunk_name_length, tpo_file_out);
1922   gbfwrite(chunk_name, 1, chunk_name_length, tpo_file_out);
1923 
1924   track_out_count = 0;
1925   track_disp_all(tpo_track_hdr, tpo_track_tlr, tpo_track_disp);
1926 }
1927 
1928 /* TPO 2.x format can read tracks only */
1929 ff_vecs_t tpo2_vecs = {
1930   ff_type_file,   /* ff_type_internal */
1931   /*    { ff_cap_none | ff_cap_none, ff_cap_read | ff_cap_write, ff_cap_none | ff_cap_none }, */
1932   { ff_cap_none, ff_cap_read,  ff_cap_none },
1933   tpo_rd_init,
1934   tpo_wr_init,
1935   tpo_rd_deinit,
1936   tpo_wr_deinit,
1937   tpo_read,
1938   tpo_write,
1939   NULL,
1940   tpo2_args,
1941   CET_CHARSET_ASCII, 0	/* CET-REVIEW */
1942 };
1943 
1944 /* TPO 3.x format can read waypoints/tracks/routes */
1945 ff_vecs_t tpo3_vecs = {
1946   ff_type_file,   /* ff_type_internal */
1947   { ff_cap_read, ff_cap_read, ff_cap_read },
1948   tpo_rd_init,
1949   tpo_wr_init,
1950   tpo_rd_deinit,
1951   tpo_wr_deinit,
1952   tpo_read,
1953   tpo_write,
1954   NULL,
1955   tpo3_args,
1956   CET_CHARSET_ASCII, 0	/* CET-REVIEW */
1957 };
1958