1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-07 15:23:22 +02:00
GPXSee/src/map/IMG/img.cpp

320 lines
7.2 KiB
C++
Raw Normal View History

#include <QMap>
2019-05-10 18:56:19 +02:00
#include <QtEndian>
#include "common/programpaths.h"
2019-05-10 18:56:19 +02:00
#include "vectortile.h"
#include "img.h"
#define CACHED_SUBDIVS_COUNT 2048 // ~32MB
2019-07-04 18:18:03 +02:00
typedef QMap<QString, VectorTile*> TileMap;
struct PolyCTX
2019-05-10 18:56:19 +02:00
{
PolyCTX(const RectC &rect, int bits, QList<IMG::Poly> *polygons,
QList<IMG::Poly> *lines, QCache<const SubDiv*, IMG::Polys> *polyCache)
2019-05-10 18:56:19 +02:00
: rect(rect), bits(bits), polygons(polygons), lines(lines),
polyCache(polyCache) {}
2019-05-10 18:56:19 +02:00
const RectC &rect;
int bits;
QList<IMG::Poly> *polygons;
QList<IMG::Poly> *lines;
QCache<const SubDiv*, IMG::Polys> *polyCache;
};
struct PointCTX
{
PointCTX(const RectC &rect, int bits, QList<IMG::Point> *points,
QCache<const SubDiv*, QList<IMG::Point> > *pointCache)
: rect(rect), bits(bits), points(points), pointCache(pointCache) {}
const RectC &rect;
int bits;
2019-05-10 18:56:19 +02:00
QList<IMG::Point> *points;
QCache<const SubDiv*, QList<IMG::Point> > *pointCache;
2019-05-10 18:56:19 +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;
}
2019-07-04 18:18:03 +02:00
IMG::IMG(const QString &fileName)
: _file(fileName), _typ(0), _style(0), _valid(false)
2019-05-10 18:56:19 +02:00
{
2020-01-19 13:23:20 +01:00
#define CHECK(condition) \
if (!(condition)) { \
_errorString = "Unsupported or invalid IMG file"; \
qDeleteAll(tileMap); \
return; \
}
TileMap tileMap;
2020-01-19 13:23:20 +01:00
QString typFile;
2019-05-10 18:56:19 +02:00
if (!_file.open(QFile::ReadOnly)) {
_errorString = _file.errorString();
return;
}
// Read IMG header
char signature[7], identifier[7];
_file.read((char*)&_key, 1) && _file.seek(0x10)
&& read(signature, sizeof(signature)) && _file.seek(0x41)
&& read(identifier, sizeof(identifier));
if (memcmp(signature, "DSKIMG", sizeof(signature))
|| memcmp(identifier, "GARMIN", sizeof(identifier))) {
_errorString = "Not a Garmin IMG file";
return;
}
char d1[20], d2[31];
quint8 e1, e2;
CHECK(_file.seek(0x49) && read(d1, sizeof(d1)) && _file.seek(0x61)
&& readValue(e1) && readValue(e2) && _file.seek(0x65)
&& read(d2, sizeof(d2)));
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();
2019-05-10 18:56:19 +02:00
_blockSize = 1 << (e1 + e2);
_polyCache.setMaxCost(CACHED_SUBDIVS_COUNT);
_pointCache.setMaxCost(CACHED_SUBDIVS_COUNT);
2019-05-10 18:56:19 +02:00
// Read the FAT table
quint8 flag;
quint64 offset = 0x200;
// Skip unused FAT blocks if any
while (true) {
CHECK(_file.seek(offset) && readValue(flag));
if (flag)
break;
offset += 512;
}
// Read first FAT block with FAT table size
char name[8], type[3];
quint32 size;
quint16 part;
CHECK(_file.seek(offset + 12) && readValue(size));
offset += 512;
int cnt = (size - offset) / 512;
// Read FAT blocks describing the IMG sub-files
for (int i = 0; i < cnt; i++) {
quint16 block;
CHECK(_file.seek(offset) && readValue(flag) && read(name, sizeof(name))
&& read(type, sizeof(type)) && readValue(size) && readValue(part));
2020-01-19 13:23:20 +01:00
SubFile::Type tt = tileType(type);
2019-05-10 18:56:19 +02:00
QString fn(QByteArray(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;
SubFile *file = part ? tile->file(tt)
: tile->addFile(this, tt);
2019-05-10 18:56:19 +02:00
CHECK(file);
_file.seek(offset + 0x20);
for (int i = 0; i < 240; i++) {
CHECK(readValue(block));
if (block == 0xFFFF)
break;
file->addBlock(block);
}
} 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;
if (typ) {
_file.seek(offset + 0x20);
for (int i = 0; i < 240; i++) {
CHECK(readValue(block));
if (block == 0xFFFF)
break;
typ->addBlock(block);
}
2019-05-10 18:56:19 +02:00
}
}
offset += 512;
}
// Create tile tree
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value();
if (!tile->init()) {
qWarning("%s: %s: Invalid map tile", qPrintable(_file.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();
2019-05-10 18:56:19 +02:00
}
if (!_tileTree.Count())
_errorString = "No usable map tile found";
else
_valid = true;
2019-07-04 18:18:03 +02:00
}
IMG::~IMG()
{
TileTree::Iterator it;
for (_tileTree.GetFirst(it); !_tileTree.IsNull(it); _tileTree.GetNext(it))
delete _tileTree.GetAt(it);
delete _typ;
delete _style;
}
void IMG::load()
{
Q_ASSERT(!_style);
if (_typ)
2019-07-04 18:18:03 +02:00
_style = new Style(_typ);
else {
QFile typFile(ProgramPaths::typFile());
2020-01-19 13:23:20 +01:00
if (typFile.open(QIODevice::ReadOnly)) {
SubFile typ(&typFile);
2019-07-04 18:18:03 +02:00
_style = new Style(&typ);
} else
_style = new Style();
2019-05-10 18:56:19 +02:00
}
}
2019-07-04 18:18:03 +02:00
void IMG::clear()
2019-05-10 18:56:19 +02:00
{
TileTree::Iterator it;
for (_tileTree.GetFirst(it); !_tileTree.IsNull(it); _tileTree.GetNext(it))
2019-07-04 18:18:03 +02:00
_tileTree.GetAt(it)->clear();
delete _style;
_style = 0;
_polyCache.clear();
_pointCache.clear();
}
static bool polyCb(VectorTile *tile, void *context)
{
PolyCTX *ctx = (PolyCTX*)context;
tile->polys(ctx->rect, ctx->bits, ctx->polygons, ctx->lines, ctx->polyCache);
return true;
2019-05-10 18:56:19 +02:00
}
static bool pointCb(VectorTile *tile, void *context)
2019-05-10 18:56:19 +02:00
{
PointCTX *ctx = (PointCTX*)context;
tile->points(ctx->rect, ctx->bits, ctx->points, ctx->pointCache);
2019-05-10 18:56:19 +02:00
return true;
}
void IMG::polys(const RectC &rect, int bits, QList<Poly> *polygons,
QList<Poly> *lines)
2019-05-10 18:56:19 +02:00
{
PolyCTX ctx(rect, bits, polygons, lines, &_polyCache);
2019-05-10 18:56:19 +02:00
double min[2], max[2];
min[0] = rect.left();
min[1] = rect.bottom();
max[0] = rect.right();
max[1] = rect.top();
_tileTree.Search(min, max, polyCb, &ctx);
}
void IMG::points(const RectC &rect, int bits, QList<Point> *points)
{
PointCTX ctx(rect, bits, points, &_pointCache);
double min[2], max[2];
min[0] = rect.left();
min[1] = rect.bottom();
max[0] = rect.right();
max[1] = rect.top();
_tileTree.Search(min, max, pointCb, &ctx);
2019-05-10 18:56:19 +02:00
}
qint64 IMG::read(char *data, qint64 maxSize)
{
qint64 ret = _file.read(data, maxSize);
if (_key)
for (int i = 0; i < ret; i++)
data[i] ^= _key;
return ret;
}
template<class T> bool IMG::readValue(T &val)
{
T data;
if (read((char*)&data, sizeof(T)) < (qint64)sizeof(T))
return false;
2019-08-13 20:50:43 +02:00
val = qFromLittleEndian(data);
2019-05-10 18:56:19 +02:00
return true;
}
bool IMG::readBlock(int blockNum, QByteArray &data)
2019-05-10 18:56:19 +02:00
{
if (!_file.seek((qint64)blockNum * (qint64)_blockSize))
return false;
data.resize(_blockSize);
if (read(data.data(), _blockSize) < _blockSize)
return false;
return true;
2019-05-10 18:56:19 +02:00
}
2019-06-07 09:37:10 +02:00
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const IMG::Point &point)
{
dbg.nospace() << "Point(" << hex << point.type << ", " << point.label
<< ", " << point.poi << ")";
return dbg.space();
}
QDebug operator<<(QDebug dbg, const IMG::Poly &poly)
{
dbg.nospace() << "Poly(" << hex << poly.type << ", " << poly.label << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG