1 /*
2     Read DeLorme drawing files (.an1)
3 
4     Copyright (C) 2005 Ron Parker and Robert Lipe.
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
19 
20  */
21 
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <limits.h>
26 
27 #define MYNAME "an1"
28 #include "defs.h"
29 
30 static gbfile* infile;
31 static gbfile* outfile;
32 
33 static char* output_type = NULL;
34 static char* road_changes = NULL;
35 static char* nogc = NULL;
36 static char* nourl = NULL;
37 static char* opt_symbol = NULL;
38 static char* opt_color = NULL;
39 static char* opt_zoom  = NULL;
40 static char* opt_wpt_type = NULL;
41 static char* opt_radius = NULL;
42 
43 static short output_type_num = 0;
44 static short opt_zoom_num = 0;
45 static long opt_color_num = 0;
46 static short wpt_type_num = 0;
47 static short last_read_type = 0;
48 static double radius = 0.0;
49 
50 static long serial=10000;
51 static long rtserial=1;
52 
53 typedef struct roadchange {
54   long type;
55   char* name;
56 } roadchange;
57 
58 roadchange* roadchanges = NULL;
59 
60 static
61 arglist_t an1_args[] = {
62   {
63     "type", &output_type, "Type of .an1 file",
64     "", ARGTYPE_STRING, ARG_NOMINMAX
65   },
66   {
67     "road", &road_changes, "Road type changes",
68     "", ARGTYPE_STRING, ARG_NOMINMAX
69   },
70   {
71     "nogc", &nogc, "Do not add geocache data to description",
72     NULL, ARGTYPE_BOOL, ARG_NOMINMAX
73   },
74   {
75     "nourl", &nourl, "Do not add URLs to description",
76     NULL, ARGTYPE_BOOL, ARG_NOMINMAX
77   },
78   {
79     "deficon", &opt_symbol, "Symbol to use for point data",
80     "Red Flag", ARGTYPE_STRING, ARG_NOMINMAX
81   },
82   {
83     "color", &opt_color, "Color for lines or mapnotes",
84     "red", ARGTYPE_STRING, ARG_NOMINMAX
85   },
86   {
87     "zoom", &opt_zoom, "Zoom level to reduce points",
88     NULL, ARGTYPE_INT, ARG_NOMINMAX
89   },
90   {
91     "wpt_type", &opt_wpt_type,
92     "Waypoint type",
93     "", ARGTYPE_STRING, ARG_NOMINMAX
94   },
95   {
96     "radius", &opt_radius, "Radius for circles",
97     NULL, ARGTYPE_STRING, ARG_NOMINMAX
98   },
99   ARG_TERMINATOR
100 };
101 
102 typedef struct guid {
103   unsigned long l;
104   unsigned short s[3];
105   unsigned char c[6];
106 } GUID;
107 
108 #include "an1sym.h"
109 
110 #define ReadShort(f) gbfgetint16(f)
111 #define WriteShort(f,s) gbfputint16((s),f)
112 #define ReadLong(f) gbfgetint32(f)
113 #define WriteLong(f,l) gbfputint32((l),f)
114 #define ReadDouble(f) gbfgetdbl(f)
115 #define WriteDouble(f,d) gbfputdbl((d),f)
116 
117 static char*
ReadString(gbfile * f,short len)118 ReadString(gbfile* f, short len)
119 {
120   char* result = NULL;
121   result = (char*)xcalloc(1, len + 1);
122   if (len) {
123     gbfread(result, 1, len, f);
124   }
125   return result;
126 }
127 
128 #define ReadChar(f) (unsigned char) gbfgetc(f)
129 #define WriteChar(f,c) gbfputc((unsigned char)(c),f)
130 #define WriteString(f,s) gbfputs((s),f)
131 
132 static void
ReadGuid(gbfile * f,GUID * guid)133 ReadGuid(gbfile* f, GUID* guid)
134 {
135   int i = 0;
136   guid->l = ReadLong(f);
137   for (i = 0; i < 3; i++) {
138     guid->s[i] = ReadShort(f);
139   }
140   for (i = 0; i < 6; i++) {
141     guid->c[i] = ReadChar(f);
142   }
143 }
144 
145 static void
WriteGuid(gbfile * f,GUID * guid)146 WriteGuid(gbfile* f, GUID* guid)
147 {
148   int i = 0;
149   WriteLong(f, guid->l);
150   for (i = 0; i < 3; i++) {
151     WriteShort(f, guid->s[i]);
152   }
153   for (i = 0; i < 6; i++) {
154     WriteChar(f, guid->c[i]);
155   }
156 }
157 
158 static void
Skip(gbfile * f,unsigned long distance)159 Skip(gbfile* f,
160      unsigned long distance)
161 {
162   gbfseek(f, distance, SEEK_CUR);
163 }
164 
165 static double
DecodeOrd(long ord)166 DecodeOrd(long ord)
167 {
168   return (double)((gbint32)(0x80000000 - ord)) / 0x800000;
169 }
170 
171 static long
EncodeOrd(double ord)172 EncodeOrd(double ord)
173 {
174   return (gbint32)(0x80000000 - (gbint32)(ord * 0x800000));
175 }
176 
177 typedef struct {
178   short hotspotxhi;
179   long hotspoty;
180   long unk1;
181   GUID guid;
182   char* name;
183 } an1_symbol_record;
184 
185 typedef struct {
186   format_specific_data fs;
187   short magic;
188   long unk1;
189   long lon;
190   long lat;
191   short type;
192   long height;
193   long width;
194   short unk2;
195   short unk3;
196   short serial;
197   short unk4;
198   unsigned char create_zoom;
199   unsigned char visible_zoom;
200   short unk5;
201   double radius; /* in km */
202   char* name;
203   char* fontname;
204   GUID guid;
205   long fontcolor;
206   long fontstyle;
207   long fontsize;
208   long outlineweight;
209   long outlinecolor;
210   long outlineflags;
211   long fillcolor;
212   long unk6;
213   long fillflags;
214 
215   /* Added in SA2006/Topo 6.0 */
216   short unk6_1;
217   char* url;
218   char* comment;
219   long creation_time;
220   long modification_time;
221   char* image_name;
222 } an1_waypoint_record;
223 
224 typedef struct {
225   format_specific_data fs;
226   short magic;
227   long unk0;
228   long lon;
229   long lat;
230   short unk1;
231 } an1_vertex_record;
232 
233 typedef struct {
234   format_specific_data fs;
235   long roadtype;
236   short serial;
237   long unk2;
238   short unk3;
239   short type;
240   long unk4;
241   char* name;
242   long lineweight;
243   long linestyle;
244   long linecolor;
245   long opacity;
246   long polyfillcolor;
247   long unk6;
248   long unk7;
249   short unk8;
250   long pointcount;
251 } an1_line_record;
252 
253 static an1_waypoint_record* Alloc_AN1_Waypoint();
254 
Destroy_AN1_Waypoint(void * vwpt)255 void Destroy_AN1_Waypoint(void* vwpt)
256 {
257 
258   an1_waypoint_record* wpt = (an1_waypoint_record*)vwpt;
259   xfree(wpt->name);
260   xfree(wpt->fontname);
261   if (wpt->url) {
262     xfree(wpt->url);
263   }
264   if (wpt->comment) {
265     xfree(wpt->comment);
266   }
267   if (wpt->image_name) {
268     xfree(wpt->image_name);
269   }
270   xfree(vwpt);
271 }
272 
Copy_AN1_Waypoint(void ** vdwpt,void * vwpt)273 void Copy_AN1_Waypoint(void** vdwpt, void* vwpt)
274 {
275   an1_waypoint_record* wpt = (an1_waypoint_record*)vwpt;
276   an1_waypoint_record* dwpt = Alloc_AN1_Waypoint();
277   memcpy(dwpt, wpt, sizeof(an1_waypoint_record));
278   dwpt->name = xstrdup(wpt->name);
279   dwpt->fontname = xstrdup(wpt->fontname);
280   dwpt->url = xstrdup(wpt->url);
281   dwpt->comment = xstrdup(wpt->comment);
282   dwpt->image_name = xstrdup(wpt->image_name);
283   *vdwpt = (void*)dwpt;
284 }
285 
Alloc_AN1_Waypoint()286 static an1_waypoint_record* Alloc_AN1_Waypoint()
287 {
288   an1_waypoint_record* result = NULL;
289   result = (an1_waypoint_record*)xcalloc(sizeof(*result), 1);
290   result->fs.type = FS_AN1W;
291   result->fs.copy = Copy_AN1_Waypoint;
292   result->fs.destroy = Destroy_AN1_Waypoint;
293   result->fs.convert = NULL;
294   return result;
295 }
296 
297 static an1_vertex_record* Alloc_AN1_Vertex();
298 
Destroy_AN1_Vertex(void * vvertex)299 void Destroy_AN1_Vertex(void* vvertex)
300 {
301   xfree(vvertex);
302 }
303 
Copy_AN1_Vertex(void ** vdvert,void * vvert)304 void Copy_AN1_Vertex(void** vdvert, void* vvert)
305 {
306   an1_vertex_record* vert = (an1_vertex_record*)vvert;
307   an1_vertex_record* dvert = Alloc_AN1_Vertex();
308   memcpy(dvert, vert, sizeof(an1_vertex_record));
309   *vdvert = (void*)dvert;
310 }
311 
Alloc_AN1_Vertex()312 static an1_vertex_record* Alloc_AN1_Vertex()
313 {
314   an1_vertex_record* result = NULL;
315   result = (an1_vertex_record*)xcalloc(sizeof(*result), 1);
316   result->fs.type = FS_AN1V;
317   result->fs.copy = Copy_AN1_Vertex;
318   result->fs.destroy = Destroy_AN1_Vertex;
319   result->fs.convert = NULL;
320   return result;
321 }
322 
323 
324 static an1_line_record* Alloc_AN1_Line();
325 
Destroy_AN1_Line(void * vline)326 void Destroy_AN1_Line(void* vline)
327 {
328   an1_line_record* line = (an1_line_record*)vline;
329   xfree(line->name);
330   xfree(vline);
331 }
332 
Copy_AN1_Line(void ** vdline,void * vline)333 void Copy_AN1_Line(void** vdline, void* vline)
334 {
335   an1_line_record* line = (an1_line_record*)vline;
336   an1_line_record* dline = Alloc_AN1_Line();
337   memcpy(dline, line, sizeof(an1_line_record));
338   dline->name = xstrdup(line->name);
339   *vdline = (void*)dline;
340 }
341 
Alloc_AN1_Line()342 static an1_line_record* Alloc_AN1_Line()
343 {
344   an1_line_record* result = NULL;
345   result = (an1_line_record*)xcalloc(sizeof(*result), 1);
346   result->fs.type = FS_AN1L;
347   result->fs.copy = Copy_AN1_Line;
348   result->fs.destroy = Destroy_AN1_Line;
349   result->fs.convert = NULL;
350   return result;
351 }
352 
353 
Destroy_AN1_Symbol(an1_symbol_record * symbol)354 static void Destroy_AN1_Symbol(an1_symbol_record* symbol)
355 {
356   xfree(symbol->name);
357 }
358 
Read_AN1_Waypoint(gbfile * f,an1_waypoint_record * wpt)359 static void Read_AN1_Waypoint(gbfile* f, an1_waypoint_record* wpt)
360 {
361   short len;
362 
363   wpt->magic = ReadShort(f);
364   wpt->unk1 = ReadLong(f);
365   wpt->lon = ReadLong(f);
366   wpt->lat = ReadLong(f);
367   wpt->type = ReadShort(f);
368   wpt->height = ReadLong(f);
369   wpt->width = ReadLong(f);
370   wpt->unk2 = ReadShort(f);
371   wpt->unk3 = ReadShort(f);
372   wpt->serial = ReadShort(f);
373   wpt->unk4 = ReadShort(f);
374   wpt->create_zoom = ReadChar(f);
375   wpt->visible_zoom = ReadChar(f);
376   wpt->unk5 = ReadShort(f);
377   wpt->radius = ReadDouble(f);
378   len = ReadShort(f);
379   wpt->name = ReadString(f, len);
380 
381   if (len != strlen(wpt->name)) {
382     /* This happens in 06/6.0 files that put extra data in the
383      * name record for backward compatibility's sake */
384     char* ofs = wpt->name + strlen(wpt->name) + 1;
385     wpt->unk6_1 = le_read16(ofs);
386     ofs += 2;
387 
388     len = le_read16(ofs);
389     ofs += 2;
390 
391     if (len) {
392       char* oldurlstr;
393       /*
394        * Trust URL encoded in new format over one in
395        * old format if both are present.  Whack the
396        * name starting at '{URL='.
397        */
398       oldurlstr = strstr(wpt->name, "{URL=");
399       if (oldurlstr) {
400         *oldurlstr = 0;
401       }
402 
403       wpt->url = (char*) xcalloc(len+1, 1);
404       memcpy(wpt->url, ofs, len);
405       ofs += len;
406     }
407 
408     len = le_read16(ofs);
409     ofs += 2;
410 
411     if (len) {
412       wpt->comment = (char*) xcalloc(len+1, 1);
413       memcpy(wpt->comment, ofs, len);
414       ofs += len;
415     }
416 
417     /* these are quadwords, presumably for year-2038 compat. */
418     wpt->creation_time = le_read32(ofs);
419     ofs += 8;
420 
421     wpt->modification_time = le_read32(ofs);
422     ofs += 8;
423   }
424 
425   if (wpt->type == 0x12) {
426     /* 'image' type */
427     ReadShort(f);    /* length of font + filename */
428     len = ReadShort(f);
429     wpt->fontname = ReadString(f, len);
430     len = ReadShort(f);
431     wpt->image_name = ReadString(f, len);
432   } else {
433     len = ReadShort(f);
434     wpt->fontname = ReadString(f, len);
435     wpt->image_name = NULL;
436   }
437   ReadGuid(f, &wpt->guid);
438   wpt->fontcolor = ReadLong(f);
439   wpt->fontstyle = ReadLong(f);
440   wpt->fontsize = ReadLong(f);
441   wpt->outlineweight = ReadLong(f);
442   wpt->outlinecolor = ReadLong(f);
443   wpt->outlineflags = ReadLong(f);
444   wpt->fillcolor = ReadLong(f);
445   wpt->unk6 = ReadLong(f);
446   wpt->fillflags = ReadLong(f);
447 }
448 
Write_AN1_Waypoint(gbfile * f,an1_waypoint_record * wpt)449 static void Write_AN1_Waypoint(gbfile* f, an1_waypoint_record* wpt)
450 {
451   short len;
452 
453   WriteShort(f, wpt->magic);
454   WriteLong(f, wpt->unk1);
455   WriteLong(f, wpt->lon);
456   WriteLong(f, wpt->lat);
457   WriteShort(f, wpt->type);
458   WriteLong(f, wpt->height);
459   WriteLong(f, wpt->width);
460   WriteShort(f, wpt->unk2);
461   WriteShort(f, wpt->unk3);
462   WriteShort(f, wpt->serial);
463   WriteShort(f, wpt->unk4);
464   WriteChar(f, wpt->create_zoom);
465   WriteChar(f, wpt->visible_zoom);
466   WriteShort(f, wpt->unk5);
467   WriteDouble(f, wpt->radius);
468 
469   len = strlen(wpt->name) + 1 + 2 + 2 +
470         (wpt->url ? strlen(wpt->url) : 0) + 2 +
471         (wpt->comment ? strlen(wpt->comment) : 0) + 8 + 8;
472   WriteShort(f, len);
473   WriteString(f, wpt->name);
474   WriteChar(f, 0);    /* name string terminator */
475 
476   WriteShort(f, wpt->unk6_1);
477 
478   if (wpt->url) {
479     WriteShort(f, strlen(wpt->url));
480     WriteString(f, wpt->url);
481   } else {
482     WriteShort(f, 0);
483   }
484 
485   if (wpt->comment) {
486     WriteShort(f, strlen(wpt->comment));
487     WriteString(f, wpt->comment);
488   } else {
489     WriteShort(f, 0);
490   }
491 
492   WriteLong(f, wpt->creation_time);
493   WriteLong(f, 0);
494 
495   WriteLong(f, wpt->modification_time);
496   WriteLong(f, 0);
497 
498   if (wpt->type == 0x12) {   /* image */
499     len = 2 + (wpt->fontname ? strlen(wpt->fontname) : 0) +
500           2 + (wpt->image_name ? strlen(wpt->image_name) : 0);
501     WriteShort(f, len);
502     if (wpt->fontname) {
503       len = strlen(wpt->fontname);
504       WriteShort(f, len);
505       WriteString(f, wpt->fontname);
506     } else {
507       WriteShort(f, 0);
508     }
509     if (wpt->image_name) {
510       len = strlen(wpt->image_name);
511       WriteShort(f, len);
512       WriteString(f, wpt->image_name);
513     } else {
514       WriteShort(f, 0);
515     }
516   } else {
517     len = strlen(wpt->fontname);
518     WriteShort(f, len);
519     WriteString(f, wpt->fontname);
520   }
521   WriteGuid(f, &wpt->guid);
522   WriteLong(f, wpt->fontcolor);
523   WriteLong(f, wpt->fontstyle);
524   WriteLong(f, wpt->fontsize);
525   WriteLong(f, wpt->outlineweight);
526   WriteLong(f, wpt->outlinecolor);
527   WriteLong(f, wpt->outlineflags);
528   WriteLong(f, wpt->fillcolor);
529   WriteLong(f, wpt->unk6);
530   WriteLong(f, wpt->fillflags);
531 }
532 
Read_AN1_Vertex(gbfile * f,an1_vertex_record * vertex)533 static void Read_AN1_Vertex(gbfile* f, an1_vertex_record* vertex)
534 {
535 
536   vertex->magic = ReadShort(f);
537   vertex->unk0 = ReadLong(f);
538   vertex->lon = ReadLong(f);
539   vertex->lat = ReadLong(f);
540   vertex->unk1 = ReadShort(f);
541 }
542 
Write_AN1_Vertex(gbfile * f,an1_vertex_record * vertex)543 static void Write_AN1_Vertex(gbfile* f, an1_vertex_record* vertex)
544 {
545   WriteShort(f, vertex->magic);
546   WriteLong(f, vertex->unk0);
547   WriteLong(f, vertex->lon);
548   WriteLong(f, vertex->lat);
549   WriteShort(f, vertex->unk1);
550 }
551 
Read_AN1_Line(gbfile * f,an1_line_record * line)552 static void Read_AN1_Line(gbfile* f, an1_line_record* line)
553 {
554 
555   short len;
556 
557   line->roadtype = ReadLong(f);
558   line->serial = ReadShort(f);
559   line->unk2 = ReadLong(f);
560   line->unk3 = ReadShort(f);
561   line->type = ReadShort(f);
562   line->unk4 = ReadLong(f);
563   len = ReadShort(f);
564   line->name = ReadString(f, len);
565   line->lineweight = ReadShort(f);
566   line->linestyle = ReadLong(f);
567   line->linecolor = ReadLong(f);
568   line->opacity = ReadLong(f);
569   line->polyfillcolor = ReadLong(f);
570   line->unk6 = ReadLong(f);
571   line->unk7 = ReadLong(f);
572   line->unk8 = ReadShort(f);
573   line->pointcount = ReadLong(f);
574 }
575 
Write_AN1_Line(gbfile * f,an1_line_record * line)576 static void Write_AN1_Line(gbfile* f, an1_line_record* line)
577 {
578   short len;
579 
580   WriteLong(f, line->roadtype);
581   WriteShort(f, line->serial);
582   WriteLong(f, line->unk2);
583   WriteShort(f, line->unk3);
584   WriteShort(f, line->type);
585   WriteLong(f, line->unk4);
586   len = strlen(line->name);
587   WriteShort(f, len);
588   WriteString(f, line->name);
589   WriteShort(f, (short) line->lineweight);
590   WriteLong(f, line->linestyle);
591   WriteLong(f, line->linecolor);
592   WriteLong(f, line->opacity);
593   WriteLong(f, line->polyfillcolor);
594   WriteLong(f, line->unk6);
595   WriteLong(f, line->unk7);
596   WriteShort(f, line->unk8);
597   WriteLong(f, line->pointcount);
598 }
599 
Skip_AN1_IL(gbfile * f)600 static void Skip_AN1_IL(gbfile* f)
601 {
602   Skip(f, 26);
603 }
604 
Skip_AN1_BM(gbfile * f)605 static void Skip_AN1_BM(gbfile* f)
606 {
607   unsigned long bmsize;
608   unsigned long palettesize;
609   unsigned long bmisize;
610   unsigned long bitoffset;
611 
612   Skip(f, 8);    /* BITMAPFILEHEADER fields 1-3 */
613   bitoffset = ReadLong(f);
614 
615   bmisize = ReadLong(f);
616   Skip(f, 16);    /* BITMAPINFOHEADER fields 2-6 */
617   bmsize = ReadLong(f);
618   Skip(f, 16);    /* BITMAPINFOHEADER fields 8-11 */
619 
620   palettesize = bitoffset - bmisize - 14;
621   Skip(f, bmsize + palettesize);
622 }
623 
Read_AN1_Symbol(gbfile * f,an1_symbol_record * symbol)624 static void Read_AN1_Symbol(gbfile* f, an1_symbol_record* symbol)
625 {
626   short len;
627 
628   /* This is just the high word of a long; we ate the low
629    * word in the caller.  Fortunately, we don't care. */
630   symbol->hotspotxhi = ReadShort(f);
631   symbol->hotspoty = ReadLong(f);
632   symbol->unk1 = ReadLong(f);
633   ReadGuid(f, &symbol->guid);
634   len = ReadChar(f);
635   symbol->name = ReadString(f, len);
636 }
637 
Read_AN1_Header(gbfile * f)638 static void Read_AN1_Header(gbfile* f)
639 {
640   unsigned short magic;
641   unsigned short type;
642 
643   magic = ReadShort(f);
644   type = ReadShort(f);
645 
646   last_read_type = type;
647 }
648 
Write_AN1_Header(gbfile * f)649 static void Write_AN1_Header(gbfile* f)
650 {
651   WriteShort(f, 11557);
652   WriteShort(f, output_type_num);
653 }
654 
Read_AN1_Bitmaps(gbfile * f)655 static void Read_AN1_Bitmaps(gbfile* f)
656 {
657   long count;
658   unsigned short magic;
659   an1_symbol_record symbol;
660 
661   count = ReadLong(f);
662 
663   while (count) {
664     magic = ReadShort(f);
665     switch (magic) {
666     case 0x4d42:
667       Skip_AN1_BM(f);
668       break;
669     case 0x4c49:
670       Skip_AN1_IL(f);
671       break;
672     default:
673       Read_AN1_Symbol(f, &symbol);
674       Destroy_AN1_Symbol(&symbol);
675       count--;
676       break;
677     }
678   }
679 
680   /* Read the symbol table */
681 }
682 
Write_AN1_Bitmaps(gbfile * f)683 static void Write_AN1_Bitmaps(gbfile* f)
684 {
685   /* On write, we don't output any bitmaps, so writing them
686    * is just a matter of writing a count of zero */
687   WriteLong(f, 0);
688 }
689 
Read_AN1_Waypoints(gbfile * f)690 static void Read_AN1_Waypoints(gbfile* f)
691 {
692   unsigned long count = 0;
693   unsigned long i = 0;
694   an1_waypoint_record* rec = NULL;
695   waypoint* wpt_tmp;
696   char* icon = NULL;
697   char* url = NULL;
698   ReadShort(f);
699   count = ReadLong(f);
700   for (i = 0; i < count; i++) {
701     rec = Alloc_AN1_Waypoint();
702     Read_AN1_Waypoint(f, rec);
703     wpt_tmp = waypt_new();
704 
705     if (rec->creation_time) {
706       wpt_tmp->creation_time = rec->creation_time;
707     }
708     wpt_tmp->longitude = -DecodeOrd(rec->lon);
709     wpt_tmp->latitude = DecodeOrd(rec->lat);
710     wpt_tmp->notes = xstrdup(rec->comment);
711     wpt_tmp->description = xstrdup(rec->name);
712     if (rec->url) {
713       wpt_tmp->url = xstrdup(rec->url);
714     } else if (NULL != (url=strstr(wpt_tmp->description, "{URL="))) {
715       *url = '\0';
716       url += 5;
717       url[strlen(url)-1] = '\0';
718       wpt_tmp->url = xstrdup(url);
719     }
720 
721     if (rec->image_name) {
722       wpt_tmp->icon_descr = xstrdup(rec->image_name);
723     } else if (FindIconByGuid(&rec->guid, &icon)) {
724       wpt_tmp->icon_descr = icon;
725     }
726 
727     fs_chain_add(&(wpt_tmp->fs), (format_specific_data*)rec);
728     rec = NULL;
729     waypt_add(wpt_tmp);
730   }
731 }
732 
733 static void
Write_One_AN1_Waypoint(const waypoint * wpt)734 Write_One_AN1_Waypoint(const waypoint* wpt)
735 {
736   an1_waypoint_record* rec;
737   int local;
738   format_specific_data* fs = NULL;
739 
740   fs = fs_chain_find(wpt->fs, FS_AN1W);
741   if (fs) {
742     rec = (an1_waypoint_record*)fs;
743     xfree(rec->name);
744     local = 0;
745     if (opt_zoom) {
746       rec->visible_zoom = opt_zoom_num;
747     }
748   } else {
749     rec = Alloc_AN1_Waypoint();
750     local = 1;
751     rec->magic = 1;
752     rec->type = wpt_type_num;
753     rec->unk2 = 3;
754     rec->unk3 = 18561;
755     rec->radius = radius;
756     rec->fillcolor = opt_color_num;
757     rec->fillflags = 3;
758     if (wpt_type_num == 5) {
759       rec->fillflags = 0x8200;
760     }
761     rec->height = -50;
762     rec->width = 20;
763     rec->fontname = xstrdup("Arial");
764     FindIconByName(opt_symbol, &rec->guid);
765     rec->fontsize = 10;
766     rec->visible_zoom = opt_zoom?opt_zoom_num:10;
767     rec->unk6_1 = 1;
768   }
769   rec->name = xstrdup(wpt->description);
770 
771   if (!nogc && wpt->gc_data->id) {
772     char* extra = (char*) xmalloc(25 + strlen(wpt->gc_data->placer) + strlen(wpt->shortname));
773     sprintf(extra, "\r\nBy %s\r\n%s (%1.1f/%1.1f)",
774             wpt->gc_data->placer,
775             wpt->shortname, wpt->gc_data->diff/10.0,
776             wpt->gc_data->terr/10.0);
777     rec->name = xstrappend(rec->name, extra);
778     xfree(extra);
779   }
780 
781   if (!nourl && wpt->url) {
782     int len = 7+strlen(wpt->url);
783     char* extra = (char*)xmalloc(len);
784     sprintf(extra, "{URL=%s}", wpt->url);
785     rec->name = xstrappend(rec->name, extra);
786     xfree(extra);
787     rec->url = xstrdup(wpt->url);
788   }
789 
790   if (wpt->notes) {
791     if (rec->comment) {
792       xfree(rec->comment);
793     }
794     rec->comment = xstrdup(wpt->notes);
795   }
796 
797 
798   rec->creation_time = rec->modification_time = wpt->creation_time;
799   rec->lat = EncodeOrd(wpt->latitude);
800   rec->lon = EncodeOrd(-wpt->longitude);
801   rec->serial = serial++;
802 
803   if (rec->type == 0x12) {    /* image */
804     if (strstr(wpt->icon_descr, ":\\")) {
805       rec->image_name = xstrdup(wpt->icon_descr);
806       rec->height = -244;
807       rec->width = -1;
808     }
809   }
810   if (!rec->image_name && wpt->icon_descr) {
811     FindIconByName((char*)(void*)wpt->icon_descr, &rec->guid);
812   }
813 
814   Write_AN1_Waypoint(outfile, rec);
815   if (local) {
816     Destroy_AN1_Waypoint(rec);
817   }
818 }
819 
Write_AN1_Waypoints(gbfile * f)820 static void Write_AN1_Waypoints(gbfile* f)
821 {
822   WriteShort(f, 2);
823   WriteLong(f, waypt_count());
824   waypt_disp_all(Write_One_AN1_Waypoint);
825 }
826 
Read_AN1_Lines(gbfile * f)827 static void Read_AN1_Lines(gbfile* f)
828 {
829   unsigned long count = 0;
830   unsigned long i = 0;
831   unsigned long j = 0;
832   an1_line_record* rec = NULL;
833   an1_vertex_record* vert = NULL;
834   route_head* rte_head;
835   waypoint* wpt_tmp;
836 
837   ReadShort(f);
838   count = ReadLong(f);
839   for (i = 0; i < count; i++) {
840     rec = Alloc_AN1_Line();
841     Read_AN1_Line(f, rec);
842     /* create route rec */
843     rte_head = route_head_alloc();
844     rte_head->line_color.bbggrr = rec->linecolor;
845     if (rec->opacity == 0x8200) {
846       rte_head->line_color.opacity = 128;
847     }
848     // lineweight isn't set for dashed/dotted lines
849     // Since we don't have a way to represent this internally yet,
850     // use leave line_width at the default.
851     if (rec->lineweight) {
852       rte_head->line_width = rec->lineweight;
853     }
854     rte_head->rte_name = xstrdup(rec->name);
855     fs_chain_add(&rte_head->fs, (format_specific_data*)rec);
856     route_add_head(rte_head);
857     for (j = 0; j < (unsigned) rec->pointcount; j++) {
858       vert = Alloc_AN1_Vertex();
859       Read_AN1_Vertex(f, vert);
860 
861       /* create route point */
862       wpt_tmp = waypt_new();
863       wpt_tmp->latitude = DecodeOrd(vert->lat);
864       wpt_tmp->longitude = -DecodeOrd(vert->lon);
865       wpt_tmp->shortname = (char*) xmalloc(7);
866       sprintf(wpt_tmp->shortname, "\\%5.5lx", rtserial++);
867       fs_chain_add(&wpt_tmp->fs,
868                    (format_specific_data*)vert);
869       route_add_wpt(rte_head, wpt_tmp);
870     }
871   }
872 }
873 
874 static void
Make_Road_Changes(an1_line_record * rec)875 Make_Road_Changes(an1_line_record* rec)
876 {
877   int i = 0;
878 
879   if (!rec) {
880     return;
881   }
882 
883   if (!roadchanges) {
884     return;
885   }
886 
887   while (roadchanges[i].name) {
888     if (!case_ignore_strcmp(roadchanges[i].name, rec->name)) {
889       rec->roadtype = roadchanges[i].type;
890       break;
891     }
892     i++;
893   }
894 }
895 
896 static void
Write_One_AN1_Line(const route_head * rte)897 Write_One_AN1_Line(const route_head* rte)
898 {
899   an1_line_record* rec;
900   int local;
901   format_specific_data* fs = NULL;
902 
903   fs = fs_chain_find(rte->fs, FS_AN1L);
904 
905   if (fs) {
906     rec = (an1_line_record*)(void*)fs;
907     local = 0;
908     switch (output_type_num) {
909     case 1:
910       if (rec->type != 14) {
911         rec = Alloc_AN1_Line();
912         memcpy(rec, fs, sizeof(an1_line_record));
913         local = 1;
914         rec->roadtype = 0x11100541;
915         rec->unk2 = 655360;
916         rec->type = 14;
917         rec->unk8 = 2;
918       } // end if
919       Make_Road_Changes(rec);
920       break;
921     case 2:
922       if (rec->type != 15) {
923         rec = Alloc_AN1_Line();
924         memcpy(rec, fs, sizeof(an1_line_record));
925         local = 1;
926         rec->type = 15;
927       } // end if
928       break;
929     case 4:
930       if (rec->type != 16) {
931         rec = Alloc_AN1_Line();
932         memcpy(rec, fs, sizeof(an1_line_record));
933         local = 1;
934         rec->type = 16;
935       } // end if
936       break;
937     }
938   } else {
939     rec = Alloc_AN1_Line();
940     local = 1;
941     rec->name = NULL;
942     switch (output_type_num) {
943       /*  drawing road trail waypoint track  */
944     case 1: /* road */
945       rec->roadtype = 0x11100541;
946       rec->unk2 = 655360;
947       rec->type = 14;
948       rec->unk8 = 2;
949       rec->name = xstrdup(rte->rte_name);
950       break;
951 
952     case 2: /* trail */
953       rec->roadtype = 0x11071c50;
954       rec->unk2 = 917504;
955       rec->type = 15;
956       rec->unk8 = 2;
957       break;
958 
959     case 4: /* track */
960       rec->roadtype = 0x48800015;
961       rec->unk2 = 917504;
962       rec->type = 16;
963       rec->unk4 = 2;
964       rec->unk8 = 2;
965       break;
966 
967     case 0: /* drawing */
968     case 3: /* waypoint - shouldn't have lines */
969     default:
970       rec->roadtype = 0x48800015;
971       rec->unk2 = 1048576;
972       rec->type = 2;
973       rec->unk4 = 2;
974       rec->lineweight = 6;
975       rec->linecolor = opt_color_num; /* red */
976       rec->opacity = 3;
977       rec->unk8 = 2;
978       break;
979     }
980     if (!rec->name) {
981       rec->name = xstrdup("");
982     }
983 
984   }
985   rec->serial = serial++;
986   rec->pointcount = rte->rte_waypt_ct;
987   Write_AN1_Line(outfile, rec);
988   if (local) {
989     Destroy_AN1_Line(rec);
990   }
991 }
992 
993 static void
Write_One_AN1_Vertex(const waypoint * wpt)994 Write_One_AN1_Vertex(const waypoint* wpt)
995 {
996   an1_vertex_record* rec;
997   int local;
998   format_specific_data* fs = NULL;
999 
1000   fs = fs_chain_find(wpt->fs, FS_AN1V);
1001 
1002   if (fs) {
1003     rec = (an1_vertex_record*)(void*)fs;
1004     local = 0;
1005   } else {
1006     rec = Alloc_AN1_Vertex();
1007     local = 1;
1008     rec->magic = 1;
1009   }
1010   rec->lat = EncodeOrd(wpt->latitude);
1011   rec->lon = EncodeOrd(-wpt->longitude);
1012 
1013   Write_AN1_Vertex(outfile, rec);
1014   if (local) {
1015     Destroy_AN1_Vertex(rec);
1016   }
1017 }
1018 
Write_AN1_Lines(gbfile * f)1019 static void Write_AN1_Lines(gbfile* f)
1020 {
1021   WriteShort(f, 2);
1022   WriteLong(f, route_count()+track_count());
1023 
1024   route_disp_all(Write_One_AN1_Line, NULL, Write_One_AN1_Vertex);
1025   track_disp_all(Write_One_AN1_Line, NULL, Write_One_AN1_Vertex);
1026 }
1027 
1028 static void
Init_Wpt_Type(void)1029 Init_Wpt_Type(void)
1030 {
1031   if (!opt_wpt_type || !opt_wpt_type[0]) {
1032     wpt_type_num = 1; /* marker */
1033     return;
1034   }
1035   if ((opt_wpt_type[0] & 0xf0) == 0x30) {
1036     wpt_type_num = atoi(opt_wpt_type);
1037   } else {
1038     wpt_type_num = 1; /* marker */
1039     if (!case_ignore_strcmp(opt_wpt_type, "marker")) {
1040       wpt_type_num = 1;
1041     } else if (!case_ignore_strcmp(opt_wpt_type, "symbol")) {
1042       wpt_type_num = 1; /* symbol and marker are synonyms */
1043     } else if (!case_ignore_strcmp(opt_wpt_type, "text")) {
1044       wpt_type_num = 4;
1045     } else if (!case_ignore_strcmp(opt_wpt_type, "mapnote")) {
1046       wpt_type_num = 6;
1047     } else if (!case_ignore_strcmp(opt_wpt_type, "circle")) {
1048       wpt_type_num = 5;
1049     } else if (!case_ignore_strcmp(opt_wpt_type, "image")) {
1050       wpt_type_num = 18;
1051     } else {
1052       fatal(MYNAME ": wpt_type must be "
1053             "symbol, text, mapnote, circle, or image\n");
1054     }
1055   }
1056 }
1057 
1058 static void
Init_Output_Type(void)1059 Init_Output_Type(void)
1060 {
1061   if (!output_type || !output_type[0]) {
1062     output_type_num = last_read_type;
1063     return;
1064   }
1065   if ((output_type[0] & 0xf0) == 0x30) {
1066     output_type_num = atoi(output_type);
1067   } else {
1068     output_type_num = 0;
1069     if (!case_ignore_strcmp(output_type, "drawing")) {
1070       output_type_num = 0;
1071     } else if (!case_ignore_strcmp(output_type, "road")) {
1072       output_type_num = 1;
1073     } else if (!case_ignore_strcmp(output_type, "trail")) {
1074       output_type_num = 2;
1075     } else if (!case_ignore_strcmp(output_type, "waypoint")) {
1076       output_type_num = 3;
1077     } else if (!case_ignore_strcmp(output_type, "track")) {
1078       output_type_num = 4;
1079     } else {
1080       fatal(MYNAME ": type must be "
1081             "drawing, road, trail, waypoint, or track\n");
1082     }
1083   }
1084   last_read_type = output_type_num;
1085 }
1086 
1087 static long
Parse_Change_Type(char * type)1088 Parse_Change_Type(char* type)
1089 {
1090   long retval = 0x11100541;
1091 
1092   if (!case_ignore_strcmp(type, "limited")) {
1093     retval = 0x11070430;
1094   } else if (!case_ignore_strcmp(type, "toll")) {
1095     retval = 0x11070470;
1096   } else if (!case_ignore_strcmp(type, "us")) {
1097     retval = 0x11070870;
1098   } else if (!case_ignore_strcmp(type, "state")) {
1099     retval = 0x11070c10;
1100   } else if (!case_ignore_strcmp(type, "primary")) {
1101     /* primary state/provincial routes */
1102     retval = 0x11070840;
1103   } else if (!case_ignore_strcmp(type, "major")) {
1104     retval = 0x11070c30;
1105   } else if (!case_ignore_strcmp(type, "local")) {
1106     retval = 0x11071010;
1107   } else if (!case_ignore_strcmp(type, "ramp")) {
1108     retval = 0x11070cb0;
1109   } else if (!case_ignore_strcmp(type, "ferry")) {
1110     retval = 0x11070ca0;
1111   } else if (!case_ignore_strcmp(type, "editable")) {
1112     retval = 0x11100541;
1113   } else {
1114     fatal(MYNAME ": unknown road type for road changes\n");
1115   }
1116   return retval;
1117 }
1118 
1119 static void
Free_Road_Changes(void)1120 Free_Road_Changes(void)
1121 {
1122   int i = 0;
1123   if (roadchanges) {
1124     while (roadchanges[i].name) {
1125       xfree(roadchanges[i].name);
1126       i++;
1127     }
1128     xfree(roadchanges);
1129   }
1130   roadchanges = NULL;
1131 }
1132 
1133 static void
Init_Road_Changes(void)1134 Init_Road_Changes(void)
1135 {
1136   int count = 0;
1137   char* strType = NULL;
1138   char* name = NULL;
1139   char* bar = NULL;
1140   char* copy = NULL;
1141   Free_Road_Changes();
1142 
1143   if (!road_changes || !road_changes[0]) {
1144     return;
1145   }
1146   bar = strchr(road_changes, '!');
1147   while (bar) {
1148     count++;
1149     bar = strchr(bar+1, '!');
1150   }
1151   if (!(count&1)) {
1152     fatal(MYNAME ": invalid format for road changes\n");
1153   }
1154   count = 1 + count / 2;
1155   roadchanges = (roadchange*)xmalloc((count+1) * sizeof(roadchange));
1156 
1157   roadchanges[count].type = 0;
1158   roadchanges[count].name = NULL;
1159 
1160   copy = xstrdup(road_changes);
1161   bar = copy;
1162 
1163   while (count) {
1164     count--;
1165     name = bar;
1166     bar = strchr(name, '!');
1167     *bar = '\0';
1168     bar++;
1169     strType = bar;
1170     bar = strchr(strType, '!');
1171     if (bar) {
1172       *bar = '\0';
1173       bar++;
1174     }
1175     roadchanges[count].name = xstrdup(name);
1176     roadchanges[count].type = Parse_Change_Type(strType);
1177   }
1178 
1179   xfree(copy);
1180 }
1181 
1182 static void
rd_init(const char * fname)1183 rd_init(const char* fname)
1184 {
1185   infile = gbfopen_le(fname, "rb", MYNAME);
1186 }
1187 
1188 static void
rd_deinit(void)1189 rd_deinit(void)
1190 {
1191   gbfclose(infile);
1192 }
1193 
1194 static void
my_read(void)1195 my_read(void)
1196 {
1197   Read_AN1_Header(infile);
1198   Read_AN1_Bitmaps(infile);
1199   Read_AN1_Waypoints(infile);
1200   Read_AN1_Lines(infile);
1201 }
1202 
1203 static void
wr_init(const char * fname)1204 wr_init(const char* fname)
1205 {
1206   outfile = gbfopen_le(fname, "wb", MYNAME);
1207   Init_Output_Type();
1208   Init_Road_Changes();
1209   opt_color_num = color_to_bbggrr(opt_color);
1210   Init_Wpt_Type();
1211   if (opt_zoom) {
1212     opt_zoom_num = atoi(opt_zoom);
1213   }
1214   radius = .1609344; /* 1/10 mi */
1215   if (opt_radius) {
1216     radius = atof(opt_radius);
1217     if (!strchr(opt_radius,'k') && !strchr(opt_radius,'K')) {
1218       radius *= 5280*12*2.54/100000;
1219     }
1220   }
1221 }
1222 
1223 static void
wr_deinit(void)1224 wr_deinit(void)
1225 {
1226   Free_Road_Changes();
1227   gbfclose(outfile);
1228 }
1229 
1230 static void
my_write(void)1231 my_write(void)
1232 {
1233   Write_AN1_Header(outfile);
1234   Write_AN1_Bitmaps(outfile);
1235   Write_AN1_Waypoints(outfile);
1236   Write_AN1_Lines(outfile);
1237 }
1238 
1239 ff_vecs_t an1_vecs = {
1240   ff_type_file,
1241   {
1242     (ff_cap)(ff_cap_read | ff_cap_write)	/* waypoints */,
1243     ff_cap_write 			/* tracks */,
1244     (ff_cap)(ff_cap_read | ff_cap_write) 	/* routes */,
1245   },
1246   rd_init,
1247   wr_init,
1248   rd_deinit,
1249   wr_deinit,
1250   my_read,
1251   my_write,
1252   NULL,
1253   an1_args,
1254   CET_CHARSET_ASCII, 0	/* CET-REVIEW */
1255 };
1256