1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-02-15 23:30:49 +01:00
GPXSee/src/map/ENC/iso8211.cpp

353 lines
7.6 KiB
C++
Raw Normal View History

2022-11-04 09:03:36 +01:00
#include <QFile>
#include <QRegularExpression>
#include "common/util.h"
#include "iso8211.h"
using namespace ENC;
2023-01-26 01:13:15 +01:00
#define UINT16(x) \
(((quint16)*(const uchar*)(x)) \
| ((quint16)(*((const uchar*)(x) + 1)) << 8))
#define INT32(x) ((qint32)UINT32(x))
#define INT16(x) ((qint16)UINT16(x))
2022-11-04 09:03:36 +01:00
struct DR {
char RecordLength[5];
char InterchangeLevel;
char LeaderID;
char ExtensionFlag;
char Version;
char ApplicationIndicator;
char FieldControlLength[2];
char BaseAddress[5];
char ExtCharSetIndicator[3];
char FieldLengthSize;
char FieldPosSize;
char Reserved;
char FieldTagSize;
};
2023-09-08 19:26:46 +02:00
const QVariant *ISO8211::Field::data(const QByteArray &name, int idx) const
{
const QVector<QVariant> &v = _data.at(idx);
for (int i = 0; i < _subFields.size(); i++)
if (_subFields.at(i) == name)
return &v.at(i);
return 0;
}
2022-11-04 09:03:36 +01:00
bool ISO8211::Field::subfield(const char *name, int *val, int idx) const
{
bool ok;
2023-09-08 19:26:46 +02:00
const QVariant *v = data(name, idx);
2022-11-04 09:03:36 +01:00
if (!v)
return false;
*val = v->toInt(&ok);
return ok;
}
bool ISO8211::Field::subfield(const char *name, uint *val, int idx) const
{
bool ok;
2023-09-08 19:26:46 +02:00
const QVariant *v = data(name, idx);
2022-11-04 09:03:36 +01:00
if (!v)
return false;
*val = v->toUInt(&ok);
return ok;
}
bool ISO8211::Field::subfield(const char *name, QByteArray *val, int idx) const
{
2023-09-08 19:26:46 +02:00
const QVariant *v = data(name, idx);
2022-11-04 09:03:36 +01:00
if (!v)
return false;
*val = v->toByteArray();
return true;
}
2023-09-08 19:26:46 +02:00
ISO8211::SubFieldDefinition ISO8211::fieldType(const QString &str, int cnt)
2022-11-04 09:03:36 +01:00
{
if (str == "A" || str == "I" || str == "R")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(String, cnt);
else if (str == "B")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(Array, cnt / 8);
else if (str == "b11")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(U8, 1);
else if (str == "b12")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(U16, 2);
else if (str == "b14")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(U32, 4);
else if (str == "b21")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(S8, 1);
else if (str == "b22")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(S16, 2);
else if (str == "b24")
2023-09-08 19:26:46 +02:00
return SubFieldDefinition(S32, 4);
else
return SubFieldDefinition();
2022-11-04 09:03:36 +01:00
}
2023-01-26 01:13:15 +01:00
int ISO8211::readDR(QVector<FieldDefinition> &fields)
2022-11-04 09:03:36 +01:00
{
DR ddr;
QByteArray fieldLen, fieldPos;
int len, lenSize, posSize, tagSize, offset;
2022-12-23 08:35:05 +01:00
static_assert(sizeof(ddr) == 24, "Invalid DR alignment");
2023-01-26 01:13:15 +01:00
if (_file.read((char*)&ddr, sizeof(ddr)) != sizeof(ddr))
2022-11-04 09:03:36 +01:00
return -1;
len = Util::str2int(ddr.RecordLength, sizeof(ddr.RecordLength));
offset = Util::str2int(ddr.BaseAddress, sizeof(ddr.BaseAddress));
lenSize = Util::str2int(&ddr.FieldLengthSize, sizeof(ddr.FieldLengthSize));
posSize = Util::str2int(&ddr.FieldPosSize, sizeof(ddr.FieldPosSize));
tagSize = Util::str2int(&ddr.FieldTagSize, sizeof(ddr.FieldTagSize));
if (len < 0 || offset < 0 || lenSize < 0 || posSize < 0 || tagSize < 0)
return -1;
fields.resize((offset - 1 - sizeof(DR)) / (lenSize + posSize + tagSize));
fieldLen.resize(lenSize);
fieldPos.resize(posSize);
for (int i = 0; i < fields.size(); i++) {
FieldDefinition &r = fields[i];
r.tag.resize(tagSize);
2023-01-26 01:13:15 +01:00
if (_file.read(r.tag.data(), tagSize) != tagSize
|| _file.read(fieldLen.data(), lenSize) != lenSize
|| _file.read(fieldPos.data(), posSize) != posSize)
2022-11-04 09:03:36 +01:00
return -1;
r.pos = offset + Util::str2int(fieldPos.constData(), posSize);
r.size = Util::str2int(fieldLen.constData(), lenSize);
if (r.pos < 0 || r.size < 0)
return -1;
}
return len;
}
2023-01-26 01:13:15 +01:00
bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
2022-11-04 09:03:36 +01:00
{
static QRegularExpression re("(\\d*)(\\w+)\\(*(\\d*)\\)*");
QByteArray ba;
bool repeat = false;
QVector<SubFieldDefinition> defs;
2023-09-08 19:26:46 +02:00
QVector<QByteArray> defTags;
2022-11-04 09:03:36 +01:00
ba.resize(def.size);
2023-01-26 01:13:15 +01:00
if (!(_file.seek(def.pos) && _file.read(ba.data(), ba.size()) == ba.size()))
2022-11-04 09:03:36 +01:00
return false;
QList<QByteArray> list(ba.split('\x1f'));
if (!list.at(1).isEmpty() && list.at(1).front() == '*') {
repeat = true;
2022-11-04 09:03:36 +01:00
list[1].remove(0, 1);
}
QList<QByteArray> tags(list.at(1).split('!'));
if (list.size() > 2) {
QRegularExpressionMatchIterator it = re.globalMatch(list.at(2));
int tag = 0;
defs.resize(tags.size());
2023-09-08 19:26:46 +02:00
defTags.resize(tags.size());
2022-11-04 09:03:36 +01:00
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
QString cntStr(match.captured(1));
QString typeStr(match.captured(2));
QString sizeStr(match.captured(3));
uint cnt = 1, size = 0;
bool ok;
if (!cntStr.isEmpty()) {
cnt = cntStr.toUInt(&ok);
if (!ok)
return false;
}
if (!sizeStr.isEmpty()) {
size = sizeStr.toUInt(&ok);
if (!ok)
return false;
}
for (uint i = 0; i < cnt; i++) {
2023-09-08 19:26:46 +02:00
SubFieldDefinition sfd(fieldType(typeStr, size));
if (sfd.type() == Unknown)
2022-11-04 09:03:36 +01:00
return false;
defs[tag] = sfd;
2023-09-08 19:26:46 +02:00
defTags[tag] = tags.at(tag);
2022-11-04 09:03:36 +01:00
tag++;
}
}
}
2023-09-08 19:26:46 +02:00
fields = SubFields(defTags, defs, repeat);
2022-11-04 09:03:36 +01:00
return true;
}
2023-01-19 10:08:17 +01:00
bool ISO8211::readDDR()
2022-11-04 09:03:36 +01:00
{
QVector<FieldDefinition> fields;
2023-01-19 10:08:17 +01:00
if (!_file.open(QIODevice::ReadOnly)) {
_errorString = _file.errorString();
return false;
}
2023-01-26 01:13:15 +01:00
int len = readDR(fields);
2022-11-04 09:03:36 +01:00
if (len < 0) {
_errorString = "Not a ISO8211 file";
2022-11-04 09:03:36 +01:00
return false;
}
for (int i = 0; i < fields.size(); i++) {
SubFields def;
2023-01-26 01:13:15 +01:00
if (!readDDA(fields.at(i), def)) {
2022-11-04 09:03:36 +01:00
_errorString = QString("Error reading %1 DDA field")
.arg(QString(fields.at(i).tag));
return false;
}
_map.insert(fields.at(i).tag, def);
}
2023-01-19 10:08:17 +01:00
if (_file.pos() != len || fields.size() < 2) {
2022-11-04 09:03:36 +01:00
_errorString = "DDR format error";
return false;
}
return true;
}
2023-09-08 19:26:46 +02:00
bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
const QVector<SubFieldDefinition> &fields, bool repeat, Data &data)
2022-11-04 09:03:36 +01:00
{
QByteArray ba;
ba.resize(def.size);
2023-01-26 01:13:15 +01:00
if (!(_file.seek(pos + def.pos)
&& _file.read(ba.data(), ba.size()) == ba.size()))
2022-11-04 09:03:36 +01:00
return false;
const char *sp;
const char *dp = ba.constData();
const char *ep = ba.constData() + ba.size() - 1;
do {
QVector<QVariant> row;
2023-09-08 19:26:46 +02:00
row.resize(fields.size());
2022-11-04 09:03:36 +01:00
2023-09-08 19:26:46 +02:00
for (int i = 0; i < fields.size(); i++) {
const SubFieldDefinition &f = fields.at(i);
2022-11-04 09:03:36 +01:00
switch (f.type()) {
2022-11-04 09:03:36 +01:00
case String:
case Array:
if (f.size()) {
row[i] = QVariant(QByteArray(dp, f.size()));
dp += f.size();
2022-11-04 09:03:36 +01:00
} else {
sp = dp;
while (dp < ep && *dp != '\x1f')
dp++;
row[i] = QVariant(QByteArray(sp, dp - sp));
dp++;
}
break;
case S8:
row[i] = QVariant(*((qint8*)dp));
dp++;
break;
case S16:
row[i] = QVariant(INT16(dp));
dp += 2;
break;
case S32:
row[i] = QVariant(INT32(dp));
dp += 4;
break;
case U8:
row[i] = QVariant(*((quint8*)dp));
dp++;
break;
case U16:
row[i] = QVariant(UINT16(dp));
dp += 2;
break;
case U32:
row[i] = QVariant(UINT32(dp));
dp += 4;
break;
default:
return false;
2022-11-04 09:03:36 +01:00
}
}
data.append(row);
2023-09-08 19:26:46 +02:00
} while (repeat && dp < ep);
2022-11-04 09:03:36 +01:00
return true;
}
2023-01-19 10:08:17 +01:00
bool ISO8211::readRecord(Record &record)
2022-11-04 09:03:36 +01:00
{
2023-01-19 10:08:17 +01:00
if (_file.atEnd())
return false;
2022-11-04 09:03:36 +01:00
QVector<FieldDefinition> fields;
2023-01-19 10:08:17 +01:00
qint64 pos = _file.pos();
2022-11-04 09:03:36 +01:00
2023-09-08 19:26:46 +02:00
if (readDR(fields) < 0) {
2022-11-04 09:03:36 +01:00
_errorString = "Error reading DR";
return false;
}
record.resize(fields.size());
for (int i = 0; i < fields.size(); i++) {
const FieldDefinition &def = fields.at(i);
2023-09-08 19:26:46 +02:00
Data data;
2022-11-04 09:03:36 +01:00
2023-12-26 14:14:08 +01:00
FieldsMap::const_iterator it(_map.find(def.tag));
2022-11-04 09:03:36 +01:00
if (it == _map.constEnd()) {
_errorString = QString("%1: unknown record").arg(QString(def.tag));
2022-11-04 09:03:36 +01:00
return false;
}
2023-09-08 19:26:46 +02:00
if (!readUDA(pos, def, it->defs(), it->repeat(), data)) {
2022-11-04 09:03:36 +01:00
_errorString = QString("Error reading %1 record")
.arg(QString(def.tag));
return false;
}
2023-09-08 19:26:46 +02:00
record[i] = Field(def.tag, it->tags(), data);
2022-11-04 09:03:36 +01:00
}
return true;
}
const ISO8211::Field *ISO8211::field(const Record &record, const QByteArray &name)
{
for (int i = 0; i < record.size(); i++)
if (record.at(i).tag() == name)
return &record.at(i);
return 0;
}