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