Fixed capitalization algorithm
[GPXSee.git] / src / map / IMG / lblfile.cpp
blob1a45fb65257df0955dd9fdeaf0727d592822cddf
1 #include "huffmantext.h"
2 #include "rgnfile.h"
3 #include "lblfile.h"
5 using namespace Garmin;
6 using namespace IMG;
8 enum Charset {Normal, Symbol, Special};
10 static quint8 NORMAL_CHARS[] = {
11 ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
12 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
13 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
14 'X', 'Y', 'Z', '~', '~', '~', ' ', ' ',
15 '0', '1', '2', '3', '4', '5', '6', '7',
16 '8', '9', '~', '~', '~', '~', '~', '~'
19 static quint8 SYMBOL_CHARS[] = {
20 '@', '!', '"', '#', '$', '%', '&', '\'',
21 '(', ')', '*', '+', ',', '-', '.', '/',
22 '~', '~', '~', '~', '~', '~', '~', '~',
23 '~', '~', ':', ';', '<', '=', '>', '?',
24 '~', '~', '~', '~', '~', '~', '~', '~',
25 '~', '~', '~', '[', '\\', ']', '^', '_'
28 static quint8 SPECIAL_CHARS[] = {
29 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
30 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
31 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
32 'x', 'y', 'z', '~', '~', '~', '~', '~',
33 '0', '1', '2', '3', '4', '5', '6', '7',
34 '8', '9', '~', '~', '~', '~', '~', '~'
37 static bool isAllUpperCase(const QString &str)
39 if (str.isEmpty())
40 return false;
42 for (int i = 0; i < str.size(); i++) {
43 QChar c(str.at(i));
44 if (c.isLetter() && !(c.isUpper() || c == QChar(0x00DF)))
45 return false;
48 return true;
51 static QString capitalized(const QString &str)
53 QString ret;
55 ret.resize(str.size());
57 if (!str.isEmpty())
58 ret[0] = str.at(0);
60 for (int i = 1; i < str.size(); i++) {
61 QChar last(str.at(i-1));
62 QChar current(str.at(i));
64 if (!(last.isSpace() || last == '('))
65 ret[i] = current.toLower();
66 else
67 ret[i] = current;
70 return ret;
73 static QByteArray ft2m(const QByteArray &str)
75 bool ok;
76 int number = str.toInt(&ok);
77 return ok ? QByteArray::number(qRound(number * 0.3048)) : str;
80 LBLFile::~LBLFile()
82 delete _huffmanText;
83 delete[] _table;
84 delete[] _rasters;
87 bool LBLFile::load(Handle &hdl, const RGNFile *rgn, Handle &rgnHdl)
89 quint16 hdrLen, codepage;
91 if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)
92 && seek(hdl, _gmpOffset + 0x15) && readUInt32(hdl, _base.offset)
93 && readUInt32(hdl, _base.size) && readByte(hdl, &_shift)
94 && readByte(hdl, &_encoding) && seek(hdl, _gmpOffset + 0x57)
95 && readUInt32(hdl, _poi.offset) && readUInt32(hdl, _poi.size)
96 && readByte(hdl, &_poiShift) && seek(hdl, _gmpOffset + 0xAA)
97 && readUInt16(hdl, codepage)))
98 return false;
100 if (hdrLen >= 0x132) {
101 quint32 offset, size;
102 quint16 recordSize;
103 if (!(seek(hdl, _gmpOffset + 0x124) && readUInt32(hdl, offset)
104 && readUInt32(hdl, size) && readUInt16(hdl, recordSize)))
105 return false;
107 if (size && recordSize) {
108 _table = new quint32[size / recordSize];
109 if (!seek(hdl, offset))
110 return false;
111 for (quint32 i = 0; i < size / recordSize; i++) {
112 if (!readVUInt32(hdl, recordSize, _table[i]))
113 return false;
118 if (hdrLen >= 0x19A) {
119 quint32 offset, recordSize, size, flags;
120 if (!(seek(hdl, _gmpOffset + 0x184) && readUInt32(hdl, offset)
121 && readUInt32(hdl, size) && readUInt16(hdl, recordSize)
122 && readUInt32(hdl, flags) && readUInt32(hdl, _img.offset)
123 && readUInt32(hdl, _img.size)))
124 return false;
126 if (size && recordSize)
127 if (!loadRasterTable(hdl, offset, size, recordSize))
128 return false;
131 if (_encoding == 11) {
132 _huffmanText = new HuffmanText();
133 if (!_huffmanText->load(rgn, rgnHdl))
134 return false;
137 _codec = TextCodec(codepage);
139 return true;
142 void LBLFile::clear()
144 delete _huffmanText;
145 delete[] _table;
146 delete[] _rasters;
147 _huffmanText = 0;
148 _table = 0;
149 _rasters = 0;
152 Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize,
153 bool convert)
155 Shield::Type shieldType = Shield::None;
156 QByteArray label, shieldLabel;
157 QByteArray *bap = &label;
158 int split = -1;
160 for (int i = 0; i < str.size(); i++) {
161 const quint8 &c = str.at(i);
163 if (c == 0 || c == 0x1d || c == 0x07)
164 break;
166 if (c == 0x1c)
167 capitalize = false;
168 else if ((c >= 0x1e && c <= 0x1f)) {
169 if (bap == &shieldLabel)
170 bap = &label;
171 else {
172 if (!bap->isEmpty())
173 bap->append('\n');
174 if (c == 0x1f && split < 0)
175 split = bap->size();
177 } else if (c < 0x07) {
178 shieldType = static_cast<Shield::Type>(c);
179 bap = &shieldLabel;
180 } else if (bap == &shieldLabel && c == 0x20) {
181 bap = &label;
182 } else
183 bap->append(c);
186 if (split >= 0)
187 label = label.left(split) + ft2m(label.mid(split));
188 else if (convert)
189 label = ft2m(label);
190 QString text(_codec.toString(label));
191 return Label(capitalize && isAllUpperCase(text) ? capitalized(text) : text,
192 Shield(shieldType, _codec.toString(shieldLabel)));
195 Label LBLFile::label6b(const SubFile *file, Handle &fileHdl, quint32 size,
196 bool capitalize, bool convert) const
198 Shield::Type shieldType = Shield::None;
199 QByteArray label, shieldLabel;
200 QByteArray *bap = &label;
201 Charset curCharSet = Normal;
202 quint8 b1, b2, b3;
203 int split = -1;
205 for (quint32 i = 0; i < size; i = i + 3) {
206 if (!(file->readByte(fileHdl, &b1) && file->readByte(fileHdl, &b2)
207 && file->readByte(fileHdl, &b3)))
208 return Label();
210 int c[]= {b1>>2, (b1&0x3)<<4|b2>>4, (b2&0xF)<<2|b3>>6, b3&0x3F};
212 for (int cpt = 0; cpt < 4; cpt++) {
213 if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) {
214 if (split >= 0)
215 label = label.left(split) + ft2m(label.mid(split));
216 else if (convert)
217 label = ft2m(label);
218 QString text(QString::fromLatin1(label));
219 return Label(capitalize && isAllUpperCase(text)
220 ? capitalized(text) : text, Shield(shieldType, shieldLabel));
222 switch (curCharSet) {
223 case Normal:
224 if (c[cpt] == 0x1c)
225 curCharSet = Symbol;
226 else if (c[cpt] == 0x1b)
227 curCharSet = Special;
228 else if (c[cpt] >= 0x1e && c[cpt] <= 0x1f) {
229 if (bap == &shieldLabel)
230 bap = &label;
231 else {
232 if (!bap->isEmpty())
233 bap->append('\n');
234 if (c[cpt] == 0x1f && split < 0)
235 split = bap->size();
237 } else if (c[cpt] >= 0x2a && c[cpt] <= 0x2f) {
238 shieldType = static_cast<Shield::Type>(c[cpt] - 0x29);
239 bap = &shieldLabel;
240 } else if (bap == &shieldLabel
241 && NORMAL_CHARS[c[cpt]] == ' ')
242 bap = &label;
243 else
244 bap->append(NORMAL_CHARS[c[cpt]]);
245 break;
246 case Symbol:
247 bap->append(SYMBOL_CHARS[c[cpt]]);
248 curCharSet = Normal;
249 break;
250 case Special:
251 bap->append(SPECIAL_CHARS[c[cpt]]);
252 curCharSet = Normal;
253 break;
258 return Label();
261 Label LBLFile::label8b(const SubFile *file, Handle &fileHdl, quint32 size,
262 bool capitalize, bool convert)
264 QVector<quint8> str;
265 quint8 c;
267 for (quint32 i = 0; i < size; i++) {
268 if (!file->readByte(fileHdl, &c))
269 break;
270 str.append(c);
271 if (!c)
272 return str2label(str, capitalize, convert);
275 return Label();
278 Label LBLFile::labelHuffman(Handle &hdl, const SubFile *file, Handle &fileHdl,
279 quint32 size, bool capitalize, bool convert)
281 QVector<quint8> str;
283 if (!_huffmanText->decode(file, fileHdl, size, str))
284 return Label();
285 if (!_table)
286 return str2label(str, capitalize, convert);
289 QVector<quint8> str2;
290 for (int i = 0; i < str.size(); i++) {
291 quint32 val = _table[str.at(i)];
292 if (val) {
293 quint32 off = _base.offset + ((val & 0x7fffff) << _shift);
294 if (!seek(hdl, off))
295 return Label();
297 if (str2.size() && str2.back() == '\0')
298 str2[str2.size() - 1] = ' ';
299 else if (str2.size())
300 str2.append(' ');
302 if (!_huffmanText->decode(this, hdl, _base.offset + _base.size - off,
303 str2))
304 return Label();
305 } else {
306 if (str.at(i) == 7) {
307 str2.append(0);
308 break;
310 if (str2.size() && str2.back() == '\0')
311 str2[str2.size() - 1] = ' ';
312 str2.append(str.at(i));
316 return str2label(str2, capitalize, convert);
319 Label LBLFile::label(Handle &hdl, quint32 offset, bool poi, bool capitalize,
320 bool convert)
322 quint32 labelOffset;
323 if (poi) {
324 quint32 poiOffset;
325 if (!(_poi.size >= (offset << _poiShift)
326 && seek(hdl, _poi.offset + (offset << _poiShift))
327 && readUInt24(hdl, poiOffset) && (poiOffset & 0x3FFFFF)))
328 return Label();
329 labelOffset = _base.offset + ((poiOffset & 0x3FFFFF) << _shift);
330 } else
331 labelOffset = _base.offset + (offset << _shift);
333 if (labelOffset > _base.offset + _base.size)
334 return Label();
335 if (!seek(hdl, labelOffset))
336 return Label();
338 return label(hdl, this, hdl, _base.offset + _base.size - labelOffset,
339 capitalize, convert);
342 Label LBLFile::label(Handle &hdl, const SubFile *file, Handle &fileHdl,
343 quint32 size, bool capitalize, bool convert)
345 switch (_encoding) {
346 case 6:
347 return label6b(file, fileHdl, size, capitalize, convert);
348 case 9:
349 case 10:
350 return label8b(file, fileHdl, size, capitalize, convert);
351 case 11:
352 return labelHuffman(hdl, file, fileHdl, size, capitalize, convert);
353 default:
354 return Label();
358 bool LBLFile::loadRasterTable(Handle &hdl, quint32 offset, quint32 size,
359 quint32 recordSize)
361 quint32 prev, cur;
363 _imgCount = size / recordSize;
364 _imgIdSize = byteSize(_imgCount - 1);
365 _rasters = new Image[_imgCount];
367 if (!(seek(hdl, offset) && readVUInt32(hdl, recordSize, prev)))
368 return false;
370 for (quint32 i = 1; i < _imgCount; i++) {
371 if (!readVUInt32(hdl, recordSize, cur))
372 return false;
374 _rasters[i-1].offset = prev;
375 _rasters[i-1].size = cur - prev;
377 prev = cur;
380 _rasters[_imgCount-1].offset = prev;
381 _rasters[_imgCount-1].size = _img.size - prev;
383 return true;
386 QPixmap LBLFile::image(Handle &hdl, quint32 id) const
388 QPixmap pm;
390 if (id >= _imgCount)
391 return pm;
393 if (!seek(hdl, _img.offset + _rasters[id].offset))
394 return pm;
395 QByteArray ba;
396 ba.resize(_rasters[id].size);
397 if (!read(hdl, ba.data(), _rasters[id].size))
398 return pm;
400 pm.loadFromData(ba, "jpeg");
402 return pm;