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

340 lines
7.0 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;
};
bool ISO8211::Field::subfield(const char *name, int *val, int idx) const
{
const QVariant *v;
bool ok;
v = data().field(name, idx);
if (!v)
return false;
*val = v->toInt(&ok);
return ok;
}
bool ISO8211::Field::subfield(const char *name, uint *val, int idx) const
{
const QVariant *v;
bool ok;
v = data().field(name, idx);
if (!v)
return false;
*val = v->toUInt(&ok);
return ok;
}
bool ISO8211::Field::subfield(const char *name, QByteArray *val, int idx) const
{
const QVariant *v;
v = data().field(name, idx);
if (!v)
return false;
*val = v->toByteArray();
return true;
}
bool ISO8211::fieldType(const QString &str, int cnt, FieldType &type, int &size)
{
if (str == "A" || str == "I" || str == "R") {
type = String;
size = cnt;
} else if (str == "B") {
type = Array;
size = cnt / 8;
} else if (str == "b11") {
type = U8;
size = 1;
} else if (str == "b12") {
type = U16;
size = 2;
} else if (str == "b14") {
type = U32;
size = 4;
} else if (str == "b21") {
type = S8;
size = 1;
} else if (str == "b22") {
type = S16;
size = 2;
} else if (str == "b24") {
type = S32;
size = 4;
} else
return false;
return true;
}
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;
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() == '*') {
fields.setRepeat(true);
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;
fields.resize(tags.size());
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++) {
SubFieldDefinition &f = fields[tag];
f.tag = tags.at(tag);
if (!fieldType(typeStr, size, f.type, f.size))
return false;
tag++;
}
}
}
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-01-26 01:13:15 +01:00
bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
const SubFields &fields, 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;
2023-01-19 10:08:17 +01:00
data.clear();
2022-11-04 09:03:36 +01:00
data.setFields(&fields);
do {
QVector<QVariant> row;
row.resize(fields.size());
for (int i = 0; i < fields.size(); i++) {
const SubFieldDefinition &f = fields.at(i);
switch (f.type) {
case String:
case Array:
if (f.size) {
row[i] = QVariant(QByteArray(dp, f.size));
dp += f.size;
} 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;
}
}
data.append(row);
} while (fields.repeat() && dp < ep);
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();
2023-01-26 01:13:15 +01:00
int len = readDR(fields);
2022-11-04 09:03:36 +01:00
if (len < 0) {
_errorString = "Error reading DR";
return false;
}
record.resize(fields.size());
for (int i = 0; i < fields.size(); i++) {
const FieldDefinition &def = fields.at(i);
Field &f = record[i];
FieldsMap::const_iterator it = _map.find(def.tag);
if (it == _map.constEnd()) {
_errorString = QString("%1: unknown record")
.arg(QString(def.tag));
return false;
}
f.setTag(def.tag);
2023-01-26 01:13:15 +01:00
if (!readUDA(pos, def, it.value(), f.rdata())) {
2022-11-04 09:03:36 +01:00
_errorString = QString("Error reading %1 record")
.arg(QString(def.tag));
return false;
}
}
return true;
}