1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-24 11:45:53 +01:00

Initial (and partial) IMG links support

+ various IMG fixes (RGN parsing, IMG parsing)
This commit is contained in:
Martin Tůma 2020-06-27 22:46:26 +02:00
parent e7729e8745
commit 32d3eab10e
22 changed files with 940 additions and 152 deletions

View File

@ -95,6 +95,7 @@ HEADERS += src/common/config.h \
src/map/IMG/gmap.h \ src/map/IMG/gmap.h \
src/map/IMG/huffmanstream.h \ src/map/IMG/huffmanstream.h \
src/map/IMG/huffmantable.h \ src/map/IMG/huffmantable.h \
src/map/IMG/nodfile.h \
src/map/IMG/mapdata.h \ src/map/IMG/mapdata.h \
src/map/IMG/rastertile.h \ src/map/IMG/rastertile.h \
src/map/IMG/textpathitem.h \ src/map/IMG/textpathitem.h \
@ -257,6 +258,7 @@ SOURCES += src/main.cpp \
src/map/IMG/gmap.cpp \ src/map/IMG/gmap.cpp \
src/map/IMG/huffmanstream.cpp \ src/map/IMG/huffmanstream.cpp \
src/map/IMG/huffmantable.cpp \ src/map/IMG/huffmantable.cpp \
src/map/IMG/nodfile.cpp \
src/map/IMG/mapdata.cpp \ src/map/IMG/mapdata.cpp \
src/map/IMG/rastertile.cpp \ src/map/IMG/rastertile.cpp \
src/map/IMG/textpathitem.cpp \ src/map/IMG/textpathitem.cpp \

View File

@ -41,8 +41,19 @@ bool BitStream1::flush()
return true; return true;
} }
bool BitStream4::flush()
{
if (_length && !_file.seek(_hdl, _hdl.pos() + _length))
return false;
bool BitStream4::read(int bits, quint32 &val) _length = 0;
_used = 32;
_unused = 0;
return true;
}
bool BitStream4F::read(int bits, quint32 &val)
{ {
if (bits <= 32 - (int)(_used + _unused)) { if (bits <= 32 - (int)(_used + _unused)) {
val = bits ? (_data << _used) >> (32 - bits) : 0; val = bits ? (_data << _used) >> (32 - bits) : 0;
@ -50,6 +61,8 @@ bool BitStream4::read(int bits, quint32 &val)
return true; return true;
} }
if (_unused)
return false;
quint32 old = (_used < 32) ? (_data << _used) >> (32 - bits) : 0; quint32 old = (_used < 32) ? (_data << _used) >> (32 - bits) : 0;
quint32 bytes = qMin(_length, 4U); quint32 bytes = qMin(_length, 4U);
@ -66,14 +79,123 @@ bool BitStream4::read(int bits, quint32 &val)
return true; return true;
} }
bool BitStream4::flush() BitStream4R::BitStream4R(const SubFile &file, SubFile::Handle &hdl,
quint32 length) : BitStream4(file, hdl, length)
{ {
if (_length && !_file.seek(_hdl, _hdl.pos() + _length)) _file.seek(_hdl, _hdl.pos() - 4);
}
bool BitStream4R::readBytes(int bytes, quint32 &val)
{
quint32 bits = _used % 8;
quint32 b;
if (bits) {
if (!read(8 - bits, b))
return false;
Q_ASSERT(!b);
}
return read(bytes * 8, val);
}
bool BitStream4R::readVUInt32(quint32 &val)
{
quint32 b;
quint8 bytes, shift;
if (!readBytes(1, b))
return false; return false;
_length = 0; if ((b & 1) == 0) {
_used = 32; if ((b & 2) == 0) {
_unused = 0; bytes = ((b >> 2) & 1) ^ 3;
shift = 5;
} else {
shift = 6;
bytes = 1;
}
} else {
shift = 7;
bytes = 0;
}
val = b >> (8 - shift);
if (bytes) {
if (!readBytes(bytes, b))
return false;
val = val | (b << shift);
}
return true; return true;
} }
bool BitStream4R::readVuint32SM(quint32 &val1, quint32 &val2, quint32 &val2Bits)
{
quint32 b, eb;
if (!readBytes(1, b))
return false;
if (!(b & 1)) {
val1 = b >> 3 & 0x1f;
val2 = b >> 1 & 3;
val2Bits = 2;
} else {
eb = b & 2;
val2 = b >> 2 & 0x3f;
val2Bits = eb * 2 + 6;
if (!readBytes((eb >> 1 | 2) - 1, b))
return false;
if (eb) {
val2 = val2 | (b & 0xf) << 6;
b = b >> 4 & 0xfff;
}
val1 = b;
}
return true;
}
bool BitStream4R::skip(quint32 bytes)
{
if (bytes * 8 > bitsAvailable())
return false;
quint32 ab = (32 - (_used + _unused))/8;
if (bytes <= ab)
_used += bytes * 8;
else {
quint32 seek = ((bytes - ab)/4)*4;
quint32 read = (bytes - ab)%4;
if (seek && !_file.seek(_hdl, _hdl.pos() - seek))
return false;
_length -= seek;
if (read) {
quint32 rb = qMin(_length, 4U);
if (!_file.readUInt32(_hdl, _data))
return false;
if (!_file.seek(_hdl, _hdl.pos() - 8))
return false;
_length -= rb;
_unused = (4 - rb) * 8;
_used = read * 8;
} else
_used = 32;
}
return true;
}
void BitStream4R::resize(quint32 length)
{
quint32 ab = (32 - _used)/8;
if (ab < length)
_length = length - ab;
else {
_length = 0;
_used += length * 8;
}
}

View File

@ -10,7 +10,7 @@ public:
bool read(int bits, quint32 &val); bool read(int bits, quint32 &val);
bool flush(); bool flush();
quint32 bitsAvailable() const {return _length * 8 + _remaining;} quint64 bitsAvailable() const {return (quint64)_length * 8 + _remaining;}
private: private:
const SubFile &_file; const SubFile &_file;
@ -25,15 +25,64 @@ public:
: _file(file), _hdl(hdl), _length(length), _used(32), _unused(0), : _file(file), _hdl(hdl), _length(length), _used(32), _unused(0),
_data(0) {} _data(0) {}
bool read(int bits, quint32 &val);
bool flush(); bool flush();
quint32 bitsAvailable() const {return _length * 8 + (32 - _used) - _unused;} quint64 bitsAvailable() const
{return (quint64)_length * 8 + (32 - _used) - _unused;}
private: protected:
const SubFile &_file; const SubFile &_file;
SubFile::Handle &_hdl; SubFile::Handle &_hdl;
quint32 _length, _used, _unused; quint32 _length, _used, _unused;
quint32 _data; quint32 _data;
}; };
class BitStream4F : public BitStream4 {
public:
BitStream4F(const SubFile &file, SubFile::Handle &hdl, quint32 length)
: BitStream4(file, hdl, length) {}
bool read(int bits, quint32 &val);
};
class BitStream4R : public BitStream4 {
public:
BitStream4R(const SubFile &file, SubFile::Handle &hdl, quint32 length);
template<typename T> bool read(int bits, T &val);
bool readBytes(int bytes, quint32 &val);
bool readVUInt32(quint32 &val);
bool readVuint32SM(quint32 &val1, quint32 &val2, quint32 &val2Bits);
bool skip(quint32 bytes);
void resize(quint32 length);
};
template<typename T>
bool BitStream4R::read(int bits, T &val)
{
if (bits <= 32 - (int)(_used + _unused)) {
val = bits ? (_data << _used) >> (32 - bits) : 0;
_used += bits;
return true;
}
if (_unused)
return false;
quint32 old = (_used < 32) ? (_data << _used) >> (32 - bits) : 0;
quint32 bytes = qMin(_length, 4U);
if (!_file.readUInt32(_hdl, _data))
return false;
if (!_file.seek(_hdl, _hdl.pos() - 8))
return false;
_length -= bytes;
_used -= 32 - bits;
_unused = (4 - bytes) * 8;
val = _data >> (32 - _used) | old;
return true;
}
#endif // BITSTREAM_H #endif // BITSTREAM_H

View File

@ -1,74 +1,30 @@
#include "huffmanstream.h" #include "huffmanstream.h"
bool HuffmanStreamF::init(bool line)
HuffmanStream::HuffmanStream(const SubFile &file, SubFile::Handle &hdl,
quint32 length, const HuffmanTable &table, bool line)
: BitStream4(file, hdl, length), _table(table), _symbolDataSize(0),
_symbolData(0)
{ {
if (line) { if (line) {
if (!(sign(_lonSign) && sign(_latSign))) if (!(sign(_lonSign) && sign(_latSign)))
return; return false;
} else { } else {
_lonSign = 0; _lonSign = 0;
_latSign = 0; _latSign = 0;
} }
quint32 eb; quint32 eb;
if (!read(1, eb)) if (!_bs.read(1, eb))
return; return false;
if (eb) { if (eb) {
qWarning("Extended polygon/lines not supported"); qWarning() << "Extended lines/polygons not supported";
flush();
}
}
bool HuffmanStream::sign(int &val)
{
quint32 bit;
val = 0;
if (!read(1, bit))
return false; return false;
if (bit) {
if (!read(1, bit))
return false;
val = bit ? -1 : 1;
} }
return true; return true;
} }
bool HuffmanStream::readDelta(int sign, qint32 &symbol) bool HuffmanStreamR::init()
{ {
quint8 size; if (!(sign(_lonSign) && sign(_latSign)))
quint32 next;
quint8 nextSize = qMin((quint32)(32 - _symbolDataSize), bitsAvailable());
if (!read(nextSize, next))
return false; return false;
_symbolData = (_symbolData << nextSize) | next;
_symbolDataSize += nextSize;
symbol = _table.symbol(_symbolData << (32 - _symbolDataSize), size);
if (size <= _symbolDataSize)
_symbolDataSize -= size;
else
return false;
if (symbol && !sign) {
if (!_symbolDataSize)
return false;
else {
sign = ((1U << (_symbolDataSize - 1)) & _symbolData) ? -1 : 1;
_symbolDataSize--;
}
}
symbol = sign * symbol;
return true; return true;
} }

View File

@ -4,10 +4,12 @@
#include "bitstream.h" #include "bitstream.h"
#include "huffmantable.h" #include "huffmantable.h"
class HuffmanStream : public BitStream4 { template <class BitStream>
class HuffmanStream {
public: public:
HuffmanStream(const SubFile &file, SubFile::Handle &hdl, quint32 length, HuffmanStream(BitStream &bitstream, const HuffmanTable &table)
const HuffmanTable &table, bool line); : _bs(bitstream), _table(table), _symbolDataSize(0), _symbolData(0),
_lonSign(0), _latSign(0) {}
bool readNext(qint32 &lonDelta, qint32 &latDelta) bool readNext(qint32 &lonDelta, qint32 &latDelta)
{ {
@ -17,19 +19,87 @@ public:
return (lonDelta || latDelta); return (lonDelta || latDelta);
} }
bool readOffset(qint32 &lonDelta, qint32 &latDelta)
{return (readDelta(1, lonDelta) && readDelta(1, latDelta));}
bool atEnd() const bool atEnd() const
{return _symbolDataSize + bitsAvailable() < _table.maxSymbolSize();} {return _symbolDataSize + _bs.bitsAvailable() < _table.maxSymbolSize();}
bool flush() {return _bs.flush();}
private: protected:
bool sign(int &val); bool sign(int &val);
bool readDelta(int sign, qint32 &delta); bool readDelta(int sign, qint32 &delta);
BitStream &_bs;
const HuffmanTable &_table; const HuffmanTable &_table;
quint32 _symbolDataSize; quint32 _symbolDataSize;
quint32 _symbolData; quint32 _symbolData;
int _lonSign, _latSign; int _lonSign, _latSign;
}; };
template <class BitStream>
bool HuffmanStream<BitStream>::sign(int &val)
{
quint32 bit;
val = 0;
if (!_bs.read(1, bit))
return false;
if (bit) {
if (!_bs.read(1, bit))
return false;
val = bit ? -1 : 1;
}
return true;
}
template <class BitStream>
bool HuffmanStream<BitStream>::readDelta(int sign, qint32 &symbol)
{
quint8 size;
quint32 next;
quint8 nextSize = qMin((quint64)(32 - _symbolDataSize), _bs.bitsAvailable());
if (!_bs.read(nextSize, next))
return false;
_symbolData = (_symbolData << nextSize) | next;
_symbolDataSize += nextSize;
symbol = _table.symbol(_symbolData << (32 - _symbolDataSize), size);
if (size <= _symbolDataSize)
_symbolDataSize -= size;
else
return false;
if (symbol && !sign) {
if (!_symbolDataSize)
return false;
else {
sign = ((1U << (_symbolDataSize - 1)) & _symbolData) ? -1 : 1;
_symbolDataSize--;
}
}
symbol = sign * symbol;
return true;
}
class HuffmanStreamF : public HuffmanStream<BitStream4F> {
public:
HuffmanStreamF(BitStream4F &bitstream, const HuffmanTable &table)
: HuffmanStream(bitstream, table) {}
bool init(bool line);
bool readOffset(qint32 &lonDelta, qint32 &latDelta)
{return (readDelta(1, lonDelta) && readDelta(1, latDelta));}
};
class HuffmanStreamR : public HuffmanStream<BitStream4R> {
public:
HuffmanStreamR(BitStream4R &bitstream, const HuffmanTable &table)
: HuffmanStream(bitstream, table) {}
bool init();
};
#endif // HUFFMANSTREAM_H #endif // HUFFMANSTREAM_H

View File

@ -42,6 +42,8 @@ bool HuffmanTable::load(const SubFile &file, SubFile::Handle &hdl,
_s10 = _s14 + _s1c * _s1d; _s10 = _s14 + _s1c * _s1d;
_s18 = _s10 + (_s1 << _s0); _s18 = _s10 + (_s1 << _s0);
_id = id;
return true; return true;
} }

View File

@ -13,6 +13,8 @@ public:
quint8 maxSymbolSize() const {return _s2;} quint8 maxSymbolSize() const {return _s2;}
quint32 symbol(quint32 data, quint8 &size) const; quint32 symbol(quint32 data, quint8 &size) const;
quint8 id() const {return _id;}
private: private:
bool getBuffer(const SubFile &file, SubFile::Handle &hdl, quint32 offset, bool getBuffer(const SubFile &file, SubFile::Handle &hdl, quint32 offset,
quint32 size, quint8 id); quint32 size, quint8 id);
@ -22,6 +24,8 @@ private:
quint8 *_s10, *_s14, *_s18; quint8 *_s10, *_s14, *_s18;
quint8 _s1c, _s1d, _s1e, _s1f, _s20; quint8 _s1c, _s1d, _s1e, _s1f, _s20;
quint16 _s22; quint16 _s22;
quint8 _id;
}; };
#endif // HUFFMANTABLE_H #endif // HUFFMANTABLE_H

View File

@ -59,7 +59,7 @@ IMG::IMG(const QString &fileName) : _file(fileName)
QByteArray nba(QByteArray(d1, sizeof(d1)) + QByteArray(d2, sizeof(d2))); QByteArray nba(QByteArray(d1, sizeof(d1)) + QByteArray(d2, sizeof(d2)));
_name = QString::fromLatin1(nba.constData(), nba.size()-1).trimmed(); _name = QString::fromLatin1(nba.constData(), nba.size()-1).trimmed();
_blockSize = 1 << (e1 + e2); _blockBits = e1 + e2;
// Read the FAT table // Read the FAT table
quint8 flag; quint8 flag;
@ -132,7 +132,6 @@ IMG::IMG(const QString &fileName) : _file(fileName)
} }
// Create tile tree // Create tile tree
int minMapZoom = 24; int minMapZoom = 24;
for (TileMap::const_iterator it = tileMap.constBegin(); for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) { it != tileMap.constEnd(); ++it) {
@ -159,14 +158,19 @@ IMG::IMG(const QString &fileName) : _file(fileName)
minMapZoom = tile->zooms().min(); minMapZoom = tile->zooms().min();
} }
for (TileMap::const_iterator it = tileMap.constBegin(); // Detect and mark basemap
it != tileMap.constEnd(); ++it) { TileTree::Iterator it;
VectorTile *tile = it.value(); for (_tileTree.GetFirst(it); !_tileTree.IsNull(it); _tileTree.GetNext(it)) {
VectorTile *tile = _tileTree.GetAt(it);
if (tile->zooms().min() > minMapZoom) if (tile->zooms().min() > minMapZoom)
_baseMap = true; _baseMap = true;
if (tile->zooms().min() == minMapZoom) if (tile->zooms().min() == minMapZoom)
tile->markAsBasemap(); tile->markAsBasemap();
} }
// 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);
if (!_tileTree.Count()) if (!_tileTree.Count())
_errorString = "No usable map tile found"; _errorString = "No usable map tile found";
@ -197,9 +201,9 @@ template<class T> bool IMG::readValue(T &val)
bool IMG::readBlock(int blockNum, char *data) bool IMG::readBlock(int blockNum, char *data)
{ {
if (!_file.seek((qint64)blockNum * (qint64)_blockSize)) if (!_file.seek((quint64)blockNum << _blockBits))
return false; return false;
if (read(data, _blockSize) < _blockSize) if (read(data, 1U<<_blockBits) < 1U<<_blockBits)
return false; return false;
return true; return true;

View File

@ -14,14 +14,14 @@ public:
private: private:
friend class SubFile; friend class SubFile;
int blockSize() const {return _blockSize;} unsigned blockBits() const {return _blockBits;}
bool readBlock(int blockNum, char *data); bool readBlock(int blockNum, char *data);
qint64 read(char *data, qint64 maxSize); qint64 read(char *data, qint64 maxSize);
template<class T> bool readValue(T &val); template<class T> bool readValue(T &val);
QFile _file; QFile _file;
quint8 _key; quint8 _key;
int _blockSize; unsigned _blockBits;
}; };
#endif // IMG_H #endif // IMG_H

View File

@ -54,7 +54,7 @@ inline bool pointCb(VectorTile *tile, void *context)
} }
MapData::MapData() : _typ(0), _style(0), _zooms(15, 28), _baseMap(false), MapData::MapData() : _typ(0), _style(0), _zooms(24, 28), _baseMap(false),
_valid(false) _valid(false)
{ {
_polyCache.setMaxCost(CACHED_SUBDIVS_COUNT); _polyCache.setMaxCost(CACHED_SUBDIVS_COUNT);

View File

@ -1,5 +1,89 @@
#include "bitstream.h"
#include "huffmanstream.h"
#include "subdiv.h"
#include "nodfile.h"
#include "netfile.h" #include "netfile.h"
bool adjCnts(BitStream4R &bs, QVector<quint16> &cnts, quint16 &mask)
{
quint32 val, cnt, bits;
if (!bs.read(4, val))
return false;
cnt = ((val >> 2) & 3) + 2;
bits = ((val * 2) & 6) + 4;
mask = 1<<(3 + ((val * 2) & 6));
if (cnt == 5) {
if (!bs.read(8, cnt))
return false;
Q_ASSERT(cnt > 4);
}
if (cnt < 2)
return false;
cnts.resize(cnt - 1);
for (int i = 0; i < cnts.size(); i++)
if (!bs.read(bits, cnts[i]))
return false;
return true;
}
bool skipNodes(BitStream4R &bs, const QVector<quint16> &cnts, quint16 mask)
{
for (int i = 0; i < cnts.size(); i++) {
if (cnts.at(i) & mask) {
quint32 v1, v2, v2b;
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
if (!bs.skip(v1))
return false;
}
}
return true;
}
bool seekToLevel(BitStream4R &bs, quint8 level)
{
quint32 v1, v2, v2b;
for (quint8 i = 1; i < level; ) {
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
if (!bs.skip(v1))
return false;
Q_ASSERT(!(v2 & 2));
if (v2 & 2)
return false;
if (v2 & 1)
i++;
};
return true;
}
bool seekToLine(BitStream4R &bs, quint8 line)
{
quint32 v1, v2, v2b;
for (quint8 i = 0; i < line; i++) {
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
if (!bs.skip(v1))
return false;
Q_ASSERT(!(v2 & 2));
if (v2 & 2)
return false;
}
return true;
}
bool NETFile::init(Handle &hdl) bool NETFile::init(Handle &hdl)
{ {
quint8 multiplier; quint8 multiplier;
@ -10,11 +94,121 @@ bool NETFile::init(Handle &hdl)
&& readUInt32(hdl, _size) && readUInt8(hdl, multiplier))) && readUInt32(hdl, _size) && readUInt8(hdl, multiplier)))
return false; return false;
if (hdrLen >= 0x47) {
quint32 info;
if (!(seek(hdl, _gmpOffset + 0x37) && readUInt32(hdl, info)))
return false;
_tableId = ((info >> 2) & 0xF);
if (!(seek(hdl, _gmpOffset + 0x43) && readUInt32(hdl, _linksOffset)
&& readUInt32(hdl, _linksSize) && readUInt8(hdl, _linksShift)))
return false;
}
_multiplier = 1<<multiplier; _multiplier = 1<<multiplier;
return true; return true;
} }
bool NETFile::link(const SubDiv *subdiv, Handle &hdl, NODFile *nod,
Handle &nodHdl, const NODFile::BlockInfo blockInfo, quint8 linkId,
quint8 lineId, const HuffmanTable &table, QList<IMG::Poly> *lines)
{
if (!_multiplier && !init(hdl))
return false;
// TODO
if (!subdiv->level())
return false;
NODFile::LinkInfo linkInfo;
if (!nod->linkInfo(nodHdl, blockInfo, linkId, linkInfo))
return false;
quint32 linkOffset = _linksOffset + (linkInfo.linkOffset << _linksShift);
Q_ASSERT(linkOffset <= _linksOffset + _linksSize);
quint8 s68 = (linkInfo.flags >> 0x12) & 1;
quint8 s69 = (linkInfo.flags >> 0x11) & 1;
quint8 s6a = (linkInfo.flags >> 0x13) & 1;
if (s6a == 1) {
QVector<quint16> ca;
quint16 mask;
if (!seek(hdl, linkOffset))
return false;
BitStream4R bs(*this, hdl, linkOffset - _linksOffset);
quint32 size;
if (!bs.readVUInt32(size))
return false;
if (s69 == 0) {
if (!adjCnts(bs, ca, mask))
return false;
}
if (s68 == 1) {
quint32 v1, v2, v2b;
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
Q_ASSERT(v1);
if (!bs.skip(v1))
return false;
}
if (!skipNodes(bs, ca, mask))
return false;
if (!seekToLevel(bs, subdiv->level()))
return false;
if (!seekToLine(bs, lineId))
return false;
quint32 v1, v2, v2b;
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
bs.resize(v1);
quint32 lon, lat;
if (!(bs.read(0x12 - v2b, lon) && bs.read(16, lat)))
return false;
if (2 < v2b)
lon |= (v2 >> 2) << (0x12U - v2b);
QPoint pos = QPoint(LS(subdiv->lon(), 8) + LS((qint16)lon,
32-subdiv->bits()), LS(subdiv->lat(), 8) + LS((qint16)lat,
32-subdiv->bits()));
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
IMG::Poly poly;
if (!nod->linkType(nodHdl, blockInfo, linkId, poly.type))
return false;
poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat()));
Q_ASSERT(_tableId == table.id());
HuffmanStreamR stream(bs, table);
if (!stream.init())
return false;
qint32 lonDelta, latDelta;
while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += LS(lonDelta, 32-subdiv->bits());
if (pos.rx() < 0 && subdiv->lon() >= 0)
pos.rx() = 0x7fffffff;
pos.ry() += LS(latDelta, 32-subdiv->bits());
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat()));
poly.boundingRect = poly.boundingRect.united(c);
}
lines->append(poly);
}
return true;
}
bool NETFile::lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset) bool NETFile::lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset)
{ {
if (!_multiplier && !init(hdl)) if (!_multiplier && !init(hdl))

View File

@ -1,25 +1,37 @@
#ifndef NETFILE_H #ifndef NETFILE_H
#define NETFILE_H #define NETFILE_H
#include "img.h"
#include "subfile.h" #include "subfile.h"
#include "nodfile.h"
class NODFile;
class LBLFile;
class SubDiv;
class HuffmanTable;
class NETFile : public SubFile class NETFile : public SubFile
{ {
public: public:
NETFile(IMG *img) NETFile(IMG *img) : SubFile(img), _offset(0), _size(0), _linksOffset(0),
: SubFile(img), _offset(0), _size(0), _multiplier(0) {} _linksSize(0), _multiplier(0), _linksShift(0) {}
NETFile(const QString &path) NETFile(const QString &path) : SubFile(path), _offset(0), _size(0),
: SubFile(path), _offset(0), _size(0), _multiplier(0) {} _linksOffset(0), _linksSize(0), _multiplier(0), _linksShift(0) {}
NETFile(SubFile *gmp, quint32 offset) NETFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset),
: SubFile(gmp, offset), _offset(0), _size(0), _multiplier(0) {} _offset(0), _size(0), _linksOffset(0), _linksSize(0), _multiplier(0),
_linksShift(0) {}
bool lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset); bool lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset);
bool link(const SubDiv *subdiv, Handle &hdl, NODFile *nod, Handle &nodHdl,
const NODFile::BlockInfo blockInfo, quint8 linkId, quint8 lineId,
const HuffmanTable &table, QList<IMG::Poly> *lines);
private: private:
bool init(Handle &hdl); bool init(Handle &hdl);
quint32 _offset, _size; quint32 _offset, _size, _linksOffset, _linksSize;
quint8 _multiplier; quint8 _multiplier, _linksShift;
quint8 _tableId;
}; };
#endif // NETFILE_H #endif // NETFILE_H

155
src/map/IMG/nodfile.cpp Normal file
View File

@ -0,0 +1,155 @@
#include "bitstream.h"
#include "nodfile.h"
bool NODFile::init(Handle &hdl)
{
quint16 hdrLen;
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)))
return false;
if (hdrLen >= 0x7b) {
if (!(seek(hdl, _gmpOffset + 0x21) && readUInt8(hdl, _blockShift)
&& readUInt8(hdl, _nodeShift)))
return false;
if (!(seek(hdl, _gmpOffset + 0x67) && readUInt32(hdl, _blockOffset)
&& readUInt32(hdl, _blockSize) && readUInt16(hdl, _blockRecordSize)
&& readUInt32(hdl, _indexOffset) && readUInt32(hdl, _indexSize)
&& readUInt16(hdl, _indexRecordSize) && readUInt32(hdl, _indexFlags)))
return false;
}
return true;
}
quint32 NODFile::indexIdSize(Handle &hdl)
{
if (!_indexRecordSize && !init(hdl))
return 0;
quint32 indexCount = _indexSize / _indexRecordSize;
if (indexCount <= 0x100)
return 1;
else if (indexCount <= 0x1000)
return 2;
else if (indexCount <= 0x1000000)
return 3;
else
return 0;
}
bool NODFile::blockInfo(Handle &hdl, quint32 blockIndexId,
BlockInfo &blockInfo) const
{
quint32 blockOffset;
quint32 offset = _indexRecordSize * blockIndexId + _indexOffset;
quint32 offsetSize = (_indexFlags & 3) + 1;
Q_ASSERT(offset <= _indexOffset + _indexSize);
if (!(seek(hdl, offset) && readVUInt32(hdl, offsetSize, blockOffset)))
return false;
blockInfo.offset = (blockOffset << _blockShift) + _blockOffset;
if (!(seek(hdl, blockInfo.offset) && readUInt16(hdl, blockInfo.h0)
&& readUInt32(hdl, blockInfo.h2) && readUInt32(hdl, blockInfo.h6)
&& readUInt32(hdl, blockInfo.ha) && readUInt16(hdl, blockInfo.he)
&& readUInt8(hdl, blockInfo.h10) && readUInt8(hdl, blockInfo.h11)
&& readUInt8(hdl, blockInfo.h12)))
return false;
return true;
}
bool NODFile::linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
LinkInfo &linkInfo) const
{
Q_ASSERT(linkId < blockInfo.h10);
quint32 infoOffset = ((blockInfo.he * linkId) >> 3) + 0x13
+ ((blockInfo.h0 >> 0xb) & 1) + blockInfo.offset;
quint32 s1 = ((blockInfo.h0 >> 2) & 0x1f) + 8;
quint32 s2 = (blockInfo.h0 >> 7) & 0xf;
quint32 skip = (blockInfo.he * linkId) & 7;
Q_ASSERT(infoOffset <= _blockOffset + _blockSize);
if (!seek(hdl, infoOffset))
return false;
quint32 unused, flags;
BitStream1 bs(*this, hdl, _blockOffset + _blockSize - infoOffset);
if (!(bs.read(skip, unused) && bs.read(0xc, flags)))
return false;
linkInfo.flags = ((flags << 8) & 0xf0000) | (flags & 0xff);
if (!(flags << 8 & 0x10000)) {
if (!bs.read(s1, linkInfo.linkOffset))
return false;
} else {
if (!bs.read(s1 - s2, linkInfo.linkOffset))
return false;
linkInfo.linkOffset += blockInfo.ha;
}
/*
if (!bs.read(s2, linkInfo.nodeOffset))
return false;
linkInfo.nodeOffset = (_blockOffset << _blockShift) - linkInfo.nodeOffset
>> _nodeShift;
*/
return true;
}
bool NODFile::linkType(Handle &hdl, const BlockInfo &blockInfo, quint8 linkId,
quint32 &type) const
{
quint32 offset = ((blockInfo.h10 * blockInfo.he + 7) >> 3) + 0x13 +
blockInfo.offset + ((blockInfo.h0 >> 0xb) & 1) + (quint32)blockInfo.h11
* 3;
quint32 low = 0;
quint32 high = blockInfo.h12 - 1;
quint32 pos;
quint16 val;
if (high > 1) {
do {
pos = (low + high) / 2;
if (!seek(hdl, offset + _blockRecordSize * pos))
return false;
if (!readUInt16(hdl, val))
return false;
quint32 tmp = pos;
if ((val >> 8) <= linkId) {
low = pos;
tmp = high;
}
high = tmp;
} while (low + 1 < high);
}
if (!seek(hdl, offset + _blockRecordSize * low))
return false;
if (!readUInt16(hdl, val))
return false;
type = val & 0x3f;
if ((low < high) && (pos != high)) {
if (!seek(hdl, offset + _blockRecordSize * high))
return false;
if (!readUInt16(hdl, val))
return false;
if ((val >> 8) <= linkId) {
type = (val & 0x3f);
}
}
type *= 256;
return true;
}

55
src/map/IMG/nodfile.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef NODFILE_H
#define NODFILE_H
#include "img.h"
#include "subfile.h"
class NODFile : public SubFile
{
public:
struct BlockInfo {
quint32 offset;
quint16 h0;
quint32 h2;
quint32 h6;
quint32 ha;
quint16 he;
quint8 h10; // links count
quint8 h11;
quint8 h12;
};
struct LinkInfo {
quint32 linkOffset;
//quint32 nodeOffset;
quint32 flags;
};
NODFile(IMG *img) : SubFile(img), _indexOffset(0), _indexSize(0),
_indexFlags(0), _blockOffset(0), _blockSize(0), _indexRecordSize(0),
_blockRecordSize(0), _blockShift(0), _nodeShift(0) {}
NODFile(const QString &path) : SubFile(path), _indexOffset(0), _indexSize(0),
_indexFlags(0), _blockOffset(0), _blockSize(0), _indexRecordSize(0),
_blockRecordSize(0), _blockShift(0), _nodeShift(0) {}
NODFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset),
_indexOffset(0), _indexSize(0),_indexFlags(0), _blockOffset(0),
_blockSize(0), _indexRecordSize(0), _blockRecordSize(0), _blockShift(0),
_nodeShift(0) {}
quint32 indexIdSize(Handle &hdl);
bool blockInfo(Handle &hdl, quint32 blockIndexId,
BlockInfo &blockInfo) const;
bool linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
LinkInfo &linkInfo) const;
bool linkType(Handle &hdl, const BlockInfo &blockInfo, quint8 linkId,
quint32 &type) const;
private:
bool init(Handle &hdl);
quint32 _indexOffset, _indexSize, _indexFlags, _blockOffset, _blockSize;
quint16 _indexRecordSize, _blockRecordSize;
quint8 _blockShift, _nodeShift;
};
#endif // NETFILE_H

View File

@ -5,9 +5,12 @@
#include "huffmanstream.h" #include "huffmanstream.h"
#include "lblfile.h" #include "lblfile.h"
#include "netfile.h" #include "netfile.h"
#include "nodfile.h"
#include "rgnfile.h" #include "rgnfile.h"
#define MASK(bits) ((2U << ((bits) - 1U)) - 1U)
static quint64 pointId(const QPoint &pos, quint32 type, quint32 labelPtr) static quint64 pointId(const QPoint &pos, quint32 type, quint32 labelPtr)
{ {
quint64 id; quint64 id;
@ -52,8 +55,7 @@ bool RGNFile::skipClassFields(Handle &hdl) const
return seek(hdl, hdl.pos() + rs); return seek(hdl, hdl.pos() + rs);
} }
bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3], bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3]) const
SegmentType type) const
{ {
quint32 bitfield = 0xFFFFFFFF; quint32 bitfield = 0xFFFFFFFF;
@ -61,29 +63,40 @@ bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3],
if (!readVBitfield32(hdl, bitfield)) if (!readVBitfield32(hdl, bitfield))
return false; return false;
for (int i = 0; i < 29; i++) { for (int i = 0, j = 0; i < 29; i++) {
if ((flags[0] >> i) & 1) { if ((flags[0] >> i) & 1) {
if (bitfield & 1) { if (bitfield & 1) {
quint32 m = flags[(i >> 4) + 1] >> ((i * 2) & 0x1e) & 3; quint32 m = flags[(j >> 4) + 1] >> ((j * 2) & 0x1e) & 3;
switch (i) {
case 5: quint32 skip = 0;
if (m == 1 && type == Point) { if (m == 3) {
quint16 u16; if (!readVUInt32(hdl, skip))
if (!readUInt16(hdl, u16)) return false;
return false; } else
} skip = m + 1;
break; if (!seek(hdl, hdl.pos() + skip))
default: return false;
break;
}
} }
bitfield >>= 1; bitfield >>= 1;
j++;
} }
} }
return true; return true;
} }
bool RGNFile::skipGblFields(Handle &hdl, quint32 flags) const
{
int cnt = 0;
do {
cnt = cnt + (flags & 3);
flags = flags >> 2;
} while (flags != 0);
return seek(hdl, hdl.pos() + cnt);
}
void RGNFile::clearFlags() void RGNFile::clearFlags()
{ {
memset(_polygonsFlags, 0, sizeof(_polygonsFlags)); memset(_polygonsFlags, 0, sizeof(_polygonsFlags));
@ -102,14 +115,17 @@ bool RGNFile::init(Handle &hdl)
if (hdrLen >= 0x68) { if (hdrLen >= 0x68) {
if (!(readUInt32(hdl, _polygonsOffset) && readUInt32(hdl, _polygonsSize) if (!(readUInt32(hdl, _polygonsOffset) && readUInt32(hdl, _polygonsSize)
&& seek(hdl, _gmpOffset + 0x2D) && readUInt32(hdl, _polygonsFlags[0]) && seek(hdl, _gmpOffset + 0x29) && readUInt32(hdl, _polygonGblFlags)
&& readUInt32(hdl, _polygonsFlags[1]) && readUInt32(hdl, _polygonsFlags[2]) && readUInt32(hdl, _polygonsFlags[0]) && readUInt32(hdl, _polygonsFlags[1])
&& readUInt32(hdl, _polygonsFlags[2])
&& readUInt32(hdl, _linesOffset) && readUInt32(hdl, _linesSize) && readUInt32(hdl, _linesOffset) && readUInt32(hdl, _linesSize)
&& seek(hdl, _gmpOffset + 0x49) && readUInt32(hdl, _linesFlags[0]) && seek(hdl, _gmpOffset + 0x45) && readUInt32(hdl, _linesGblFlags)
&& readUInt32(hdl, _linesFlags[1]) && readUInt32(hdl, _linesFlags[2]) && readUInt32(hdl, _linesFlags[0]) && readUInt32(hdl, _linesFlags[1])
&& readUInt32(hdl, _linesFlags[2])
&& readUInt32(hdl, _pointsOffset) && readUInt32(hdl, _pointsSize) && readUInt32(hdl, _pointsOffset) && readUInt32(hdl, _pointsSize)
&& seek(hdl, _gmpOffset + 0x65) && readUInt32(hdl, _pointsFlags[0]) && seek(hdl, _gmpOffset + 0x61) && readUInt32(hdl, _pointsGblFlags)
&& readUInt32(hdl, _pointsFlags[1]) && readUInt32(hdl, _pointsFlags[2]))) && readUInt32(hdl, _pointsFlags[0]) && readUInt32(hdl, _pointsFlags[1])
&& readUInt32(hdl, _pointsFlags[2])))
return false; return false;
} }
@ -230,6 +246,7 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
&& readInt16(hdl, lon) && readInt16(hdl, lat) && readInt16(hdl, lon) && readInt16(hdl, lat)
&& readVUInt32(hdl, len))) && readVUInt32(hdl, len)))
return false; return false;
Q_ASSERT(hdl.pos() + len <= segment.end());
poly.type = 0x10000 | (quint16(type)<<8) | (subtype & 0x1F); poly.type = 0x10000 | (quint16(type)<<8) | (subtype & 0x1F);
labelPtr = 0; labelPtr = 0;
@ -239,8 +256,10 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits()))); LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits())));
qint32 lonDelta, latDelta; qint32 lonDelta, latDelta;
HuffmanStream stream(*this, hdl, len, _huffmanTable, BitStream4F bs(*this, hdl, len);
segmentType == Line); HuffmanStreamF stream(bs, _huffmanTable);
if (!stream.init(segmentType == Line))
return false;
if (shift) { if (shift) {
if (!stream.readOffset(lonDelta, latDelta)) if (!stream.readOffset(lonDelta, latDelta))
@ -298,7 +317,11 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
if (subtype & 0x80 && !skipClassFields(hdl)) if (subtype & 0x80 && !skipClassFields(hdl))
return false; return false;
if (subtype & 0x40 && !skipLclFields(hdl, segmentType == Line if (subtype & 0x40 && !skipLclFields(hdl, segmentType == Line
? _linesFlags : _polygonsFlags, segmentType)) ? _linesFlags : _polygonsFlags))
return false;
quint32 gblFlags = (segmentType == Line)
? _linesGblFlags : _polygonGblFlags;
if (gblFlags && !skipGblFields(hdl, gblFlags))
return false; return false;
if (lbl && (labelPtr & 0x3FFFFF)) if (lbl && (labelPtr & 0x3FFFFF))
@ -317,6 +340,7 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
const SubDiv::Segment &segment = (segmentType == IndexedPoint) const SubDiv::Segment &segment = (segmentType == IndexedPoint)
? subdiv->idxPoints() : subdiv->points(); ? subdiv->idxPoints() : subdiv->points();
if (!segment.isValid()) if (!segment.isValid())
return true; return true;
if (!seek(hdl, segment.offset())) if (!seek(hdl, segment.offset()))
@ -379,7 +403,9 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
return false; return false;
if (subtype & 0x80 && !skipClassFields(hdl)) if (subtype & 0x80 && !skipClassFields(hdl))
return false; return false;
if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags, Point)) if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags))
return false;
if (_pointsGblFlags && !skipGblFields(hdl, _pointsGblFlags))
return false; return false;
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()), QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
@ -403,6 +429,86 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
return true; return true;
} }
bool RGNFile::links(Handle &hdl, const SubDiv *subdiv, NETFile *net,
Handle &netHdl, NODFile *nod, Handle &nodHdl, QList<IMG::Poly> *lines) const
{
quint32 size, blockIndexIdSize, blockIndexId;
quint8 flags;
const SubDiv::Segment &segment = subdiv->roadReferences();
if (!segment.isValid())
return true;
if (!seek(hdl, segment.offset()))
return false;
if (!net || !nod)
return false;
if (!(blockIndexIdSize = nod->indexIdSize(nodHdl)))
return false;
while (hdl.pos() < (int)segment.end()) {
if (!readVUInt32(hdl, size))
return false;
int pos = hdl.pos();
if (!(readUInt8(hdl, flags) && readVUInt32(hdl, blockIndexIdSize,
blockIndexId)))
return false;
quint8 bits[3];
for (int i = 0; i < 3; i++)
bits[i] = 0x4000a08 >> (((flags >> (2*i) & 3) << 3) ^ 0x10);
quint8 byteSize = ((bits[0] + bits[1] + bits[2]) + 7) >> 3;
quint32 counts;
if (!readVUInt32(hdl, byteSize, counts))
return false;
quint16 b8 = bits[0] ? (MASK(bits[0]) & counts) + 1 : 0;
quint16 b10 = bits[1] ? (MASK(bits[1]) & (counts >> bits[0])) + 1 : 0;
quint16 b16 = bits[2] ? (MASK(bits[2]) & (counts >> (bits[0] + bits[1])))
+ 1 : 0;
NODFile::BlockInfo blockInfo;
if (!nod->blockInfo(nodHdl, blockIndexId, blockInfo))
return false;
quint8 linkId, lineId;
for (int i = 0; i < b8 + b10 + b16; i++) {
if (!b8 || b8 <= i) {
quint16 v16;
if (!readUInt16(hdl, v16))
return false;
if (!b16 || b8 + b16 <= i) {
int shift = ((i - (b8 + b16)) * 10) % 8;
linkId = (quint8)(v16 >> shift);
lineId = (((v16 >> shift) >> 8) & 3) + 1;
if (shift < 6 && i < b8 + b10 + b16 - 1)
seek(hdl, hdl.pos() - 1);
} else {
linkId = (quint8)v16;
lineId = v16 >> 8;
Q_ASSERT(lineId > 4);
}
} else {
if (!readUInt8(hdl, linkId))
return false;
lineId = 0;
}
net->link(subdiv, netHdl, nod, nodHdl, blockInfo, linkId, lineId,
_huffmanTable, lines);
}
Q_ASSERT(pos + (int)size == hdl.pos());
}
return true;
}
QMap<RGNFile::SegmentType, SubDiv::Segment> RGNFile::segments(Handle &hdl, QMap<RGNFile::SegmentType, SubDiv::Segment> RGNFile::segments(Handle &hdl,
SubDiv *subdiv) const SubDiv *subdiv) const
{ {

View File

@ -8,6 +8,7 @@
class LBLFile; class LBLFile;
class NETFile; class NETFile;
class NODFile;
class RGNFile : public SubFile class RGNFile : public SubFile
{ {
@ -46,6 +47,8 @@ public:
QList<IMG::Poly> *polys) const; QList<IMG::Poly> *polys) const;
bool extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl, bool extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
Handle &lblHdl, QList<IMG::Point> *points) const; Handle &lblHdl, QList<IMG::Point> *points) const;
bool links(Handle &hdl, const SubDiv *subdiv, NETFile *net, Handle &netHdl,
NODFile *nod, Handle &nodHdl, QList<IMG::Poly> *lines) const;
bool subdivInit(Handle &hdl, SubDiv *subdiv) const; bool subdivInit(Handle &hdl, SubDiv *subdiv) const;
@ -54,8 +57,9 @@ private:
const; const;
void clearFlags(); void clearFlags();
bool skipClassFields(Handle &hdl) const; bool skipClassFields(Handle &hdl) const;
bool skipLclFields(Handle &hdl, const quint32 flags[3], SegmentType type) bool skipLclFields(Handle &hdl, const quint32 flags[3])
const; const;
bool skipGblFields(Handle &hdl, quint32 flags) const;
quint32 _offset; quint32 _offset;
quint32 _size; quint32 _size;
@ -69,6 +73,9 @@ private:
quint32 _pointsOffset; quint32 _pointsOffset;
quint32 _pointsSize; quint32 _pointsSize;
quint32 _pointsFlags[3]; quint32 _pointsFlags[3];
quint32 _polygonGblFlags;
quint32 _linesGblFlags;
quint32 _pointsGblFlags;
HuffmanTable _huffmanTable; HuffmanTable _huffmanTable;

View File

@ -21,8 +21,9 @@ public:
quint32 _offset, _end; quint32 _offset, _end;
}; };
SubDiv(quint32 offset, qint32 lon, qint32 lat, int bits, quint8 objects) SubDiv(quint32 offset, qint32 lon, qint32 lat, quint8 level, quint8 bits,
: _lon(lon), _lat(lat), _bits(bits), _init(false) quint8 objects) : _lon(lon), _lat(lat), _level(level), _bits(bits),
_init(false)
{ {
_tre.objects = objects; _tre.objects = objects;
_tre.offset = offset; _tre.offset = offset;
@ -78,6 +79,7 @@ public:
qint32 lon() const {return _lon;} qint32 lon() const {return _lon;}
qint32 lat() const {return _lat;} qint32 lat() const {return _lat;}
quint8 bits() const {return _bits;} quint8 bits() const {return _bits;}
quint8 level() const {return _level;}
// Valid only after initialization // Valid only after initialization
Segment points() const Segment points() const
@ -94,6 +96,8 @@ public:
{return Segment(_rgn.extLinesOffset, _rgn.extLinesEnd);} {return Segment(_rgn.extLinesOffset, _rgn.extLinesEnd);}
Segment extPolygons() const Segment extPolygons() const
{return Segment(_rgn.extPolygonsOffset, _rgn.extPolygonsEnd);} {return Segment(_rgn.extPolygonsOffset, _rgn.extPolygonsEnd);}
Segment roadReferences() const
{return Segment(_rgn.roadReferencesOffset, _rgn.roadReferencesEnd);}
// Valid only until initialization // Valid only until initialization
quint8 objects() const {return _tre.objects;} quint8 objects() const {return _tre.objects;}
@ -142,6 +146,7 @@ private:
}; };
qint32 _lon, _lat; qint32 _lon, _lat;
quint8 _level;
quint8 _bits; quint8 _bits;
bool _init; bool _init;
union { union {

View File

@ -3,26 +3,28 @@
#include "subfile.h" #include "subfile.h"
#define mod2n(x, m) ((x) & ((m) - 1));
bool SubFile::seek(Handle &handle, quint32 pos) const bool SubFile::seek(Handle &handle, quint32 pos) const
{ {
if (handle._file) { if (handle._file) {
int blockNum = pos / BLOCK_SIZE; int blockNum = pos >> BLOCK_BITS;
if (handle._blockNum != blockNum) { if (handle._blockNum != blockNum) {
if (!handle._file->seek((qint64)blockNum * BLOCK_SIZE)) if (!handle._file->seek((quint64)blockNum << BLOCK_BITS))
return false; return false;
if (handle._file->read(handle._data.data(), BLOCK_SIZE) < 0) if (handle._file->read(handle._data.data(), (1<<BLOCK_BITS)) < 0)
return false; return false;
handle._blockNum = blockNum; handle._blockNum = blockNum;
} }
handle._blockPos = pos % BLOCK_SIZE; handle._blockPos = mod2n(pos, 1U<<BLOCK_BITS);
handle._pos = pos; handle._pos = pos;
return true; return true;
} else { } else {
quint32 blockSize = _img->blockSize(); quint32 blockBits = _img->blockBits();
int blockNum = pos / blockSize; int blockNum = pos >> blockBits;
if (handle._blockNum != blockNum) { if (handle._blockNum != blockNum) {
if (blockNum >= _blocks->size()) if (blockNum >= _blocks->size())
@ -32,7 +34,7 @@ bool SubFile::seek(Handle &handle, quint32 pos) const
handle._blockNum = blockNum; handle._blockNum = blockNum;
} }
handle._blockPos = pos % blockSize; handle._blockPos = mod2n(pos, 1U<<blockBits);
handle._pos = pos; handle._pos = pos;
return true; return true;
@ -70,6 +72,22 @@ bool SubFile::readVUInt32(Handle &hdl, quint32 &val) const
return true; return true;
} }
bool SubFile::readVUInt32(Handle &hdl, quint32 bytes, quint32 &val) const
{
switch (bytes) {
case 1:
return readUInt8(hdl, val);
case 2:
return readUInt16(hdl, val);
case 3:
return readUInt24(hdl, val);
case 4:
return readUInt32(hdl, val);
default:
return false;
}
}
bool SubFile::readVBitfield32(Handle &hdl, quint32 &bitfield) const bool SubFile::readVBitfield32(Handle &hdl, quint32 &bitfield) const
{ {
quint8 bits; quint8 bits;

View File

@ -6,12 +6,12 @@
#include "img.h" #include "img.h"
#define BLOCK_SIZE 4096 #define BLOCK_BITS 12 /* 4096 bytes */
class SubFile class SubFile
{ {
public: public:
enum Type {Unknown, TRE, RGN, LBL, NET, TYP, GMP}; enum Type {Unknown, TRE, RGN, LBL, NET, NOD, TYP, GMP};
class Handle class Handle
{ {
@ -22,9 +22,9 @@ public:
if (subFile && subFile->_path) { if (subFile && subFile->_path) {
_file = new QFile(*(subFile->_path)); _file = new QFile(*(subFile->_path));
_file->open(QIODevice::ReadOnly); _file->open(QIODevice::ReadOnly);
_data.resize(BLOCK_SIZE); _data.resize(1U<<BLOCK_BITS);
} else if (subFile) } else if (subFile)
_data.resize(subFile->_img->blockSize()); _data.resize(1U<<subFile->_img->blockBits());
} }
~Handle() {delete _file;} ~Handle() {delete _file;}
@ -132,6 +132,7 @@ public:
} }
bool readVUInt32(Handle &hdl, quint32 &val) const; bool readVUInt32(Handle &hdl, quint32 &val) const;
bool readVUInt32(Handle &hdl, quint32 bytes, quint32 &val) const;
bool readVBitfield32(Handle &hdl, quint32 &bitfield) const; bool readVBitfield32(Handle &hdl, quint32 &bitfield) const;
QString fileName() const {return _path ? *_path : _img->fileName();} QString fileName() const {return _path ? *_path : _img->fileName();}
@ -142,7 +143,7 @@ protected:
private: private:
bool readByte(Handle &handle, quint8 &val) const bool readByte(Handle &handle, quint8 &val) const
{ {
int blockSize = _img ? _img->blockSize() : BLOCK_SIZE; int blockSize = _img ? 1U<<_img->blockBits() : 1U<<BLOCK_BITS;
val = handle._data.at(handle._blockPos++); val = handle._data.at(handle._blockPos++);
handle._pos++; handle._pos++;
return (handle._blockPos >= blockSize) return (handle._blockPos >= blockSize)

View File

@ -70,14 +70,16 @@ bool TREFile::init()
return false; return false;
if (hdrLen > 0x9A) { if (hdrLen > 0x9A) {
// TRE7 info // TRE7 info + flags
if (!(seek(hdl, _gmpOffset + 0x7C) && readUInt32(hdl, _extended.offset) if (!(seek(hdl, _gmpOffset + 0x7C) && readUInt32(hdl, _extended.offset)
&& readUInt32(hdl, _extended.size) && readUInt32(hdl, _extended.size)
&& readUInt16(hdl, _extended.itemSize))) && readUInt16(hdl, _extended.itemSize) && readUInt32(hdl, _flags)))
return false;
// flags
if (!(seek(hdl, _gmpOffset + 0x86) && readUInt32(hdl, _flags)))
return false; return false;
} else {
_extended.offset = 0;
_extended.size = 0;
_extended.itemSize = 0;
_flags = 0;
} }
// Tile levels // Tile levels
@ -126,9 +128,10 @@ bool TREFile::load(int idx)
QList<SubDiv*> sl; QList<SubDiv*> sl;
SubDiv *s = 0; SubDiv *s = 0;
SubDivTree *tree = new SubDivTree(); SubDivTree *tree = new SubDivTree();
const MapLevel &level = _levels.at(idx);
_subdivs.insert(_levels.at(idx).bits, tree); _subdivs.insert(level.bits, tree);
quint32 skip = 0; quint32 skip = 0;
for (int i = 0; i < idx; i++) for (int i = 0; i < idx; i++)
@ -137,7 +140,7 @@ bool TREFile::load(int idx)
if (!seek(hdl, _subdivOffset + skip * 16)) if (!seek(hdl, _subdivOffset + skip * 16))
return false; return false;
for (int j = 0; j < _levels.at(idx).subdivs; j++) { for (int j = 0; j < level.subdivs; j++) {
quint32 oo; quint32 oo;
qint32 lon, lat, width, height; qint32 lon, lat, width, height;
quint16 nextLevel; quint16 nextLevel;
@ -156,10 +159,10 @@ bool TREFile::load(int idx)
s->setEnd(offset); s->setEnd(offset);
width &= 0x7FFF; width &= 0x7FFF;
width = LS(width, 24 - _levels.at(idx).bits); width = LS(width, 24 - level.bits);
height = LS(height, 24 - _levels.at(idx).bits); height = LS(height, 24 - level.bits);
s = new SubDiv(offset, lon, lat, _levels.at(idx).bits, objects); s = new SubDiv(offset, lon, lat, level.level, level.bits, objects);
sl.append(s); sl.append(s);
double min[2], max[2]; double min[2], max[2];
@ -184,30 +187,40 @@ bool TREFile::load(int idx)
// Objects with extended types (TRE7) // Objects with extended types (TRE7)
if (_extended.size && _extended.itemSize >= 12) { if (_extended.size && _extended.itemSize) {
/* Some maps skip entries for the inherited levels, some don't. Our
decision is based on the difference between the extended subdivs
count and the total subdivs count. */
quint32 totalSubdivs = 0; quint32 totalSubdivs = 0;
for (int i = 0; i < _levels.size(); i++) for (int i = 0; i < _levels.size(); i++)
totalSubdivs += _levels.at(i).subdivs; totalSubdivs += _levels.at(i).subdivs;
quint32 extendedSubdivs = _extended.size / _extended.itemSize; quint32 extendedSubdivs = _extended.size / _extended.itemSize;
quint32 diff = totalSubdivs - extendedSubdivs + 1; quint32 diff = totalSubdivs - extendedSubdivs + 1;
quint32 polygons, lines, points;
if (!seek(hdl, _extended.offset + (skip - diff) * _extended.itemSize)) if (!seek(hdl, _extended.offset + (skip - diff) * _extended.itemSize))
goto error; goto error;
quint32 polygons = 0, lines = 0, points = 0;
for (int i = 0; i < sl.size(); i++) { for (int i = 0; i < sl.size(); i++) {
if (!(readUInt32(hdl, polygons) && readUInt32(hdl, lines) quint32 rb = 0;
&& readUInt32(hdl, points)))
goto error; if (_flags & 1) {
if (!readUInt32(hdl, polygons))
goto error;
rb += 4;
}
if (_flags & 2) {
if (!readUInt32(hdl, lines))
goto error;
rb += 4;
}
if (_flags & 4) {
if (!readUInt32(hdl, points))
goto error;
rb += 4;
}
sl.at(i)->setExtOffsets(polygons, lines, points); sl.at(i)->setExtOffsets(polygons, lines, points);
if (i) if (i)
sl.at(i-1)->setExtEnds(polygons, lines, points); sl.at(i-1)->setExtEnds(polygons, lines, points);
if (!seek(hdl, hdl.pos() + _extended.itemSize - 12)) if (!seek(hdl, hdl.pos() + _extended.itemSize - rb))
goto error; goto error;
} }

View File

@ -29,6 +29,8 @@ SubFile *VectorTile::file(SubFile::Type type)
return _lbl; return _lbl;
case SubFile::NET: case SubFile::NET:
return _net; return _net;
case SubFile::NOD:
return _nod;
case SubFile::GMP: case SubFile::GMP:
return _gmp; return _gmp;
default: default:
@ -51,6 +53,9 @@ SubFile *VectorTile::addFile(IMG *img, SubFile::Type type)
case SubFile::NET: case SubFile::NET:
_net = new NETFile(img); _net = new NETFile(img);
return _net; return _net;
case SubFile::NOD:
_nod = new NODFile(img);
return _nod;
case SubFile::GMP: case SubFile::GMP:
_gmp = new SubFile(img); _gmp = new SubFile(img);
return _gmp; return _gmp;
@ -74,6 +79,9 @@ SubFile *VectorTile::addFile(const QString &path, SubFile::Type type)
case SubFile::NET: case SubFile::NET:
_net = new NETFile(path); _net = new NETFile(path);
return _net; return _net;
case SubFile::NOD:
_nod = new NODFile(path);
return _nod;
case SubFile::GMP: case SubFile::GMP:
_gmp = new SubFile(path); _gmp = new SubFile(path);
return _gmp; return _gmp;
@ -96,17 +104,18 @@ bool VectorTile::init()
bool VectorTile::initGMP() bool VectorTile::initGMP()
{ {
SubFile::Handle hdl(_gmp); SubFile::Handle hdl(_gmp);
quint32 tre, rgn, lbl, net; quint32 tre, rgn, lbl, net, nod;
if (!(_gmp->seek(hdl, 0x19) && _gmp->readUInt32(hdl, tre) if (!(_gmp->seek(hdl, 0x19) && _gmp->readUInt32(hdl, tre)
&& _gmp->readUInt32(hdl, rgn) && _gmp->readUInt32(hdl, lbl) && _gmp->readUInt32(hdl, rgn) && _gmp->readUInt32(hdl, lbl)
&& _gmp->readUInt32(hdl, net))) && _gmp->readUInt32(hdl, net) && _gmp->readUInt32(hdl, nod)))
return false; return false;
_tre = tre ? new TREFile(_gmp, tre) : 0; _tre = tre ? new TREFile(_gmp, tre) : 0;
_rgn = rgn ? new RGNFile(_gmp, rgn) : 0; _rgn = rgn ? new RGNFile(_gmp, rgn) : 0;
_lbl = lbl ? new LBLFile(_gmp, lbl) : 0; _lbl = lbl ? new LBLFile(_gmp, lbl) : 0;
_net = net ? new NETFile(_gmp, net) : 0; _net = net ? new NETFile(_gmp, net) : 0;
_nod = nod ? new NODFile(_gmp, nod) : 0;
return true; return true;
} }
@ -115,7 +124,7 @@ void VectorTile::polys(const RectC &rect, int bits, bool baseMap,
QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines, QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines,
QCache<const SubDiv *, IMG::Polys> *polyCache) const QCache<const SubDiv *, IMG::Polys> *polyCache) const
{ {
SubFile::Handle rgnHdl(_rgn), lblHdl(_lbl), netHdl(_net); SubFile::Handle rgnHdl(_rgn), lblHdl(_lbl), netHdl(_net), nodHdl(_nod);
if (!_rgn->initialized() && !_rgn->init(rgnHdl)) if (!_rgn->initialized() && !_rgn->init(rgnHdl))
return; return;
@ -140,6 +149,7 @@ void VectorTile::polys(const RectC &rect, int bits, bool baseMap,
lblHdl, &p); lblHdl, &p);
_rgn->extPolyObjects(rgnHdl, subdiv, shift, RGNFile::Line, _lbl, _rgn->extPolyObjects(rgnHdl, subdiv, shift, RGNFile::Line, _lbl,
lblHdl, &l); lblHdl, &l);
_rgn->links(rgnHdl, subdiv, _net, netHdl, _nod, nodHdl, &l);
copyPolys(rect, &p, polygons); copyPolys(rect, &p, polygons);
copyPolys(rect, &l, lines); copyPolys(rect, &l, lines);

View File

@ -6,13 +6,15 @@
#include "rgnfile.h" #include "rgnfile.h"
#include "lblfile.h" #include "lblfile.h"
#include "netfile.h" #include "netfile.h"
#include "nodfile.h"
class VectorTile { class VectorTile {
public: public:
VectorTile() : _tre(0), _rgn(0), _lbl(0), _net(0), _gmp(0) {} VectorTile() : _tre(0), _rgn(0), _lbl(0), _net(0), _nod(0), _gmp(0) {}
~VectorTile() ~VectorTile()
{ {
delete _tre; delete _rgn; delete _lbl; delete _net; delete _gmp; delete _tre; delete _rgn; delete _lbl; delete _net; delete _nod;
delete _gmp;
} }
bool init(); bool init();
@ -37,7 +39,7 @@ public:
{ {
return (type == SubFile::TRE || type == SubFile::LBL return (type == SubFile::TRE || type == SubFile::LBL
|| type == SubFile::RGN || type == SubFile::NET || type == SubFile::RGN || type == SubFile::NET
|| type == SubFile::GMP); || type == SubFile::NOD || type == SubFile::GMP);
} }
private: private:
@ -47,6 +49,7 @@ private:
RGNFile *_rgn; RGNFile *_rgn;
LBLFile *_lbl; LBLFile *_lbl;
NETFile *_net; NETFile *_net;
NODFile *_nod;
SubFile *_gmp; SubFile *_gmp;
}; };