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
|
|
|
{
|
2023-09-07 09:31:23 +02:00
|
|
|
if (str == "A" || str == "I" || str == "R")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(String, cnt);
|
2023-09-07 09:31:23 +02:00
|
|
|
else if (str == "B")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(Array, cnt / 8);
|
2023-09-07 09:31:23 +02:00
|
|
|
else if (str == "b11")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(U8, 1);
|
2023-09-07 09:31:23 +02:00
|
|
|
else if (str == "b12")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(U16, 2);
|
2023-09-07 09:31:23 +02:00
|
|
|
else if (str == "b14")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(U32, 4);
|
2023-09-07 09:31:23 +02:00
|
|
|
else if (str == "b21")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(S8, 1);
|
2023-09-07 09:31:23 +02:00
|
|
|
else if (str == "b22")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(S16, 2);
|
2023-09-07 09:31:23 +02:00
|
|
|
else if (str == "b24")
|
2023-09-08 19:26:46 +02:00
|
|
|
return SubFieldDefinition(S32, 4);
|
2023-09-07 09:31:23 +02:00
|
|
|
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;
|
2023-09-07 09:31:23 +02:00
|
|
|
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() == '*') {
|
2023-09-07 09:31:23 +02:00
|
|
|
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;
|
|
|
|
|
2023-09-07 09:31:23 +02:00
|
|
|
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));
|
2023-09-07 09:31:23 +02:00
|
|
|
if (sfd.type() == Unknown)
|
2022-11-04 09:03:36 +01:00
|
|
|
return false;
|
2023-09-07 09:31:23 +02:00
|
|
|
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);
|
2023-09-07 09:31:23 +02:00
|
|
|
|
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) {
|
2022-11-25 23:20:35 +01:00
|
|
|
_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
|
|
|
|
2023-09-07 09:31:23 +02:00
|
|
|
switch (f.type()) {
|
2022-11-04 09:03:36 +01:00
|
|
|
case String:
|
|
|
|
case Array:
|
2023-09-07 09:31:23 +02:00
|
|
|
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;
|
2023-09-07 09:31:23 +02:00
|
|
|
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()) {
|
2023-09-07 09:31:23 +02:00
|
|
|
_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-07 09:31:23 +02:00
|
|
|
|
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;
|
|
|
|
}
|
2023-09-07 09:31:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|