1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-07 07:13:21 +02:00
GPXSee/src/map/IMG/imgdata.cpp

239 lines
5.5 KiB
C++
Raw Normal View History

#include <QMap>
2019-05-10 18:56:19 +02:00
#include <QtEndian>
2021-01-27 21:18:06 +01:00
#include <QFile>
2019-05-10 18:56:19 +02:00
#include "vectortile.h"
2021-04-10 15:27:40 +02:00
#include "imgdata.h"
2019-05-10 18:56:19 +02:00
2021-04-10 15:27:40 +02:00
using namespace IMG;
2019-07-04 18:18:03 +02:00
2020-01-19 13:23:20 +01:00
static SubFile::Type tileType(const char str[3])
{
if (!memcmp(str, "TRE", 3))
return SubFile::TRE;
else if (!memcmp(str, "RGN", 3))
return SubFile::RGN;
else if (!memcmp(str, "LBL", 3))
return SubFile::LBL;
else if (!memcmp(str, "TYP", 3))
return SubFile::TYP;
else if (!memcmp(str, "GMP", 3))
return SubFile::GMP;
else if (!memcmp(str, "NET", 3))
return SubFile::NET;
else
return SubFile::Unknown;
}
2021-08-10 20:44:16 +02:00
bool IMGData::readSubFileBlocks(QFile &file, quint64 offset, SubFile *subFile)
2019-05-10 18:56:19 +02:00
{
2021-08-10 20:44:16 +02:00
quint16 block;
2020-01-19 13:23:20 +01:00
2021-08-10 20:44:16 +02:00
if (!file.seek(offset + 0x20))
return false;
for (int i = 0; i < 240; i++) {
if (!readValue(file, block))
return false;
if (block == 0xFFFF)
break;
subFile->addBlock(block);
2019-05-10 18:56:19 +02:00
}
2021-08-10 20:44:16 +02:00
return true;
}
bool IMGData::readIMGHeader(QFile &file)
{
2019-05-10 18:56:19 +02:00
char signature[7], identifier[7];
2021-01-27 21:18:06 +01:00
file.read((char*)&_key, 1) && file.seek(0x10)
&& read(file, signature, sizeof(signature)) && file.seek(0x41)
&& read(file, identifier, sizeof(identifier));
2019-05-10 18:56:19 +02:00
if (memcmp(signature, "DSKIMG", sizeof(signature))
|| memcmp(identifier, "GARMIN", sizeof(identifier))) {
_errorString = "Not a Garmin IMG file";
2021-08-10 20:44:16 +02:00
return false;
2019-05-10 18:56:19 +02:00
}
2021-08-10 20:44:16 +02:00
2019-05-10 18:56:19 +02:00
char d1[20], d2[31];
quint8 e1, e2;
2021-08-10 20:44:16 +02:00
if (!(file.seek(0x49) && read(file, d1, sizeof(d1)) && file.seek(0x61)
2021-01-27 21:18:06 +01:00
&& readValue(file, e1) && readValue(file, e2) && file.seek(0x65)
2021-08-10 20:44:16 +02:00
&& read(file, d2, sizeof(d2)))) {
_errorString = "Error reading IMG header";
return false;
}
2019-12-20 23:39:20 +01:00
2019-05-10 18:56:19 +02:00
QByteArray nba(QByteArray(d1, sizeof(d1)) + QByteArray(d2, sizeof(d2)));
2019-12-20 23:39:20 +01:00
_name = QString::fromLatin1(nba.constData(), nba.size()-1).trimmed();
_blockBits = e1 + e2;
2019-05-10 18:56:19 +02:00
2021-08-10 20:44:16 +02:00
return true;
}
bool IMGData::readFAT(QFile &file, TileMap &tileMap)
{
QByteArray typFile;
2019-05-10 18:56:19 +02:00
quint8 flag;
quint64 offset = 0x200;
2021-08-10 20:44:16 +02:00
2019-05-10 18:56:19 +02:00
// Skip unused FAT blocks if any
while (true) {
2021-08-10 20:44:16 +02:00
if (!(file.seek(offset) && readValue(file, flag)))
return false;
2019-05-10 18:56:19 +02:00
if (flag)
break;
offset += 512;
}
// Read first FAT block with FAT table size
char name[8], type[3];
quint32 size;
quint16 part;
2021-08-10 20:44:16 +02:00
if (!(file.seek(offset + 12) && readValue(file, size)))
return false;
2019-05-10 18:56:19 +02:00
offset += 512;
int cnt = (size - offset) / 512;
// Read FAT blocks describing the IMG sub-files
for (int i = 0; i < cnt; i++) {
2021-08-10 20:44:16 +02:00
if (!(file.seek(offset) && readValue(file, flag)
2021-01-27 21:18:06 +01:00
&& read(file, name, sizeof(name))
&& read(file, type, sizeof(type)) && readValue(file, size)
2021-08-10 20:44:16 +02:00
&& readValue(file, part)))
return false;
2020-01-19 13:23:20 +01:00
SubFile::Type tt = tileType(type);
2019-05-10 18:56:19 +02:00
2020-02-10 08:55:34 +01:00
QByteArray fn(name, sizeof(name));
2020-01-19 13:23:20 +01:00
if (VectorTile::isTileFile(tt)) {
2019-05-10 18:56:19 +02:00
VectorTile *tile;
TileMap::const_iterator it = tileMap.find(fn);
if (it == tileMap.constEnd()) {
2019-05-10 18:56:19 +02:00
tile = new VectorTile();
tileMap.insert(fn, tile);
} else
tile = *it;
2021-01-27 21:18:06 +01:00
SubFile *subFile = part ? tile->file(tt)
: tile->addFile(this, tt);
2021-08-10 20:44:16 +02:00
if (!(subFile && readSubFileBlocks(file, offset, subFile)))
return false;
2019-05-10 18:56:19 +02:00
} else if (tt == SubFile::TYP) {
2019-07-04 18:18:03 +02:00
SubFile *typ = 0;
if (typFile.isNull()) {
_typ = new SubFile(this);
2019-07-04 18:18:03 +02:00
typ = _typ;
typFile = fn;
} else if (fn == typFile)
typ = _typ;
2021-08-10 20:44:16 +02:00
if (typ && !readSubFileBlocks(file, offset, typ))
return false;
2019-05-10 18:56:19 +02:00
}
offset += 512;
}
2021-08-10 20:44:16 +02:00
return true;
}
bool IMGData::createTileTree(const TileMap &tileMap)
{
int minMapZoom = 24;
2021-08-10 20:44:16 +02:00
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value();
if (!tile->init()) {
2021-08-10 20:44:16 +02:00
qWarning("%s: %s: Invalid map tile", qPrintable(_fileName),
qPrintable(it.key()));
delete tile;
continue;
}
2019-05-10 18:56:19 +02:00
double min[2], max[2];
min[0] = tile->bounds().left();
min[1] = tile->bounds().bottom();
max[0] = tile->bounds().right();
max[1] = tile->bounds().top();
_tileTree.Insert(min, max, tile);
2019-05-10 18:56:19 +02:00
_bounds |= tile->bounds();
if (tile->zooms().min() < _zooms.min())
_zooms.setMin(tile->zooms().min());
if (tile->zooms().min() < minMapZoom)
minMapZoom = tile->zooms().min();
}
// Detect and mark basemap
TileTree::Iterator it;
for (_tileTree.GetFirst(it); !_tileTree.IsNull(it); _tileTree.GetNext(it)) {
VectorTile *tile = _tileTree.GetAt(it);
if (tile->zooms().min() > minMapZoom)
_baseMap = true;
if (tile->zooms().min() == minMapZoom)
tile->markAsBasemap();
2019-05-10 18:56:19 +02:00
}
// Allow some extra zoom out on maps without basemaps, but not too much as
// this would kill the rendering performance
if (!_baseMap)
_zooms.setMin(_zooms.min() - 2);
2019-05-10 18:56:19 +02:00
2021-08-10 20:44:16 +02:00
return (_tileTree.Count() > 0);
}
IMGData::IMGData(const QString &fileName) : _fileName(fileName)
{
QFile file(fileName);
TileMap tileMap;
if (!file.open(QFile::ReadOnly)) {
_errorString = file.errorString();
return;
}
if (!readIMGHeader(file))
return;
if (!readFAT(file, tileMap)) {
_errorString = "Error reading FAT data";
qDeleteAll(tileMap);
return;
}
if (!createTileTree(tileMap)) {
_errorString = "No usable map tile found";
2021-08-10 20:44:16 +02:00
return;
}
_valid = true;
2019-07-04 18:18:03 +02:00
}
2021-04-10 15:27:40 +02:00
qint64 IMGData::read(QFile &file, char *data, qint64 maxSize) const
2019-05-10 18:56:19 +02:00
{
2021-01-27 21:18:06 +01:00
qint64 ret = file.read(data, maxSize);
2019-05-10 18:56:19 +02:00
if (_key)
for (int i = 0; i < ret; i++)
data[i] ^= _key;
return ret;
}
2021-04-10 15:27:40 +02:00
template<class T> bool IMGData::readValue(QFile &file, T &val) const
2019-05-10 18:56:19 +02:00
{
T data;
2021-01-27 21:18:06 +01:00
if (read(file, (char*)&data, sizeof(T)) < (qint64)sizeof(T))
2019-05-10 18:56:19 +02:00
return false;
2019-08-13 20:50:43 +02:00
val = qFromLittleEndian(data);
2019-05-10 18:56:19 +02:00
return true;
}
2021-04-10 15:27:40 +02:00
bool IMGData::readBlock(QFile &file, int blockNum, char *data) const
2019-05-10 18:56:19 +02:00
{
2021-01-27 21:18:06 +01:00
if (!file.seek((quint64)blockNum << _blockBits))
return false;
2021-01-27 21:18:06 +01:00
if (read(file, data, 1ULL<<_blockBits) < (qint64)(1ULL<<_blockBits))
return false;
return true;
2019-05-10 18:56:19 +02:00
}