1 /**********************************************************************************************
2     Copyright (C) 2009 Oliver Eichler <oliver.eichler@gmx.de>
3 
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17     NOTE:   this code is based on the opensource typ file generator at
18             http://ati.land.cz/gps/typdecomp/editor.cgi
19 
20 **********************************************************************************************/
21 
22 #include "CMainWindow.h"
23 #include "map/garmin/CGarminTyp.h"
24 #include "units/IUnit.h"
25 #include <QMessageBox>
26 #include <QtCore>
27 
28 #include <stdio.h>
29 #include <string.h>
30 
31 #undef DBG
32 
33 
decode(const QByteArray & array,QMap<quint32,polygon_property> & polygons,QMap<quint32,polyline_property> & polylines,QList<quint32> & drawOrder,QMap<quint32,point_property> & points)34 bool CGarminTyp::decode(const QByteArray& array, QMap<quint32, polygon_property>& polygons, QMap<quint32, polyline_property>& polylines, QList<quint32>& drawOrder, QMap<quint32, point_property>& points)
35 {
36     QDataStream in(array);
37     in.setVersion(QDataStream::Qt_4_5);
38     in.setByteOrder( QDataStream::LittleEndian);
39 
40     /* Read typ file descriptor */
41     quint16 descriptor;
42     in >> descriptor;
43 
44     qDebug() << "descriptor" << hex << descriptor;
45 
46     if(!parseHeader(in))
47     {
48         return false;
49     }
50 
51     if(!parseDrawOrder(in, drawOrder))
52     {
53         return false;
54     }
55 
56     if(!parsePolygon(in, polygons))
57     {
58         return false;
59     }
60 
61     if(!parsePolyline(in, polylines))
62     {
63         return false;
64     }
65 
66     if(!parsePoint(in, points))
67     {
68         return false;
69     }
70 
71     return true;
72 }
73 
getCodec(quint16 codepage)74 QTextCodec* CGarminTyp::getCodec(quint16 codepage)
75 {
76     QTextCodec* codec = QTextCodec::codecForName(QString("CP%1").arg(codepage).toLatin1());
77     if(codepage == 65001)
78     {
79         codec = QTextCodec::codecForName("UTF-8");
80     }
81 
82     return codec;
83 }
84 
85 
parseHeader(QDataStream & in)86 bool CGarminTyp::parseHeader(QDataStream& in)
87 {
88     int i;
89     QString garmintyp;
90     quint8 byte;
91 
92     for(i = 0; i < 10; ++i)
93     {
94         in >> byte;
95         garmintyp.append(byte);
96     }
97     garmintyp.append(0);
98     if(garmintyp != "GARMIN TYP")
99     {
100         qDebug() << "CMapTDB::readTYP() not a known typ file";
101         return false;
102     }
103 
104     /* reading typ creation date string */
105 
106     in.device()->seek(0x0c);
107     in >> version >> year >> month >> day >> hour >> minutes >> seconds >> codepage;
108     month -= 1;                  /* Month are like Microsoft starting 0 ? */
109     year += 1900;
110 
111     /* Reading points / lines / polygons struct */
112     in >> sectPoints.dataOffset >> sectPoints.dataLength;
113     in >> sectPolylines.dataOffset >> sectPolylines.dataLength;
114     in >> sectPolygons.dataOffset >> sectPolygons.dataLength;
115 
116     in >> pid >> fid;
117 
118     /* Read Array datas */
119     in >> sectPoints.arrayOffset >> sectPoints.arrayModulo >> sectPoints.arraySize;
120     in >> sectPolylines.arrayOffset >> sectPolylines.arrayModulo >> sectPolylines.arraySize;
121     in >> sectPolygons.arrayOffset >> sectPolygons.arrayModulo >> sectPolygons.arraySize;
122     in >> sectOrder.arrayOffset >> sectOrder.arrayModulo >> sectOrder.arraySize;
123 
124 #ifdef DBG
125     qDebug() << "Version:" << version << "Codepage:" << codepage;
126     qDebug() << "PID" << hex << pid << "FID" << hex << fid;
127     qDebug() << "Points     doff/dlen/aoff/amod/asize:" << hex << "\t" << sectPoints.dataOffset << "\t" << sectPoints.dataLength << "\t" << sectPoints.arrayOffset << "\t" << sectPoints.arrayModulo << "\t" << sectPoints.arrayOffset;
128     qDebug() << "Polylines  doff/dlen/aoff/amod/asize:" << hex << "\t" << sectPolylines.dataOffset << "\t" << sectPolylines.dataLength << "\t" << sectPolylines.arrayOffset << "\t" << sectPolylines.arrayModulo << "\t" << sectPolylines.arrayOffset;
129     qDebug() << "Polygons   doff/dlen/aoff/amod/asize:" << hex << "\t" << sectPolygons.dataOffset << "\t" << sectPolygons.dataLength << "\t" << sectPolygons.arrayOffset << "\t" << sectPolygons.arrayModulo << "\t" << sectPolygons.arrayOffset;
130     qDebug() << "Order      doff/dlen/aoff/amod/asize:" << hex << "\t" << sectOrder.dataOffset << "\t" << sectOrder.dataLength << "\t" << sectOrder.arrayOffset << "\t" << sectOrder.arrayModulo << "\t" << sectOrder.arrayOffset;
131 #endif
132 
133     return true;
134 }
135 
136 
parseDrawOrder(QDataStream & in,QList<quint32> & drawOrder)137 bool CGarminTyp::parseDrawOrder(QDataStream& in, QList<quint32>& drawOrder)
138 {
139     if(sectOrder.arraySize == 0)
140     {
141         return true;
142     }
143 
144     if(sectOrder.arrayModulo != 5)
145     {
146         return false;
147     }
148 
149     if((sectOrder.arraySize % sectOrder.arrayModulo) != 0)
150     {
151         return true;
152     }
153 
154     in.device()->seek(sectOrder.arrayOffset);
155 
156     int i, n;
157     quint8 typ;
158     quint32 subtyp;
159 
160     int count = 1;
161 
162     const int N = sectOrder.arraySize / sectOrder.arrayModulo;
163 
164     for (i = 0; i < N; i++)
165     {
166         in >> typ >> subtyp;
167         //         qDebug() << hex << typ << subtyp;
168         if (typ == 0)
169         {
170             count++;
171         }
172         else if(subtyp == 0)
173         {
174 #ifdef DBG
175             qDebug() << QString("Type 0x%1 is priority %2").arg(typ, 0, 16).arg(count);
176 #endif
177             int idx = drawOrder.indexOf(typ);
178             if(idx != NOIDX)
179             {
180                 drawOrder.move(idx, 0);
181             }
182         }
183         else
184         {
185             quint32 exttyp = 0x010000 | (typ << 8);
186             quint32 mask = 0x1;
187 
188             for(n = 0; n < 0x20; ++n)
189             {
190                 if(subtyp & mask)
191                 {
192                     drawOrder.push_front(exttyp | n);
193 #ifdef DBG
194                     qDebug() << QString("Type 0x%1 is priority %2").arg(exttyp | n, 0, 16).arg(count);
195 #endif
196                 }
197                 mask = mask << 1;
198             }
199         }
200     }
201 
202 
203 #ifdef DBG
204     for(unsigned i = 0; i < drawOrder.size(); ++i)
205     {
206         if(i && i % 16 == 0)
207         {
208             printf(" \n");
209         }
210         printf("%06X ", drawOrder[i]);
211     }
212 
213 
214     printf(" \n");
215 #endif
216 
217     return true;
218 }
219 
220 
parsePolygon(QDataStream & in,QMap<quint32,polygon_property> & polygons)221 bool CGarminTyp::parsePolygon(QDataStream& in, QMap<quint32, polygon_property>& polygons)
222 {
223     bool tainted = false;
224 
225     if(sectPolygons.arraySize == 0)
226     {
227         return true;
228     }
229 
230     if(!sectPolygons.arrayModulo || ((sectPolygons.arraySize % sectPolygons.arrayModulo) != 0))
231     {
232         return true;
233     }
234 
235     QTextCodec* codec = getCodec(codepage);
236 
237     const int N = sectPolygons.arraySize / sectPolygons.arrayModulo;
238     for (int element = 0; element < N; element++)
239     {
240         quint16 t16_1 = 0, t16_2, subtyp;
241         quint8 t8;
242         quint32 typ, offset = 0;
243         bool hasLocalization = false;
244         bool hasTextColor = false;
245         quint8 ctyp;
246         QImage xpmDay(32, 32, QImage::Format_Indexed8);
247         QImage xpmNight(32, 32, QImage::Format_Indexed8);
248         quint8 r, g, b;
249         quint8 langcode;
250 
251         in.device()->seek( sectPolygons.arrayOffset + (sectPolygons.arrayModulo * element ) );
252 
253         if (sectPolygons.arrayModulo == 5)
254         {
255             in >> t16_1 >> t16_2 >> t8;
256             offset = t16_2 | (t8 << 16);
257         }
258         else if (sectPolygons.arrayModulo == 4)
259         {
260             in >> t16_1 >> t16_2;
261             offset = t16_2;
262         }
263         else if (sectPolygons.arrayModulo == 3)
264         {
265             in >> t16_1 >> t8;
266             offset = t8;
267         }
268 
269         t16_2 = (t16_1 >> 5) | (( t16_1 & 0x1f) << 11);
270         typ = t16_2 & 0x7F;
271         subtyp = t16_1 & 0x1F;
272 
273         if(t16_1 & 0x2000)
274         {
275             typ = 0x10000 | (typ << 8) | subtyp;
276         }
277 
278         in.device()->seek(sectPolygons.dataOffset + offset);
279         in >> t8;
280         hasLocalization = t8 & 0x10;
281         hasTextColor = t8 & 0x20;
282         ctyp = t8 & 0x0F;
283 
284 #ifdef DBG
285         qDebug() << "Polygon typ:" << hex << typ << "ctype:" << ctyp << "offset:" << (sectPolygons.dataOffset + offset) << "orig data:" << t16_1;
286 #endif
287 
288         polygon_property& property = polygons[typ];
289 
290         switch(ctyp)
291         {
292         case 0x01:
293         {
294             // day & night single color
295             in >> b >> g >> r;
296             property.brushDay = QBrush(qRgb(r, g, b));
297             in >> b >> g >> r;
298             property.brushNight = QBrush(qRgb(r, g, b));
299 
300             // night and day color for line?
301             in >> b >> g >> r;
302             property.pen = QPen(QBrush(qRgb(r, g, b)), 2);
303             in >> b >> g >> r;
304             property.known = true;
305 
306             break;
307         }
308 
309         case 0x06:
310         {
311             // day & night single color
312             in >> b >> g >> r;
313             property.brushDay = QBrush(qRgb(r, g, b));
314             property.brushNight = QBrush(qRgb(r, g, b));
315             property.pen = Qt::NoPen;
316             property.known = true;
317 
318             break;
319         }
320 
321         case 0x07:
322         {
323             // day single color & night single color
324             in >> b >> g >> r;
325             property.brushDay = QBrush(qRgb(r, g, b));
326             in >> b >> g >> r;
327             property.brushNight = QBrush(qRgb(r, g, b));
328             property.pen = Qt::NoPen;
329             property.known = true;
330 
331             break;
332         }
333 
334         case 0x08:
335         {
336             // day & night two color
337             xpmDay.setColorCount(2);
338 
339             in >> b >> g >> r;
340             xpmDay.setColor(1, qRgb(r, g, b) );
341             in >> b >> g >> r;
342             xpmDay.setColor(0, qRgb(r, g, b) );
343 
344             decodeBitmap(in, xpmDay, 32, 32, 1);
345             property.brushDay.setTextureImage(xpmDay);
346             property.brushNight.setTextureImage(xpmDay);
347             property.pen = Qt::NoPen;
348             property.known = true;
349             break;
350         }
351 
352         case 0x09:
353         {
354             //day two color & night two color
355             xpmDay.setColorCount(2);
356             xpmNight.setColorCount(2);
357             in >> b >> g >> r;
358             xpmDay.setColor(1, qRgb(r, g, b) );
359             in >> b >> g >> r;
360             xpmDay.setColor(0, qRgb(r, g, b) );
361             in >> b >> g >> r;
362             xpmNight.setColor(1, qRgb(r, g, b) );
363             in >> b >> g >> r;
364             xpmNight.setColor(0, qRgb(r, g, b) );
365 
366             decodeBitmap(in, xpmDay, 32, 32, 1);
367             memcpy(xpmNight.bits(), xpmDay.bits(), (32 * 32));
368             property.brushDay.setTextureImage(xpmDay);
369             property.brushNight.setTextureImage(xpmNight);
370             property.pen = Qt::NoPen;
371             property.known = true;
372 
373             break;
374         }
375 
376         case 0x0B:
377         {
378             // day one color, transparent & night two color
379             xpmDay.setColorCount(2);
380             xpmNight.setColorCount(2);
381             in >> b >> g >> r;
382             xpmDay.setColor(1, qRgb(r, g, b) );
383             xpmDay.setColor(0, qRgba(255, 255, 255, 0) );
384 
385             in >> b >> g >> r;
386             xpmNight.setColor(1, qRgb(r, g, b) );
387             in >> b >> g >> r;
388             xpmNight.setColor(0, qRgb(r, g, b) );
389 
390             decodeBitmap(in, xpmDay, 32, 32, 1);
391             memcpy(xpmNight.bits(), xpmDay.bits(), (32 * 32));
392             property.brushDay.setTextureImage(xpmDay);
393             property.brushNight.setTextureImage(xpmNight);
394             property.pen = Qt::NoPen;
395             property.known = true;
396             break;
397         }
398 
399         case 0x0D:
400         {
401             // day two color & night one color, transparent
402 
403             xpmDay.setColorCount(2);
404             xpmNight.setColorCount(2);
405             in >> b >> g >> r;
406             xpmDay.setColor(1, qRgb(r, g, b) );
407             in >> b >> g >> r;
408             xpmDay.setColor(0, qRgb(r, g, b) );
409 
410             in >> b >> g >> r;
411             xpmNight.setColor(1, qRgb(r, g, b) );
412             xpmNight.setColor(0, qRgba(255, 255, 255, 0) );
413 
414             decodeBitmap(in, xpmDay, 32, 32, 1);
415             memcpy(xpmNight.bits(), xpmDay.bits(), (32 * 32));
416             property.brushDay.setTextureImage(xpmDay);
417             property.brushNight.setTextureImage(xpmNight);
418             property.pen = Qt::NoPen;
419             property.known = true;
420 
421             break;
422         }
423 
424         case 0x0E:
425         {
426             // day & night one color, transparent
427             xpmDay.setColorCount(2);
428             in >> b >> g >> r;
429             xpmDay.setColor(1, qRgb(r, g, b) );
430             xpmDay.setColor(0, qRgba(255, 255, 255, 0) );
431 
432             decodeBitmap(in, xpmDay, 32, 32, 1);
433             property.brushDay.setTextureImage(xpmDay);
434             property.brushNight.setTextureImage(xpmDay);
435             property.pen = Qt::NoPen;
436             property.known = true;
437 
438             break;
439         }
440 
441         case 0x0F:
442         {
443             // day one color, transparent & night one color, transparent
444             xpmDay.setColorCount(2);
445             xpmNight.setColorCount(2);
446             in >> b >> g >> r;
447             xpmDay.setColor(1, qRgb(r, g, b) );
448             xpmDay.setColor(0, qRgba(255, 255, 255, 0) );
449 
450             in >> b >> g >> r;
451             xpmNight.setColor(1, qRgb(r, g, b) );
452             xpmNight.setColor(0, qRgba(255, 255, 255, 0) );
453 
454             decodeBitmap(in, xpmDay, 32, 32, 1);
455             memcpy(xpmNight.bits(), xpmDay.bits(), (32 * 32));
456             property.brushDay.setTextureImage(xpmDay);
457             property.brushNight.setTextureImage(xpmNight);
458             property.pen = Qt::NoPen;
459             property.known = true;
460 
461             break;
462         }
463 
464         default:
465             if(!tainted)
466             {
467                 QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Warning..."), tr("This is a typ file with unknown polygon encoding. Please report!"), QMessageBox::Abort, QMessageBox::Abort);
468                 tainted = true;
469             }
470             qDebug() << "Failed polygon:" << typ << subtyp << hex << typ << subtyp << ctyp;
471         }
472 
473         if(hasLocalization)
474         {
475             qint16 len;
476             quint8 n = 1;
477 
478             in >> t8;
479             len = t8;
480 
481             if(!(t8 & 0x01))
482             {
483                 n = 2;
484                 in >> t8;
485                 len |= t8 << 8;
486             }
487 
488             len -= n;
489             while(len > 0)
490             {
491                 QByteArray str;
492                 in >> langcode;
493                 languages << langcode;
494                 len -= 2 * n;
495                 while(len > 0)
496                 {
497                     in >> t8;
498                     len -= 2 * n;
499 
500                     if(t8 == 0)
501                     {
502                         break;
503                     }
504 
505                     str += t8;
506                 }
507                 if(codec != nullptr)
508                 {
509                     property.strings[langcode] = codec->toUnicode(str);
510                 }
511 #ifdef DBG
512                 qDebug() << len << langcode << property.strings[langcode];
513 #endif
514             }
515         }
516 
517         if(hasTextColor)
518         {
519             in >> t8;
520             property.labelType = (label_type_e)(t8 & 0x07);
521 
522             if(t8 & 0x08)
523             {
524                 in >> r >> g >> b;
525                 property.colorLabelDay = qRgb(r, g, b);
526             }
527 
528             if(t8 & 0x10)
529             {
530                 in >> r >> g >> b;
531                 property.colorLabelNight = qRgb(r, g, b);
532             }
533 #ifdef DBG
534             qDebug() << "ext. label: type" << property.labelType << "day" << property.colorLabelDay << "night" << property.colorLabelNight;
535 #endif
536         }
537     }
538 
539     return true;
540 }
541 
542 
parsePolyline(QDataStream & in,QMap<quint32,polyline_property> & polylines)543 bool CGarminTyp::parsePolyline(QDataStream& in, QMap<quint32, polyline_property>& polylines)
544 {
545     bool tainted = false;
546 
547     if(sectPolylines.arraySize == 0)
548     {
549         return true;
550     }
551 
552     if(!sectPolylines.arrayModulo || ((sectPolylines.arraySize % sectPolylines.arrayModulo) != 0))
553     {
554         return true;
555     }
556 
557     QTextCodec* codec = getCodec(codepage);
558 
559     const int N = sectPolylines.arraySize / sectPolylines.arrayModulo;
560     for (int element = 0; element < N; element++)
561     {
562         quint16 t16_1 = 0, t16_2, subtyp;
563         quint8 t8_1, t8_2;
564         quint32 typ, offset = 0;
565         bool hasLocalization = false;
566         bool hasTextColor = false;
567         //bool renderMode = false;
568         quint8 ctyp, rows;
569         quint8 r, g, b;
570         quint8 langcode;
571 
572         in.device()->seek( sectPolylines.arrayOffset + (sectPolylines.arrayModulo * element ) );
573 
574         if (sectPolylines.arrayModulo == 5)
575         {
576             in >> t16_1 >> t16_2 >> t8_1;
577             offset = t16_2 | (t8_1 << 16);
578         }
579         else if (sectPolylines.arrayModulo == 4)
580         {
581             in >> t16_1 >> t16_2;
582             offset = t16_2;
583         }
584         else if (sectPolylines.arrayModulo == 3)
585         {
586             in >> t16_1 >> t8_1;
587             offset = t8_1;
588         }
589 
590         t16_2 = (t16_1 >> 5) | (( t16_1 & 0x1f) << 11);
591         typ = t16_2 & 0x7F;
592         subtyp = t16_1 & 0x1F;
593 
594         if(t16_1 & 0x2000)
595         {
596             typ = 0x10000 | (typ << 8) | subtyp;
597         }
598 
599         in.device()->seek(sectPolylines.dataOffset + offset);
600         in >> t8_1 >> t8_2;
601         ctyp = t8_1 & 0x07;
602         rows = t8_1 >> 3;
603 
604         hasLocalization = t8_2 & 0x01;
605         //renderMode      = t8_2 & 0x02;
606         hasTextColor = t8_2 & 0x04;
607 
608 #ifdef DBG
609         qDebug() << "Polyline typ:" << hex << typ << "ctyp:" << ctyp << "offset:" << (sectPolylines.dataOffset + offset) << "orig data:" << t16_1;
610 #endif
611 
612         polyline_property& property = polylines[typ];
613 #ifdef DBG
614         qDebug() << "rows" << rows << "t8_2" << hex << t8_2;
615 #endif
616 
617         switch(ctyp)
618         {
619         case 0x00:
620         {
621             if(rows)
622             {
623                 QImage xpm(32, rows, QImage::Format_Indexed8 );
624                 in >> b >> g >> r;
625                 xpm.setColor(1, qRgb(r, g, b) );
626                 in >> b >> g >> r;
627                 xpm.setColor(0, qRgb(r, g, b) );
628                 decodeBitmap(in, xpm, 32, rows, 1);
629                 property.imgDay = xpm;
630                 property.imgNight = xpm;
631                 property.hasPixmap = true;
632                 property.known = true;
633             }
634             else
635             {
636                 quint8 w1, w2;
637                 in >> b >> g >> r;
638                 property.penLineDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
639                 property.penLineNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
640                 in >> b >> g >> r;
641                 property.penBorderDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
642                 property.penBorderNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
643                 in >> w1 >> w2;
644                 property.penLineDay.setWidth(w1);
645                 property.penLineNight.setWidth(w1);
646                 property.penBorderDay.setWidth(w2);
647                 property.penBorderNight.setWidth(w2);
648                 property.hasBorder = w2 > w1;
649                 property.hasPixmap = false;
650                 property.known = true;
651             }
652 
653             break;
654         }
655 
656         case 0x01:
657         {
658             if(rows)
659             {
660                 QImage xpm1(32, rows, QImage::Format_Indexed8 );
661                 QImage xpm2(32, rows, QImage::Format_Indexed8 );
662                 in >> b >> g >> r;
663                 xpm1.setColor(1, qRgb(r, g, b) );
664                 in >> b >> g >> r;
665                 xpm1.setColor(0, qRgb(r, g, b) );
666                 in >> b >> g >> r;
667                 xpm2.setColor(1, qRgb(r, g, b) );
668                 in >> b >> g >> r;
669                 xpm2.setColor(0, qRgb(r, g, b) );
670                 decodeBitmap(in, xpm1, 32, rows, 1);
671                 memcpy(xpm2.bits(), xpm1.bits(), (32 * rows));
672                 property.imgDay = xpm1;
673                 property.imgNight = xpm2;
674                 property.hasPixmap = true;
675                 property.known = true;
676             }
677             else
678             {
679                 quint8 w1, w2;
680                 in >> b >> g >> r;
681                 property.penLineDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
682                 in >> b >> g >> r;
683                 property.penBorderDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
684                 in >> b >> g >> r;
685                 property.penLineNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
686                 in >> b >> g >> r;
687                 property.penBorderNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
688                 in >> w1 >> w2;
689                 property.penLineDay.setWidth(w1);
690                 property.penLineNight.setWidth(w1);
691                 property.penBorderDay.setWidth(w2);
692                 property.penBorderNight.setWidth(w2);
693                 property.hasBorder = w2 > w1;
694                 property.hasPixmap = false;
695                 property.known = true;
696             }
697             break;
698         }
699 
700         case 0x03:
701         {
702             if(rows)
703             {
704                 QImage xpm1(32, rows, QImage::Format_Indexed8 );
705                 QImage xpm2(32, rows, QImage::Format_Indexed8 );
706                 in >> b >> g >> r;
707                 xpm1.setColor(1, qRgb(r, g, b) );
708                 xpm1.setColor(0, qRgba(255, 255, 255, 0) );
709                 in >> b >> g >> r;
710                 xpm2.setColor(1, qRgb(r, g, b) );
711                 in >> b >> g >> r;
712                 xpm2.setColor(0, qRgb(r, g, b) );
713                 decodeBitmap(in, xpm1, 32, rows, 1);
714                 memcpy(xpm2.bits(), xpm1.bits(), (32 * rows));
715                 property.imgDay = xpm1;
716                 property.imgNight = xpm2;
717                 property.hasPixmap = true;
718                 property.known = true;
719             }
720             else
721             {
722                 quint8 w1, w2;
723                 in >> b >> g >> r;
724                 property.penLineDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
725                 property.penBorderDay = QPen(Qt::NoPen);
726                 in >> b >> g >> r;
727                 property.penLineNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
728                 in >> b >> g >> r;
729                 property.penBorderNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
730                 in >> w1 >> w2;
731                 property.penLineDay.setWidth(w1);
732                 property.penLineNight.setWidth(w1);
733                 property.penBorderDay.setWidth(w2);
734                 property.penBorderNight.setWidth(w2);
735                 property.hasBorder = w2 > w1;
736                 property.hasPixmap = false;
737                 property.known = true;
738             }
739 
740             break;
741         }
742 
743         case 0x05:
744         {
745             if(rows)
746             {
747                 QImage xpm1(32, rows, QImage::Format_Indexed8 );
748                 QImage xpm2(32, rows, QImage::Format_Indexed8 );
749                 in >> b >> g >> r;
750                 xpm1.setColor(1, qRgb(r, g, b) );
751                 in >> b >> g >> r;
752                 xpm1.setColor(0, qRgb(r, g, b) );
753                 in >> b >> g >> r;
754                 xpm2.setColor(1, qRgb(r, g, b) );
755                 xpm2.setColor(0, qRgba(255, 255, 255, 0) );
756                 decodeBitmap(in, xpm1, 32, rows, 1);
757                 memcpy(xpm2.bits(), xpm1.bits(), (32 * rows));
758                 property.imgDay = xpm1;
759                 property.imgNight = xpm2;
760                 property.hasPixmap = true;
761                 property.known = true;
762             }
763             else
764             {
765                 quint8 w1;
766                 in >> b >> g >> r;
767                 property.penLineDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
768                 in >> b >> g >> r;
769                 property.penBorderDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
770                 in >> b >> g >> r;
771                 property.penLineNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
772                 property.penBorderNight = QPen(Qt::NoPen);
773                 in >> w1;
774                 property.penLineDay.setWidth(w1);
775                 property.penLineNight.setWidth(w1);
776                 property.hasBorder = false;
777                 property.hasPixmap = false;
778                 property.known = true;
779             }
780             break;
781         }
782 
783         case 0x06:
784         {
785             if(rows)
786             {
787                 QImage xpm(32, rows, QImage::Format_Indexed8 );
788                 in >> b >> g >> r;
789                 xpm.setColor(1, qRgb(r, g, b) );
790                 xpm.setColor(0, qRgba(255, 255, 255, 0) );
791                 decodeBitmap(in, xpm, 32, rows, 1);
792                 property.imgDay = xpm;
793                 property.imgNight = xpm;
794                 property.hasPixmap = true;
795                 property.known = true;
796             }
797             else
798             {
799                 quint8 w1;
800                 in >> b >> g >> r;
801                 property.penLineDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
802                 property.penBorderDay = QPen(Qt::NoPen);
803                 property.penLineNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
804                 property.penBorderNight = QPen(Qt::NoPen);
805                 in >> w1;
806                 property.penLineDay.setWidth(w1);
807                 property.penLineNight.setWidth(w1);
808                 property.hasBorder = false;
809                 property.hasPixmap = false;
810                 property.known = true;
811             }
812             break;
813         }
814 
815         case 0x07:
816         {
817             if(rows)
818             {
819                 QImage xpm1(32, rows, QImage::Format_Indexed8 );
820                 QImage xpm2(32, rows, QImage::Format_Indexed8 );
821                 in >> b >> g >> r;
822                 xpm1.setColor(1, qRgb(r, g, b) );
823                 xpm1.setColor(0, qRgba(255, 255, 255, 0) );
824                 in >> b >> g >> r;
825                 xpm2.setColor(1, qRgb(r, g, b) );
826                 xpm2.setColor(0, qRgba(255, 255, 255, 0) );
827                 decodeBitmap(in, xpm1, 32, rows, 1);
828                 memcpy(xpm2.bits(), xpm1.bits(), (32 * rows));
829                 property.imgDay = xpm1;
830                 property.imgNight = xpm2;
831                 property.hasPixmap = true;
832                 property.known = true;
833             }
834             else
835             {
836                 quint8 w1;
837                 in >> b >> g >> r;
838                 property.penLineDay = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
839                 property.penBorderDay = QPen(Qt::NoPen);
840                 in >> b >> g >> r;
841                 property.penLineNight = QPen(QBrush(qRgb(r, g, b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
842                 property.penBorderNight = QPen(Qt::NoPen);
843                 in >> w1;
844                 property.penLineDay.setWidth(w1);
845                 property.penLineNight.setWidth(w1);
846                 property.hasBorder = false;
847                 property.hasPixmap = false;
848                 property.known = true;
849             }
850             break;
851         }
852 
853         default:
854             if(!tainted)
855             {
856                 QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Warning..."), tr("This is a typ file with unknown polyline encoding. Please report!"), QMessageBox::Abort, QMessageBox::Abort);
857                 tainted = true;
858             }
859 
860             qDebug() << "Failed polyline" << hex << ":" << typ << ctyp << rows;
861             continue;
862         }
863 
864         property.imgDay = property.imgDay.convertToFormat(QImage::Format_ARGB32_Premultiplied);
865         property.imgNight = property.imgNight.convertToFormat(QImage::Format_ARGB32_Premultiplied);
866         if(hasLocalization)
867         {
868             qint16 len;
869             quint8 n = 1;
870 
871             in >> t8_1;
872             len = t8_1;
873 
874             if(!(t8_1 & 0x01))
875             {
876                 n = 2;
877                 in >> t8_1;
878                 len |= t8_1 << 8;
879             }
880 
881             len -= n;
882             while(len > 0)
883             {
884                 QByteArray str;
885                 in >> langcode;
886                 languages << langcode;
887                 len -= 2 * n;
888                 while(len > 0)
889                 {
890                     in >> t8_1;
891                     len -= 2 * n;
892 
893                     if(t8_1 == 0)
894                     {
895                         break;
896                     }
897 
898                     str += t8_1;
899                 }
900                 if(codec != nullptr)
901                 {
902                     property.strings[langcode] = codec->toUnicode(str);
903                 }
904 #ifdef DBG
905                 qDebug() << len << langcode << property.strings[langcode];
906 #endif
907             }
908         }
909 
910         if(hasTextColor)
911         {
912             in >> t8_1;
913             property.labelType = (label_type_e)(t8_1 & 0x07);
914 
915             if(t8_1 & 0x08)
916             {
917                 in >> r >> g >> b;
918                 property.colorLabelDay = qRgb(r, g, b);
919             }
920 
921             if(t8_1 & 0x10)
922             {
923                 in >> r >> g >> b;
924                 property.colorLabelNight = qRgb(r, g, b);
925             }
926 #ifdef DBG
927             qDebug() << "ext. label: type" << property.labelType << "day" << property.colorLabelDay << "night" << property.colorLabelNight;
928 #endif
929         }
930 
931         if(property.hasPixmap)
932         {
933             property.imgDay = property.imgDay.mirrored(false, true);
934             property.imgNight = property.imgNight.mirrored(false, true);
935         }
936     }
937     return true;
938 }
939 
940 
decodeBppAndBytes(int ncolors,int w,int flags,int & bpp,int & bytes)941 bool CGarminTyp::decodeBppAndBytes(int ncolors, int w, int flags, int& bpp, int& bytes)
942 {
943     switch(flags)
944     {
945     case 0x00:
946     {
947         if(ncolors < 3)
948         {
949             bpp = ncolors;
950         }
951         else if(ncolors == 3)
952         {
953             bpp = 2;
954         }
955         else if(ncolors < 16)
956         {
957             bpp = 4;
958         }
959         else if(ncolors < 256)
960         {
961             bpp = 8;
962         }
963         else
964         {
965             return false;
966         }
967         break;
968     }
969 
970     case 0x10:
971     {
972         if(ncolors == 0)
973         {
974             bpp = 1;
975         }
976         else if(ncolors < 3)
977         {
978             bpp = 2;
979         }
980         else if(ncolors < 15)
981         {
982             bpp = 4;
983         }
984         else if(ncolors < 256)
985         {
986             bpp = 8;
987         }
988         else
989         {
990             return false;
991         }
992         break;
993     }
994 
995     case 0x20:
996     {
997         if(ncolors == 0)
998         {
999             bpp = 16;
1000         }
1001         else if(ncolors < 3)
1002         {
1003             bpp = ncolors;
1004         }
1005         else if(ncolors < 4)
1006         {
1007             bpp = 2;
1008         }
1009         else if(ncolors < 16)
1010         {
1011             bpp = 4;
1012         }
1013         else if(ncolors < 256)
1014         {
1015             bpp = 8;
1016         }
1017         else
1018         {
1019             return false;
1020         }
1021         break;
1022     }
1023 
1024     default:
1025         return false;
1026     }
1027 
1028     bytes = (w * bpp) / 8;
1029     if( (w * bpp) & 0x07 )
1030     {
1031         ++bytes;
1032     }
1033 
1034     return true;
1035 }
1036 
1037 
decodeColorTable(QDataStream & in,QImage & img,int ncolors,int maxcolor,bool hasAlpha)1038 bool CGarminTyp::decodeColorTable(QDataStream& in, QImage& img, int ncolors, int maxcolor, bool hasAlpha)
1039 {
1040     img.setColorCount(ncolors);
1041 
1042     if(hasAlpha)
1043     {
1044         int i;
1045         quint8 byte;
1046         quint32 bits = 0;
1047         quint32 reg = 0;
1048         quint32 mask = 0x000000FF;
1049 
1050         for (i = 0; i < ncolors; i++)
1051         {
1052             while(bits < 28)
1053             {
1054                 in >> byte;
1055                 mask = 0x000000FF << bits;
1056                 reg = reg & (~mask);
1057                 reg = reg | (byte << bits);
1058                 bits += 8;
1059             }
1060 
1061             img.setColor(i, qRgba((reg >> 16) & 0x0FF, (reg >> 8) & 0x0FF, reg & 0x0FF, ~((reg >> 24) & 0x0F) << 4));
1062 
1063             reg = reg >> 28;
1064             bits -= 28;
1065         }
1066         for(; i < maxcolor; ++i)
1067         {
1068             img.setColor(i, qRgba(0, 0, 0, 0));
1069         }
1070     }
1071     else
1072     {
1073         int i;
1074         quint8 r, g, b;
1075         for(i = 0; i < ncolors; ++i)
1076         {
1077             in >> b >> g >> r;
1078             img.setColor(i, qRgb(r, g, b));
1079         }
1080         for(; i < maxcolor; ++i)
1081         {
1082             img.setColor(i, qRgba(0, 0, 0, 0));
1083         }
1084     }
1085     return true;
1086 }
1087 
1088 
decodeBitmap(QDataStream & in,QImage & img,int w,int h,int bpp)1089 void CGarminTyp::decodeBitmap(QDataStream& in, QImage& img, int w, int h, int bpp)
1090 {
1091     int x = 0, j = 0;
1092     quint8 color;
1093 
1094     if(bpp == 0)
1095     {
1096         return;
1097     }
1098 
1099     for (int y = 0; y < h; y++)
1100     {
1101         while ( x < w )
1102         {
1103             in >> color;
1104 
1105             for ( int i = 0; (i < (8 / bpp)) && (x < w); i++ )
1106             {
1107                 int value;
1108                 if ( i > 0 )
1109                 {
1110                     value = (color >>= bpp);
1111                 }
1112                 else
1113                 {
1114                     value = color;
1115                 }
1116                 if ( bpp == 4)
1117                 {
1118                     value = value & 0xf;
1119                 }
1120                 if ( bpp == 2)
1121                 {
1122                     value = value & 0x3;
1123                 }
1124                 if ( bpp == 1)
1125                 {
1126                     value = value & 0x1;
1127                 }
1128                 img.setPixel(x, y, value);
1129                 //                 qDebug() << QString("value(%4) pixel at (%1,%2) is 0x%3 j is %5").arg(x).arg(y).arg(value,0,16).arg(color).arg(j);
1130                 x += 1;
1131             }
1132             j += 1;
1133         }
1134         x = 0;
1135     }
1136 }
1137 
1138 
parsePoint(QDataStream & in,QMap<quint32,point_property> & points)1139 bool CGarminTyp::parsePoint(QDataStream& in, QMap<quint32, point_property>& points)
1140 {
1141     //    bool tainted = false;
1142 
1143     if(!sectPoints.arrayModulo || ((sectPoints.arraySize % sectPoints.arrayModulo) != 0))
1144     {
1145         return true;
1146     }
1147 
1148     QTextCodec* codec = getCodec(codepage);
1149 
1150     const int N = sectPoints.arraySize / sectPoints.arrayModulo;
1151     for (int element = 0; element < N; element++)
1152     {
1153         quint16 t16_1 = 0, t16_2, subtyp;
1154         quint8 t8_1;
1155         quint32 typ, offset = 0;
1156         bool hasLocalization = false;
1157         bool hasTextColor = false;
1158         quint8 langcode;
1159         quint8 r, g, b;
1160 
1161         in.device()->seek( sectPoints.arrayOffset + (sectPoints.arrayModulo * element ) );
1162 
1163         if (sectPoints.arrayModulo == 5)
1164         {
1165             in >> t16_1 >> t16_2 >> t8_1;
1166             offset = t16_2 | (t8_1 << 16);
1167         }
1168         else if (sectPoints.arrayModulo == 4)
1169         {
1170             in >> t16_1 >> t16_2;
1171             offset = t16_2;
1172         }
1173         else if (sectPoints.arrayModulo == 3)
1174         {
1175             in >> t16_1 >> t8_1;
1176             offset = t8_1;
1177         }
1178 
1179         t16_2 = (t16_1 >> 5) | (( t16_1 & 0x1f) << 11);
1180         typ = t16_2 & 0x7FF;
1181         subtyp = t16_1 & 0x01F;
1182 
1183         if(t16_1 & 0x2000)
1184         {
1185             typ = 0x10000 | (typ << 8) | subtyp;
1186         }
1187         else
1188         {
1189             typ = (typ << 8) + subtyp;
1190         }
1191 
1192         in.device()->seek( sectPoints.dataOffset + offset );
1193 
1194         int bpp = 0, wbytes = 0;
1195         quint8 w, h, ncolors, ctyp;
1196         in >> t8_1 >> w >> h >> ncolors >> ctyp;
1197 
1198         hasLocalization = t8_1 & 0x04;
1199         hasTextColor = t8_1 & 0x08;
1200         t8_1 = t8_1 & 0x03;
1201 #ifdef DBG
1202         qDebug() << "Point typ:" << hex << typ << "ctyp:" << ctyp << "offset:" << (sectPoints.dataOffset + offset) << "orig data:" << t16_1;
1203 #endif
1204 
1205         if(!decodeBppAndBytes(ncolors, w, ctyp, bpp, wbytes))
1206         {
1207             continue;
1208         }
1209 
1210 #ifdef DBG
1211         qDebug() << "          " << dec << "w" << w << "h" << h << "ncolors" << ncolors << "bpp" << bpp << "wbytes" << wbytes;
1212 #endif
1213 
1214         if(ctyp == 0x20 || ctyp == 0x00)
1215         {
1216             if((ncolors == 0) && (bpp >= 16))
1217             {
1218                 ncolors = w * h;
1219             }
1220         }
1221 
1222         point_property& property = points[typ];
1223         QImage imgDay(w, h, QImage::Format_Indexed8 );
1224         QImage imgNight(w, h, QImage::Format_Indexed8 );
1225 
1226         if(!decodeColorTable(in, imgDay, ncolors, 1 << bpp, ctyp == 0x20))
1227         {
1228             continue;
1229         }
1230 
1231         if(bpp >= 16)
1232         {
1233             continue;
1234         }
1235         else
1236         {
1237             decodeBitmap(in, imgDay, w, h, bpp);
1238             property.imgDay = imgDay;
1239         }
1240 
1241         if(t8_1 == 0x03)
1242         {
1243             in >> ncolors >> ctyp;
1244             if(!decodeBppAndBytes(ncolors, w, ctyp, bpp, wbytes))
1245             {
1246                 continue;
1247             }
1248             if(!decodeColorTable(in, imgNight, ncolors, 1 << bpp, ctyp == 0x20))
1249             {
1250                 continue;
1251             }
1252             decodeBitmap(in, imgNight, w, h, bpp);
1253             points[typ].imgNight = imgNight;
1254         }
1255         else if(t8_1 == 0x02)
1256         {
1257             in >> ncolors >> ctyp;
1258             if(!decodeBppAndBytes(ncolors, w, ctyp, bpp, wbytes))
1259             {
1260                 continue;
1261             }
1262             if(!decodeColorTable(in, imgDay, ncolors, 1 << bpp, ctyp == 0x20))
1263             {
1264                 continue;
1265             }
1266             property.imgNight = imgDay;
1267         }
1268         else
1269         {
1270             property.imgNight = imgDay;
1271         }
1272 
1273         if(hasLocalization)
1274         {
1275             qint16 len;
1276             quint8 n = 1;
1277 
1278             in >> t8_1;
1279             len = t8_1;
1280 
1281             if(!(t8_1 & 0x01))
1282             {
1283                 n = 2;
1284                 in >> t8_1;
1285                 len |= t8_1 << 8;
1286             }
1287 
1288             len -= n;
1289             while(len > 0)
1290             {
1291                 QByteArray str;
1292                 in >> langcode;
1293                 languages << langcode;
1294                 len -= 2 * n;
1295                 while(len > 0)
1296                 {
1297                     in >> t8_1;
1298                     len -= 2 * n;
1299 
1300                     if(t8_1 == 0)
1301                     {
1302                         break;
1303                     }
1304 
1305                     str += t8_1;
1306                 }
1307                 if(codec != nullptr)
1308                 {
1309                     property.strings[langcode] = codec->toUnicode(str);
1310                 }
1311 #ifdef DBG
1312                 qDebug() << len << langcode << property.strings[langcode];
1313 #endif
1314             }
1315         }
1316 
1317         if(hasTextColor)
1318         {
1319             in >> t8_1;
1320             property.labelType = (label_type_e)(t8_1 & 0x07);
1321 
1322             if(t8_1 & 0x08)
1323             {
1324                 in >> r >> g >> b;
1325                 property.colorLabelDay = qRgb(r, g, b);
1326             }
1327 
1328             if(t8_1 & 0x10)
1329             {
1330                 in >> r >> g >> b;
1331                 property.colorLabelNight = qRgb(r, g, b);
1332             }
1333 #ifdef DBG
1334             qDebug() << "ext. label: type" << property.labelType << "day" << property.colorLabelDay << "night" << property.colorLabelNight;
1335 #endif
1336         }
1337     }
1338 
1339     return true;
1340 }
1341