1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-28 13:41:16 +01:00
GPXSee/src/map/ozf.cpp

263 lines
5.3 KiB
C++
Raw Normal View History

2017-08-22 15:57:37 +02:00
#include <cstring>
2017-04-14 22:39:33 +02:00
#include <QtEndian>
#include <QFile>
2021-10-10 08:38:38 +02:00
#include "common/color.h"
2017-04-14 22:39:33 +02:00
#include "ozf.h"
2017-05-03 21:34:13 +02:00
#define OZF2_MAGIC 0x7778
#define OZF3_MAGIC 0x7780
static const quint8 XKEY[] =
{
0x2D, 0x4A, 0x43, 0xF1, 0x27, 0x9B, 0x69, 0x4F,
0x36, 0x52, 0x87, 0xEC, 0x5F, 0x42, 0x53, 0x22,
0x9E, 0x8B, 0x2D, 0x83, 0x3D, 0xD2, 0x84, 0xBA,
0xD8, 0x5B
};
static void decrypt(void *data, size_t size, quint8 init)
{
for (size_t i = 0; i < size; i++)
reinterpret_cast<quint8*>(data)[i] ^= XKEY[i % sizeof(XKEY)] + init;
}
2017-04-14 22:39:33 +02:00
template<class T> bool OZF::readValue(T &val)
{
T data;
if (_file.read((char*)&data, sizeof(T)) < (qint64)sizeof(T))
return false;
2017-05-03 21:34:13 +02:00
if (_decrypt)
decrypt(&data, sizeof(T), _key);
2019-08-13 20:50:43 +02:00
val = qFromLittleEndian(data);
2017-04-14 22:39:33 +02:00
return true;
}
2017-08-11 00:22:21 +02:00
bool OZF::read(void *data, size_t size, size_t decryptSize)
2017-05-03 21:34:13 +02:00
{
if (_file.read((char*)data, size) < (qint64)size)
return false;
if (_decrypt)
2017-08-11 00:22:21 +02:00
decrypt(data, decryptSize ? qMin(decryptSize, size) : size, _key);
2017-05-03 21:34:13 +02:00
return true;
}
2017-08-11 00:22:21 +02:00
bool OZF::initOZF3()
2017-05-03 21:34:13 +02:00
{
quint8 randomNumber, initial;
2017-08-11 00:22:21 +02:00
quint8 h1[8];
2017-08-22 15:57:37 +02:00
quint8 h2[16], h2d[16];
2017-05-03 21:34:13 +02:00
if (!_file.seek(14))
return false;
if (!readValue(randomNumber))
return false;
if (!_file.seek(162))
return false;
if (!readValue(initial))
return false;
2017-08-11 00:22:21 +02:00
_decrypt = true;
_key = initial;
if (!_file.seek(0))
return false;
if (!read(h1, sizeof(h1)))
return false;
_tileSize = *(h1 + 6);
2017-08-22 15:57:37 +02:00
if (!_file.seek(15 + randomNumber + 4))
return false;
if (_file.read((char*)h2, sizeof(h2)) != (qint64)sizeof(h2))
return false;
2017-05-03 21:34:13 +02:00
2017-08-22 15:57:37 +02:00
for (int i = 0; i < 256; i++) {
memcpy(h2d, h2, sizeof(h2d));
decrypt(h2d, sizeof(h2d), (quint8)i);
2017-05-03 21:34:13 +02:00
2017-08-22 15:57:37 +02:00
if ((quint32)*h2d == 40 && (quint16)*(h2d + 12) == 1
&& (quint16)*(h2d + 14) == 8) {
_key = (quint8)i;
return true;
}
}
return false;
2017-05-03 21:34:13 +02:00
}
2017-08-11 00:22:21 +02:00
bool OZF::initOZF2()
{
if (!_file.seek(6))
return false;
if (!readValue(_tileSize))
return false;
return true;
}
2017-04-14 22:39:33 +02:00
bool OZF::readHeaders()
{
quint16 magic;
2017-05-03 21:34:13 +02:00
if (!readValue(magic))
2017-04-14 22:39:33 +02:00
return false;
2017-05-03 21:34:13 +02:00
if (magic == OZF2_MAGIC) {
2017-08-11 00:22:21 +02:00
if (!initOZF2())
2017-05-03 21:34:13 +02:00
return false;
} else if (magic == OZF3_MAGIC) {
2017-08-11 00:22:21 +02:00
if (!initOZF3())
2017-05-03 21:34:13 +02:00
return false;
2017-08-22 15:57:37 +02:00
} else {
qWarning("%s: not a OZF2/OZF3 file", qPrintable(_file.fileName()));
2017-04-14 22:39:33 +02:00
return false;
2017-08-22 15:57:37 +02:00
}
2017-04-14 22:39:33 +02:00
return true;
}
bool OZF::readTileTable()
{
2019-03-07 22:58:43 +01:00
quint32 tableOffset, headerOffset, w, h;
2017-04-14 22:39:33 +02:00
quint16 x, y;
2017-08-11 00:22:21 +02:00
int zooms;
2017-04-14 22:39:33 +02:00
2017-08-11 00:22:21 +02:00
if (!_file.seek(_file.size() - sizeof(tableOffset)))
2017-04-14 22:39:33 +02:00
return false;
2017-08-11 00:22:21 +02:00
if (!readValue(tableOffset))
2017-04-14 22:39:33 +02:00
return false;
2017-08-31 15:32:44 +02:00
zooms = (int)((_file.size() - tableOffset - sizeof(quint32))
/ sizeof(quint32));
2017-04-14 22:39:33 +02:00
2017-08-11 00:22:21 +02:00
for (int i = 0; i < zooms - 2; i++) {
if (!_file.seek(tableOffset + i * sizeof(quint32)))
return false;
if (!readValue(headerOffset))
return false;
if (!_file.seek(headerOffset))
return false;
2017-04-14 22:39:33 +02:00
2017-08-11 00:22:21 +02:00
if (!readValue(w))
return false;
if (!readValue(h))
return false;
if (!readValue(x))
return false;
if (!readValue(y))
return false;
2017-04-14 22:39:33 +02:00
2017-08-11 00:22:21 +02:00
Zoom zoom;
zoom.size = QSize(w, h);
zoom.dim = QSize(x, y);
2017-04-14 22:39:33 +02:00
2017-08-11 00:22:21 +02:00
zoom.palette = QVector<quint32>(256);
if (!read(&(zoom.palette[0]), sizeof(quint32) * 256))
return false;
2019-03-07 22:58:43 +01:00
for (int i = 0; i < zoom.palette.size(); i++)
zoom.palette[i] = Color::bgr2rgb(qFromLittleEndian(
zoom.palette.at(i)));
2017-08-11 00:22:21 +02:00
zoom.tiles = QVector<quint32>(zoom.dim.width() * zoom.dim.height() + 1);
for (int i = 0; i < zoom.tiles.size(); i++)
if (!readValue(zoom.tiles[i]))
return false;
_zooms.append(zoom);
}
2017-04-14 22:39:33 +02:00
2017-08-22 23:14:32 +02:00
return _zooms.isEmpty() ? false : true;
2017-04-14 22:39:33 +02:00
}
2018-03-08 02:24:10 +01:00
bool OZF::open()
2017-04-14 22:39:33 +02:00
{
if (!_file.open(QIODevice::ReadOnly))
return false;
if (!readHeaders()) {
2017-08-22 15:57:37 +02:00
qWarning("%s: Invalid header", qPrintable(_file.fileName()));
2017-04-14 22:39:33 +02:00
_file.close();
return false;
}
if (!readTileTable()) {
2017-08-22 15:57:37 +02:00
qWarning("%s: Invalid tile table", qPrintable(_file.fileName()));
2017-04-14 22:39:33 +02:00
_file.close();
return false;
}
return true;
}
2017-08-11 00:22:21 +02:00
QPixmap OZF::tile(int zoom, int x, int y)
2017-04-14 22:39:33 +02:00
{
Q_ASSERT(_file.isOpen());
2017-08-11 00:22:21 +02:00
Q_ASSERT(0 <= zoom && zoom < _zooms.count());
2017-04-14 22:39:33 +02:00
2017-08-11 00:22:21 +02:00
const Zoom &z = _zooms.at(zoom);
int i = (y/tileSize().height()) * z.dim.width() + (x/tileSize().width());
if (i >= z.tiles.size() - 1 || i < 0)
2017-04-15 08:59:31 +02:00
return QPixmap();
2017-04-14 22:39:33 +02:00
2017-08-11 00:22:21 +02:00
int size = z.tiles.at(i+1) - z.tiles.at(i);
if (!_file.seek(z.tiles.at(i)))
2017-04-15 08:59:31 +02:00
return QPixmap();
2017-04-14 22:39:33 +02:00
2017-08-09 11:32:05 +02:00
quint32 bes = qToBigEndian(tileSize().width() * tileSize().height());
QByteArray ba;
ba.resize(sizeof(bes) + size);
2018-03-26 01:02:31 +02:00
memcpy(ba.data(), &bes, sizeof(bes));
2017-08-09 11:32:05 +02:00
2017-08-11 00:22:21 +02:00
if (!read(ba.data() + sizeof(bes), size, 16))
2017-04-15 08:59:31 +02:00
return QPixmap();
2017-04-14 22:39:33 +02:00
QByteArray uba = qUncompress(ba);
if (uba.size() != tileSize().width() * tileSize().height())
2017-04-15 08:59:31 +02:00
return QPixmap();
2017-04-14 22:39:33 +02:00
QImage img((const uchar*)uba.constData(), tileSize().width(),
tileSize().height(), QImage::Format_Indexed8);
2017-08-11 00:22:21 +02:00
img.setColorTable(z.palette);
2017-04-14 22:39:33 +02:00
return QPixmap::fromImage(img.mirrored());
}
2017-08-11 00:22:21 +02:00
QSize OZF::size(int zoom) const
{
Q_ASSERT(_file.isOpen());
Q_ASSERT(0 <= zoom && zoom < _zooms.count());
return _zooms.at(zoom).size;
}
2018-03-08 02:24:10 +01:00
QPointF OZF::scale(int zoom) const
{
return QPointF((qreal)size(zoom).width() / (qreal)size(0).width(),
(qreal)size(zoom).height() / (qreal)size(0).height());
}
bool OZF::isOZF(const QString &path)
{
QFile file(path);
quint16 magic;
if (!file.open(QIODevice::ReadOnly))
return false;
if (file.read((char*)&magic, sizeof(magic)) < (qint64)sizeof(magic))
return false;
2017-07-31 00:46:10 +02:00
magic = qFromLittleEndian(magic);
if (magic == OZF2_MAGIC || magic == OZF3_MAGIC)
return true;
return false;
}