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