Handle invalid triplets
"Invalid triplets" are triplets that have failed Hamming 24/18 decoding. Previously, invalid triplets were converted at load time to either all zero bits or, in the case of X/26 enhancement triplets, to "dummy" triplets of reserved mode 11110 with a row address group that hopefully have no effect. Now the X/26 enhancement triplet list can explicitly store invalid triplets and will show them as "error decoding triplet". Invalid triplets in other packets such as X/28/0 will still be zeroed out at load time. Since the TTI format has no provision for storing invalid triplets, saving a page will convert the invalid triplets to reserved mode 11110 as described above. The actual bits of invalid triplets are not stored on the assumption that they are not recoverable. Thus exporting to t42 format will write an invalid triplet as a Hamming coded result of all zero bits which will still cause a Hamming decoding failure.
This commit is contained in:
@@ -36,9 +36,13 @@ QByteArray PageX26Base::packetFromEnhancementList(int p) const
|
||||
const int enhanceListPointer = p*13+t;
|
||||
|
||||
if (enhanceListPointer < m_enhancements.size()) {
|
||||
if (!m_enhancements.at(enhanceListPointer).isValid())
|
||||
result[t*3+1] = result[t*3+2] = result[t*3+3] = 0xff;
|
||||
else {
|
||||
result[t*3+1] = m_enhancements.at(enhanceListPointer).address();
|
||||
result[t*3+2] = m_enhancements.at(enhanceListPointer).mode() | ((m_enhancements.at(enhanceListPointer).data() & 1) << 5);
|
||||
result[t*3+3] = m_enhancements.at(enhanceListPointer).data() >> 1;
|
||||
}
|
||||
|
||||
// If this is the last triplet, get a copy to repeat to the end of the packet
|
||||
if (enhanceListPointer == m_enhancements.size()-1) {
|
||||
@@ -64,19 +68,24 @@ QByteArray PageX26Base::packetFromEnhancementList(int p) const
|
||||
void PageX26Base::setEnhancementListFromPacket(int p, QByteArray pkt)
|
||||
{
|
||||
// Preallocate entries in the m_enhancements list to hold our incoming triplets.
|
||||
// We write "dummy" reserved 11110 Row Triplets in the allocated entries which then get overwritten by the packet contents.
|
||||
// We write invalid triplets in the allocated entries which then get overwritten by the packet contents.
|
||||
// This is in case of missing packets so we can keep Local Object pointers valid.
|
||||
while (m_enhancements.size() < (p+1)*13)
|
||||
m_enhancements.append( X26Triplet{ 41, 0x1e, 0 } );
|
||||
m_enhancements.append( X26Triplet{ 0xff, 0xff, 0xff } );
|
||||
|
||||
X26Triplet newX26Triplet;
|
||||
|
||||
for (int t=0; t<13; t++) {
|
||||
const int enhanceListPointer = p*13+t;
|
||||
|
||||
// Need the "& 0xff" since QByteArray.at() returns (signed) chars
|
||||
if ((pkt.at(t*3+2) & 0xff) == 0xff)
|
||||
newX26Triplet.setInvalid();
|
||||
else {
|
||||
newX26Triplet.setAddress(pkt.at(t*3+1) & 0x3f);
|
||||
newX26Triplet.setMode(pkt.at(t*3+2) & 0x1f);
|
||||
newX26Triplet.setData(((pkt.at(t*3+3) & 0x3f) << 1) | ((pkt.at(t*3+2) & 0x20) >> 5));
|
||||
}
|
||||
m_enhancements.replace(enhanceListPointer, newX26Triplet);
|
||||
}
|
||||
if (newX26Triplet.mode() == 0x1f && newX26Triplet.address() == 0x3f && newX26Triplet.data() & 0x01)
|
||||
|
||||
@@ -26,6 +26,11 @@ X26Triplet::X26Triplet(int address, int mode, int data)
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
bool X26Triplet::isValid() const
|
||||
{
|
||||
return m_mode != 0xff;
|
||||
}
|
||||
|
||||
int X26Triplet::address() const
|
||||
{
|
||||
return m_address;
|
||||
@@ -61,6 +66,11 @@ bool X26Triplet::isRowTriplet() const
|
||||
return (m_address >= 40);
|
||||
}
|
||||
|
||||
void X26Triplet::setInvalid()
|
||||
{
|
||||
m_address = m_mode = m_data = 0xff;
|
||||
}
|
||||
|
||||
void X26Triplet::setAddress(int address)
|
||||
{
|
||||
m_address = address;
|
||||
@@ -180,7 +190,9 @@ void X26TripletList::updateInternalData()
|
||||
triplet->m_reservedMode = false;
|
||||
triplet->m_reservedData = false;
|
||||
|
||||
if (triplet->isRowTriplet()) {
|
||||
if (!triplet->isValid())
|
||||
triplet->m_error = X26Triplet::ErrorDecodingTriplet;
|
||||
else if (triplet->isRowTriplet()) {
|
||||
switch (triplet->modeExt()) {
|
||||
case 0x00: // Full screen colour
|
||||
if (activePosition.isDeployed())
|
||||
|
||||
@@ -26,7 +26,7 @@ class X26Triplet
|
||||
{
|
||||
public:
|
||||
// x26model.h has the Plain English descriptions of these errors
|
||||
enum X26TripletError { NoError, ActivePositionMovedUp, ActivePositionMovedLeft, InvokePointerInvalid, InvokeTypeMismatch, OriginModifierAlone };
|
||||
enum X26TripletError { NoError, ErrorDecodingTriplet, ActivePositionMovedUp, ActivePositionMovedLeft, InvokePointerInvalid, InvokeTypeMismatch, OriginModifierAlone };
|
||||
enum ObjectSource { InvalidObjectSource, LocalObject, POPObject, GPOPObject };
|
||||
|
||||
X26Triplet() {}
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
|
||||
X26Triplet(int address, int mode, int data);
|
||||
|
||||
bool isValid() const;
|
||||
int address() const;
|
||||
int mode() const;
|
||||
int modeExt() const;
|
||||
@@ -44,6 +45,7 @@ public:
|
||||
int addressColumn() const;
|
||||
bool isRowTriplet() const;
|
||||
|
||||
void setInvalid();
|
||||
void setAddress(int address);
|
||||
void setMode(int mode);
|
||||
void setData(int data);
|
||||
|
||||
@@ -227,6 +227,9 @@ bool LoadT42Format::load(QFile *inFile, QList<PageBase>& subPages, QVariantHash
|
||||
int foundPageNumber = -1;
|
||||
bool firstPacket0Found = false;
|
||||
bool pageBodyPacketsFound = false;
|
||||
bool errorEnhancements = false;
|
||||
bool errorLinks = false;
|
||||
bool errorPresentation = false;
|
||||
|
||||
m_inFile = inFile;
|
||||
|
||||
@@ -374,6 +377,7 @@ bool LoadT42Format::load(QFile *inFile, QList<PageBase>& subPages, QVariantHash
|
||||
// Error found in at least one byte of the link
|
||||
// Neutralise the whole link to same magazine, page FF, subcode 3F7F
|
||||
qDebug("X/27/%d link %d decoding error", readDesignationCode, i);
|
||||
errorLinks = true;
|
||||
m_inLine[b] = 0xf;
|
||||
m_inLine[b+1] = 0xf;
|
||||
m_inLine[b+2] = 0xf;
|
||||
@@ -416,15 +420,17 @@ bool LoadT42Format::load(QFile *inFile, QList<PageBase>& subPages, QVariantHash
|
||||
// Error decoding Hamming 24/18
|
||||
qDebug("X/%d/%d triplet %d decoding error", readPacketNumber, readDesignationCode, i);
|
||||
if (readPacketNumber == 26) {
|
||||
// Enhancements packet, set to "dummy" Address 41, Mode 0x1e, Data 0
|
||||
m_inLine[b] = 41;
|
||||
m_inLine[b+1] = 0x1e;
|
||||
m_inLine[b+2] = 0;
|
||||
// Enhancements packet, set to invalid triplet
|
||||
m_inLine[b] = 0xff;
|
||||
m_inLine[b+1] = 0xff;
|
||||
m_inLine[b+2] = 0xff;
|
||||
errorEnhancements = true;
|
||||
} else {
|
||||
// Zero out whole decoded triplet, bound to make things go wrong...
|
||||
m_inLine[b] = 0x00;
|
||||
m_inLine[b+1] = 0x00;
|
||||
m_inLine[b+2] = 0x00;
|
||||
errorPresentation = true;
|
||||
}
|
||||
} else {
|
||||
m_inLine[b] = d & 0x0003f;
|
||||
@@ -441,7 +447,14 @@ bool LoadT42Format::load(QFile *inFile, QList<PageBase>& subPages, QVariantHash
|
||||
} else if (!pageBodyPacketsFound) {
|
||||
m_error = "X/0 found, but no page body packets were found.";
|
||||
return false;
|
||||
} else
|
||||
}
|
||||
|
||||
if (errorEnhancements)
|
||||
m_warnings.append("Error decoding triplet(s) in enhancement data.");
|
||||
if (errorLinks)
|
||||
m_warnings.append("Error decoding FLOF links.");
|
||||
if (errorPresentation)
|
||||
m_warnings.append("Error decoding triplet(s) in presentation data.");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,8 +160,23 @@ QByteArray SaveTTIFormat::format18BitPacket(QByteArray packet)
|
||||
// TTI stores the triplets 6 bits at a time like we do, without Hamming encoding
|
||||
// We don't touch the first byte; the caller replaces it with the designation code
|
||||
// unless it's X/1-X/25 used in (G)POP pages
|
||||
for (int i=1; i<packet.size(); i++)
|
||||
for (int i=1; i<packet.size(); i++) {
|
||||
// Save invalid triplets as address 41, mode 0x1e, data 0
|
||||
// which hopefully won't do anything when parsed as X/26 enhancements
|
||||
if ((packet.at(i) & 0xff) == 0xff)
|
||||
switch (i % 3) {
|
||||
case 1:
|
||||
packet[i] = 41;
|
||||
break;
|
||||
case 2:
|
||||
packet[i] = 0x1e;
|
||||
break;
|
||||
case 0:
|
||||
packet[i] = 0;
|
||||
break;
|
||||
}
|
||||
packet[i] = packet.at(i) | 0x40;
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
@@ -322,7 +337,11 @@ QByteArray SaveT42Format::format4BitPacket(QByteArray packet)
|
||||
|
||||
QByteArray SaveT42Format::format18BitPacket(QByteArray packet)
|
||||
{
|
||||
for (int c=1; c<packet.size(); c+=3) {
|
||||
for (int c=1; c<packet.size(); c+=3)
|
||||
// For invalid packets, save as all zeroes which will fail Hamming 24/18 decoding
|
||||
if ((packet.at(c) & 0xff) == 0xff)
|
||||
packet[c] = packet[c+1] = packet[c+2] = 0;
|
||||
else {
|
||||
unsigned int D5_D11;
|
||||
unsigned int D12_D18;
|
||||
unsigned int P5, P6;
|
||||
@@ -440,7 +459,14 @@ bool SaveEP1Format::getWarnings(const PageBase &subPage)
|
||||
|
||||
QByteArray SaveEP1Format::format18BitPacket(QByteArray packet)
|
||||
{
|
||||
for (int c=1; c<packet.size(); c+=3) {
|
||||
for (int c=1; c<packet.size(); c+=3)
|
||||
if ((packet.at(c+1) & 0xff) == 0xff) {
|
||||
// Save invalid triplets as address 41, mode 0x1e, data 0
|
||||
// which hopefully won't do anything when parsed as X/26 enhancements
|
||||
packet[c] = 41;
|
||||
packet[c+1] = 0x1e;
|
||||
packet[c+2] = 0;
|
||||
} else {
|
||||
// Shuffle triplet bits to 6 bit address, 5 bit mode, 7 bit data
|
||||
packet[c+2] = ((packet.at(c+2) & 0x3f) << 1) | ((packet.at(c+1) & 0x20) >> 5);
|
||||
packet[c+1] = packet.at(c+1) & 0x1f;
|
||||
|
||||
@@ -678,6 +678,13 @@ void X26DockWidget::updateAllCookedTripletWidgets(const QModelIndex &index)
|
||||
{
|
||||
const int modeExt = index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole).toInt();
|
||||
|
||||
if (modeExt == 0xff) {
|
||||
disableTripletWidgets();
|
||||
m_cookedModePushButton->setEnabled(true);
|
||||
m_cookedModePushButton->setText("Replace...");
|
||||
return;
|
||||
}
|
||||
|
||||
m_cookedModePushButton->setEnabled(true);
|
||||
m_cookedModePushButton->setText(m_modeTripletNames.modeName(modeExt));
|
||||
|
||||
@@ -1034,7 +1041,7 @@ void X26DockWidget::insertTriplet(int modeExt, int row)
|
||||
if (modeExt >= 0x20 && modeExt != 0x24 && modeExt != 0x25 && modeExt != 0x26 && modeExt != 0x2a) {
|
||||
const int existingTripletModeExt = index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole).toInt();
|
||||
|
||||
if (existingTripletModeExt >= 0x20 && existingTripletModeExt != 0x24 && existingTripletModeExt != 0x25 && existingTripletModeExt != 0x26 && existingTripletModeExt != 0x2a)
|
||||
if (existingTripletModeExt >= 0x20 && existingTripletModeExt <= 0x3f && existingTripletModeExt != 0x24 && existingTripletModeExt != 0x25 && existingTripletModeExt != 0x26 && existingTripletModeExt != 0x2a)
|
||||
newTriplet.setAddress(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole).toInt());
|
||||
}
|
||||
// If we're inserting a Set Active Position or Full Row Colour triplet,
|
||||
|
||||
@@ -108,7 +108,7 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
|
||||
else
|
||||
return QVariant();
|
||||
case 1:
|
||||
if (!triplet.isRowTriplet())
|
||||
if (triplet.isValid() && !triplet.isRowTriplet())
|
||||
return triplet.addressColumn();
|
||||
// For Set Active Position and Origin Modifier, data is the column
|
||||
else if (triplet.modeExt() == 0x04)
|
||||
@@ -122,9 +122,14 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
|
||||
QString result;
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (index.column() == 2)
|
||||
if (index.column() == 2) {
|
||||
if (!triplet.isValid())
|
||||
return "Error decoding triplet";
|
||||
return (m_modeTripletNames.modeName(triplet.modeExt()));
|
||||
}
|
||||
// Column 3 - describe effects of data/address triplet parameters in plain English
|
||||
if (!triplet.isValid())
|
||||
return QVariant();
|
||||
switch (triplet.modeExt()) {
|
||||
case 0x01: // Full row colour
|
||||
case 0x07: // Address row 0
|
||||
@@ -609,12 +614,15 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role
|
||||
return true;
|
||||
|
||||
case 2: // Cooked triplet mode
|
||||
if (intValue < 0x20 && !triplet.isRowTriplet()) {
|
||||
if (!triplet.isValid()) {
|
||||
// Changing from invalid triplet
|
||||
m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, intValue < 0x20 ? 41 : 0, role));
|
||||
m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, 32, role));
|
||||
} else if (intValue < 0x20 && !triplet.isRowTriplet()) {
|
||||
// Changing mode from column triplet to row triplet
|
||||
m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, 41, role));
|
||||
m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, 0, role));
|
||||
}
|
||||
if (intValue >= 0x20 && triplet.isRowTriplet()) {
|
||||
} else if (intValue >= 0x20 && triplet.isRowTriplet()) {
|
||||
// Changing mode from row triplet to column triplet
|
||||
m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, 0, role));
|
||||
m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, 0, role));
|
||||
|
||||
@@ -60,8 +60,9 @@ private:
|
||||
};
|
||||
|
||||
// Needs to be in the same order as enum X26TripletError in x26triplets.h
|
||||
const tripletErrorShow m_tripletErrors[6] {
|
||||
const tripletErrorShow m_tripletErrors[7] {
|
||||
{ "", 0 }, // No error
|
||||
{ "Error decoding triplet", 2 },
|
||||
{ "Active Position can't move up", 0 },
|
||||
{ "Active Position can't move left within row", 1 },
|
||||
{ "Invocation not pointing to Object Definition", 3 },
|
||||
|
||||
Reference in New Issue
Block a user