1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-02-08 04:25:12 +01:00
GPXSee/src/map/IMG/nodfile.cpp

519 lines
12 KiB
C++
Raw Normal View History

2022-11-04 09:03:36 +01:00
#include "common/util.h"
2021-01-11 23:41:44 +01:00
#include "bitstream.h"
#include "nodfile.h"
2021-09-20 21:43:17 +02:00
using namespace Garmin;
2021-04-10 15:27:40 +02:00
using namespace IMG;
2020-09-18 20:56:00 +02:00
2022-04-14 04:31:46 +02:00
#define DELTA(val, llBits, maxBits) \
(((int)((val) << (32 - (llBits))) >> (32 - (llBits))) << (32 - (maxBits)))
2020-09-18 20:56:00 +02:00
2020-09-29 18:53:49 +02:00
static const struct
{
2020-09-18 20:56:00 +02:00
quint8 lon;
quint8 lat;
} LLBITS[] = {
{0xc, 0xc}, {0x8, 0x10}, {0x10, 0x8}, {0x10, 0x10}, {0xc, 0x14},
{0x14, 0xc}, {0x14, 0x14}
};
2020-09-29 18:53:49 +02:00
struct NodeOffset
{
bool ext;
union {
qint32 offset;
quint8 id;
} u;
2020-09-18 20:56:00 +02:00
};
2020-09-29 18:53:49 +02:00
static bool adjDistInfo(BitStream1 &bs, bool extraBit, bool &eog)
2020-09-18 20:56:00 +02:00
{
quint32 data, cnt;
2021-01-10 15:21:00 +01:00
if (!bs.read((int)extraBit | 8, data))
2020-09-18 20:56:00 +02:00
return false;
2021-01-10 15:42:29 +01:00
if (!extraBit)
data <<= 1;
2020-09-18 20:56:00 +02:00
2020-09-29 18:53:49 +02:00
eog |= (quint8)data & 1;
2020-09-18 20:56:00 +02:00
data >>= 1;
for (cnt = 0; (data >> cnt) & 1; cnt++) {
if (cnt == 4)
break;
}
if (!bs.read(cnt * 4, data))
return false;
return true;
}
2020-09-29 18:53:49 +02:00
static bool adjNodeInfo(BitStream1 &bs, bool extraBit, NodeOffset &offset)
2020-09-18 20:56:00 +02:00
{
quint32 data;
if (!bs.read(9, data))
return false;
2021-01-10 15:42:29 +01:00
if (!extraBit)
data <<= 1;
2020-09-18 20:56:00 +02:00
2020-09-29 18:53:49 +02:00
if (data & 1) {
offset.ext = true;
offset.u.id = data >> 1;
} else {
2020-09-18 20:56:00 +02:00
quint32 bits = (data >> 1) & 7;
quint32 data2;
if (!bs.read(bits + extraBit + 1, data2))
return false;
data = data2 << (6 - extraBit) | data >> 4;
bits = 0x19 - bits;
2020-09-29 18:53:49 +02:00
offset.ext = false;
offset.u.offset = ((qint32)(data << bits) >> bits);
2020-09-18 20:56:00 +02:00
}
return true;
}
static bool skipOptAdjData(BitStream1 &bs)
{
// TODO
Q_UNUSED(bs);
Q_ASSERT(false);
return false;
}
bool NODFile::load(Handle &hdl)
{
quint16 hdrLen;
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)))
return false;
2021-03-19 20:09:11 +01:00
if (hdrLen >= 0x7F) {
if (!(seek(hdl, _gmpOffset + 0x1d) && readUInt32(hdl, _flags)
&& readByte(hdl, &_blockShift) && readByte(hdl, &_nodeShift)))
return false;
2020-09-18 20:56:00 +02:00
2022-03-25 19:28:32 +01:00
if (!(seek(hdl, _gmpOffset + 0x67) && readUInt32(hdl, _block.offset)
&& readUInt32(hdl, _block.size) && readUInt16(hdl, _blockRecordSize)
&& readUInt32(hdl, _index.offset) && readUInt32(hdl, _index.size)
2021-03-19 20:09:11 +01:00
&& readUInt16(hdl, _indexRecordSize) && readUInt32(hdl, _indexFlags)))
return false;
2022-03-25 19:28:32 +01:00
if (!_indexRecordSize || _index.size < _indexRecordSize)
2021-03-19 20:09:11 +01:00
return false;
2022-03-25 19:28:32 +01:00
quint32 indexCount = _index.size / _indexRecordSize;
2021-03-19 20:09:11 +01:00
_indexIdSize = byteSize(indexCount - 1);
}
return true;
}
2020-09-18 20:56:00 +02:00
bool NODFile::readBlock(Handle &hdl, quint32 blockOffset,
BlockInfo &blockInfo) const
{
2020-09-18 20:56:00 +02:00
blockInfo.offset = blockOffset;
2022-03-25 19:28:32 +01:00
return (seek(hdl, blockInfo.offset + _block.offset)
2021-09-29 19:52:39 +02:00
&& readUInt16(hdl, blockInfo.hdr.flags)
2021-09-30 20:26:16 +02:00
&& readUInt32(hdl, blockInfo.hdr.nodeLonBase)
&& readUInt32(hdl, blockInfo.hdr.nodeLatBase)
&& readUInt32(hdl, blockInfo.hdr.linkInfoOffsetBase)
2021-09-29 19:52:39 +02:00
&& readUInt16(hdl, blockInfo.hdr.linkInfoSize)
&& readByte(hdl, &blockInfo.hdr.linksCount)
&& readByte(hdl, &blockInfo.hdr.nodesCount)
&& readByte(hdl, &blockInfo.hdr.linkTypesCount));
2020-09-18 20:56:00 +02:00
}
2020-09-18 20:56:00 +02:00
bool NODFile::blockInfo(Handle &hdl, quint32 blockId, BlockInfo &blockInfo) const
{
quint32 blockOffset;
2022-03-25 19:28:32 +01:00
quint32 offset = _indexRecordSize * blockId + _index.offset;
2020-09-18 20:56:00 +02:00
quint32 offsetSize = (_indexFlags & 3) + 1;
2022-03-25 19:28:32 +01:00
if (offset > _index.offset + _index.size)
2020-09-18 20:56:00 +02:00
return false;
if (!(seek(hdl, offset) && readVUInt32(hdl, offsetSize, blockOffset)))
return false;
2020-09-18 20:56:00 +02:00
return readBlock(hdl, blockOffset << _blockShift, blockInfo);
}
bool NODFile::linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
LinkInfo &linkInfo) const
{
2021-09-29 19:52:39 +02:00
if (linkId >= blockInfo.hdr.linksCount)
2020-09-18 20:56:00 +02:00
return false;
2022-03-25 19:28:32 +01:00
quint32 infoOffset = _block.offset + blockInfo.offset + blockInfo.hdr.size()
2021-09-29 19:52:39 +02:00
+ ((blockInfo.hdr.linkInfoSize * linkId) >> 3);
quint32 s1 = ((blockInfo.hdr.flags >> 2) & 0x1f) + 8;
quint32 s2 = (blockInfo.hdr.flags >> 7) & 0xf;
quint32 skip = (blockInfo.hdr.linkInfoSize * linkId) & 7;
2022-03-25 19:28:32 +01:00
if (infoOffset > _block.offset + _block.size || infoOffset < blockInfo.offset)
2020-09-18 20:56:00 +02:00
return false;
if (!seek(hdl, infoOffset))
return false;
2021-08-23 22:27:36 +02:00
quint32 padding;
2022-03-25 19:28:32 +01:00
BitStream1 bs(*this, hdl, _block.offset + _block.size - infoOffset);
2021-08-23 22:27:36 +02:00
if (!(bs.read(skip, padding) && bs.read(0xc, linkInfo.flags)))
return false;
2021-08-23 22:27:36 +02:00
if (!(linkInfo.flags & 0x100)) {
if (!bs.read(s1, linkInfo.linkOffset))
return false;
2022-04-13 22:13:57 +02:00
linkInfo.nodeOffset = 0xFFFFFFFF;
} else {
if (!bs.read(s1 - s2, linkInfo.linkOffset))
return false;
2021-09-30 20:26:16 +02:00
linkInfo.linkOffset += blockInfo.hdr.linkInfoOffsetBase;
2020-09-18 20:56:00 +02:00
if (!bs.read(s2, linkInfo.nodeOffset))
return false;
linkInfo.nodeOffset = (blockInfo.offset - linkInfo.nodeOffset)
>> _nodeShift;
}
2020-09-18 20:56:00 +02:00
return true;
}
2022-04-13 22:13:57 +02:00
bool NODFile::nodeInfo(Handle &hdl, AdjacencyInfo &adj) const
2020-09-18 20:56:00 +02:00
{
2022-04-13 22:13:57 +02:00
quint32 infoOffset = (adj.nodeOffset << _nodeShift) + _block.offset;
if (infoOffset > _block.offset + _block.size
|| infoOffset < adj.blockInfo.offset)
2020-09-18 20:56:00 +02:00
return false;
if (!seek(hdl, infoOffset))
return false;
2022-03-25 19:28:32 +01:00
BitStream1 bs(*this, hdl, _block.offset + _block.size - infoOffset);
2020-09-18 20:56:00 +02:00
2022-04-13 22:13:57 +02:00
if (!bs.read(8, adj.nodeInfo.flags))
2020-09-18 20:56:00 +02:00
return false;
2022-04-13 22:13:57 +02:00
if ((adj.nodeInfo.flags & 7) >= ARRAY_SIZE(LLBITS))
2020-09-18 20:56:00 +02:00
return false;
2022-04-13 22:13:57 +02:00
quint8 lonBits = LLBITS[adj.nodeInfo.flags & 7].lon;
quint8 latBits = LLBITS[adj.nodeInfo.flags & 7].lat;
2020-09-18 20:56:00 +02:00
quint8 maxBits = ((_flags >> 10) & 7) | 0x18;
quint32 lon, lat;
if (!(bs.read(lonBits, lon) && bs.read(latBits, lat)))
return false;
2021-09-30 20:26:16 +02:00
QPoint pos(
2022-04-14 04:31:46 +02:00
adj.blockInfo.hdr.nodeLonBase + DELTA(lon, lonBits, maxBits),
adj.blockInfo.hdr.nodeLatBase + DELTA(lat, latBits, maxBits));
2020-09-18 20:56:00 +02:00
2022-04-13 22:13:57 +02:00
if ((maxBits < 0x1c) && (adj.nodeInfo.flags & 8)) {
2020-09-18 20:56:00 +02:00
quint8 extraBits = 0x1c - maxBits;
quint32 extraLon, extraLat;
if (!(bs.read(extraBits, extraLon) && bs.read(extraBits, extraLat)))
return false;
pos.setX(pos.x() | extraLon << 4); pos.setY(pos.y() | extraLat << 4);
}
2021-08-23 22:27:36 +02:00
// TODO?: check and adjust (shift) coordinates
2020-09-18 20:56:00 +02:00
2022-04-13 22:13:57 +02:00
adj.nodeInfo.pos = pos;
2020-09-18 20:56:00 +02:00
return true;
}
bool NODFile::nodeOffset(Handle &hdl, const BlockInfo &blockInfo,
quint8 nodeId, quint32 &nodeOffset) const
{
2021-09-29 19:52:39 +02:00
if (nodeId >= blockInfo.hdr.nodesCount)
2020-09-18 20:56:00 +02:00
return false;
2022-03-25 19:28:32 +01:00
quint32 offset = _block.offset + blockInfo.offset + blockInfo.hdr.size()
2021-09-29 19:52:39 +02:00
+ bs(blockInfo.hdr.linksCount * blockInfo.hdr.linkInfoSize)
+ nodeId * 3;
2020-09-18 20:56:00 +02:00
2021-09-29 19:52:39 +02:00
return (seek(hdl, offset) && readUInt24(hdl, nodeOffset));
}
2020-09-18 20:56:00 +02:00
bool NODFile::nodeBlock(Handle &hdl, quint32 nodeOffset,
BlockInfo &blockInfo) const
{
int low = 0;
2022-03-25 19:28:32 +01:00
int high = _index.size / _indexRecordSize - 1;
2020-09-18 20:56:00 +02:00
quint32 offsetSize = (_indexFlags & 3) + 1;
while (low <= high) {
2021-01-11 23:38:46 +01:00
int m = ((low + high) / 2);
2022-03-25 19:28:32 +01:00
quint32 offset = _indexRecordSize * m + _index.offset;
2020-09-18 20:56:00 +02:00
quint32 blockOffset, prevBlockOffset;
if (m > 0) {
if (!(seek(hdl, offset - _indexRecordSize)
&& readVUInt32(hdl, offsetSize, prevBlockOffset)
&& readVUInt32(hdl, offsetSize, blockOffset)))
return false;
} else {
if (!(seek(hdl, offset)
&& readVUInt32(hdl, offsetSize, blockOffset)))
return false;
prevBlockOffset = 0;
}
prevBlockOffset <<= _blockShift;
blockOffset <<= _blockShift;
if (blockOffset < nodeOffset)
low = m + 1;
else {
if (prevBlockOffset <= nodeOffset)
return readBlock(hdl, blockOffset, blockInfo);
else
high = m - 1;
}
}
return false;
}
bool NODFile::absAdjInfo(Handle &hdl, AdjacencyInfo &adj) const
{
BitStream1 bs(*this, hdl, _block.offset + _block.size - pos(hdl));
2020-09-18 20:56:00 +02:00
2021-09-29 19:52:39 +02:00
quint8 linkId = adj.blockInfo.hdr.linksCount;
2020-09-18 20:56:00 +02:00
quint32 m2p = 2;
quint32 skip = 8;
quint32 flags;
2020-09-26 16:02:14 +02:00
quint32 nextOffset = 0xFFFFFFFF;
2020-09-18 20:56:00 +02:00
bool extraBit = (adj.nodeInfo.flags >> 6) & 1;
bool linkIdValid = true;
bool firstLoop = true;
2020-09-29 18:53:49 +02:00
NodeOffset offset;
2020-09-18 20:56:00 +02:00
do {
2020-09-29 18:53:49 +02:00
adj.eog = false;
2020-09-18 20:56:00 +02:00
if (!bs.read(8, flags))
return false;
if (firstLoop) {
skip >>= (flags >> 5) & 1;
flags |= 0x20;
2021-08-23 22:27:36 +02:00
firstLoop = false;
2020-09-18 20:56:00 +02:00
}
2021-08-23 22:27:36 +02:00
2020-09-18 20:56:00 +02:00
quint32 f4 = flags & 0x10;
quint32 f4sn = (f4 >> 4) ^ 1;
quint32 m1 = (flags >> 5) & f4sn;
quint32 m2 = (f4 >> 3) | (f4sn & (flags >> 6));
if (m1) {
if (!bs.read(8, linkId))
return false;
linkIdValid = true;
}
if ((m2 != m2p) || (flags & 0x10) || m1) {
quint32 data;
if (!bs.read(skip, data))
return false;
}
if (!(flags & 0x10)) {
2020-09-29 18:53:49 +02:00
if (!adjDistInfo(bs, (m2 == 1 && linkIdValid), adj.eog))
2020-09-18 20:56:00 +02:00
return false;
2020-09-29 18:53:49 +02:00
if (!adjNodeInfo(bs, extraBit, offset))
2020-09-18 20:56:00 +02:00
return false;
2020-09-29 18:53:49 +02:00
if (!offset.ext)
nextOffset = adj.nodeOffset + offset.u.offset;
else if (!nodeOffset(adj.extHdl, adj.blockInfo, offset.u.id,
nextOffset))
2020-09-18 20:56:00 +02:00
return false;
m2p = m2;
}
if (flags & 0x8) {
quint32 data;
if (!bs.read(8, data))
return false;
if (!(data & 0xe0)) {
if (!bs.read(8, data))
return false;
}
}
if ((_flags & 0x18) && !skipOptAdjData(bs))
return false;
if ((m2 == 1) && linkIdValid) {
LinkInfo li;
if (adj.linkId == 0xFFFFFFFF) {
if (!linkInfo(adj.extHdl, adj.blockInfo, linkId, li))
return false;
} else
li.linkOffset = 0xFFFFFFFF;
if ((adj.linkOffset == li.linkOffset) || (adj.linkId == linkId)) {
adj.nodeOffset = nextOffset;
2020-09-29 18:53:49 +02:00
if (offset.ext) {
2020-09-18 20:56:00 +02:00
adj.linkId = 0xFFFFFFFF;
return nodeBlock(hdl, adj.nodeOffset << _nodeShift,
adj.blockInfo);
} else {
adj.linkId = linkId;
return true;
}
}
linkIdValid = false;
}
} while (!(flags & 0x80));
adj.nodeOffset = 0xFFFFFFFF;
return true;
}
bool NODFile::relAdjInfo(Handle &hdl, AdjacencyInfo &adj) const
{
BitStream1 bs(*this, hdl, _block.offset + _block.size - pos(hdl));
2020-09-18 20:56:00 +02:00
2021-09-29 19:52:39 +02:00
quint32 linkId = adj.blockInfo.hdr.linksCount;
2020-09-18 20:56:00 +02:00
quint32 skip = 8;
quint32 flagsBits = 8;
quint32 flags;
2020-09-26 16:02:14 +02:00
quint32 nextOffset = 0xFFFFFFFF;
2020-09-29 18:53:49 +02:00
NodeOffset offset;
2020-09-18 20:56:00 +02:00
bool extraBit = (adj.nodeInfo.flags >> 6) & 1;
bool linkIdValid = true;
bool firstLoop = true;
do {
2020-09-29 18:53:49 +02:00
adj.eog = false;
2020-09-18 20:56:00 +02:00
if (!bs.read(flagsBits, flags))
return false;
flags <<= (8U - flagsBits);
if (firstLoop) {
skip >>= (flags >> 5) & 1;
flags = ((flags >> 1) & 0x20) | (flags & 0xffffffdf);
2021-08-23 22:27:36 +02:00
firstLoop = false;
2020-09-18 20:56:00 +02:00
}
flagsBits >>= (flags >> 3) & 1;
quint32 m = (((flags & 0x70) == 0x30) << 1) | ((flags >> 6) & 1);
if (!m) {
adj.nodeOffset = 0xFFFFFFFF;
return true;
}
if ((flags & 0x60) == 0x60) {
if (!bs.read(8, linkId))
return false;
linkIdValid = true;
}
if (((flags & 0x70) == 0x70)) {
quint32 data;
if (!bs.read(skip, data))
return false;
}
if ((flags & 0x50) == 0x50) {
2020-09-29 18:53:49 +02:00
if (!adjDistInfo(bs, false, adj.eog))
2020-09-18 20:56:00 +02:00
return false;
2020-09-29 18:53:49 +02:00
adj.eog = true;
2020-09-18 20:56:00 +02:00
}
if ((flags >> 6) & 1) {
2020-09-29 18:53:49 +02:00
if (!adjNodeInfo(bs, extraBit, offset))
2020-09-18 20:56:00 +02:00
return false;
2020-09-29 18:53:49 +02:00
if (!offset.ext)
nextOffset = adj.nodeOffset + offset.u.offset;
else if (!nodeOffset(adj.extHdl, adj.blockInfo, offset.u.id,
nextOffset))
2020-09-18 20:56:00 +02:00
return false;
}
if ((_flags & 0x18) && !skipOptAdjData(bs))
return false;
if (((m == 1) && linkIdValid)) {
LinkInfo li;
if (adj.linkId == 0xFFFFFFFF) {
if (!linkInfo(adj.extHdl, adj.blockInfo, linkId, li))
return false;
} else
li.linkOffset = 0xFFFFFFFF;
if ((adj.linkOffset == li.linkOffset) || (adj.linkId == linkId)) {
adj.nodeOffset = nextOffset;
2020-09-29 18:53:49 +02:00
if (offset.ext) {
2020-09-18 20:56:00 +02:00
adj.linkId = 0xFFFFFFFF;
return nodeBlock(hdl, adj.nodeOffset << _nodeShift,
adj.blockInfo);
} else {
adj.linkId = linkId;
return true;
}
}
linkIdValid = false;
}
} while (!(flags & 0x80));
adj.nodeOffset = 0xFFFFFFFF;
return true;
}
2022-04-13 22:13:57 +02:00
int NODFile::nextNode(Handle &hdl, AdjacencyInfo &adj) const
2020-09-18 20:56:00 +02:00
{
2022-04-13 22:13:57 +02:00
if (adj.nodeOffset == 0xFFFFFFFF)
2020-09-18 20:56:00 +02:00
return 1;
2022-04-13 22:13:57 +02:00
if (!nodeInfo(hdl, adj))
2020-09-18 20:56:00 +02:00
return -1;
2022-04-13 22:13:57 +02:00
if (!adjacencyInfo(hdl, adj))
2020-09-18 20:56:00 +02:00
return -1;
return 0;
}
bool NODFile::linkType(Handle &hdl, const BlockInfo &blockInfo, quint8 linkId,
quint32 &type) const
{
2022-03-25 19:28:32 +01:00
quint32 offset = _block.offset + blockInfo.offset + blockInfo.hdr.size()
2021-09-29 19:52:39 +02:00
+ bs(blockInfo.hdr.linksCount * blockInfo.hdr.linkInfoSize)
+ blockInfo.hdr.nodesCount * 3;
2021-01-11 23:38:46 +01:00
int low = 0;
2021-09-29 19:52:39 +02:00
int high = blockInfo.hdr.linkTypesCount - 1;
quint16 val;
2021-01-11 23:38:46 +01:00
while (low <= high) {
int m = ((low + high) / 2);
2021-01-11 23:38:46 +01:00
if (!(seek(hdl, offset + _blockRecordSize * m) && readUInt16(hdl, val)))
return false;
2021-01-11 23:38:46 +01:00
if ((val >> 8) < linkId)
low = m + 1;
else if ((val >> 8) > linkId)
high = m - 1;
else {
type = (val & 0x3f) << 8;
return true;
}
}
2021-01-11 23:38:46 +01:00
if (high < 0)
return false;
2021-09-29 19:52:39 +02:00
if ((blockInfo.hdr.linkTypesCount > 1)
&& !(seek(hdl, offset + _blockRecordSize * high) && readUInt16(hdl, val)))
2021-01-11 23:38:46 +01:00
return false;
type = (val & 0x3f) << 8;
return true;
}