diff --git a/src/qteletextdecoder/pagex26base.cpp b/src/qteletextdecoder/pagex26base.cpp index fdb7048..c5305d9 100644 --- a/src/qteletextdecoder/pagex26base.cpp +++ b/src/qteletextdecoder/pagex26base.cpp @@ -36,9 +36,13 @@ QByteArray PageX26Base::packetFromEnhancementList(int p) const const int enhanceListPointer = p*13+t; if (enhanceListPointer < m_enhancements.size()) { - 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 (!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; - 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)); + // 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) diff --git a/src/qteletextdecoder/x26triplets.cpp b/src/qteletextdecoder/x26triplets.cpp index cae0628..a1f753b 100644 --- a/src/qteletextdecoder/x26triplets.cpp +++ b/src/qteletextdecoder/x26triplets.cpp @@ -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()) diff --git a/src/qteletextdecoder/x26triplets.h b/src/qteletextdecoder/x26triplets.h index f8b48a4..000f0b8 100644 --- a/src/qteletextdecoder/x26triplets.h +++ b/src/qteletextdecoder/x26triplets.h @@ -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); diff --git a/src/qteletextmaker/loadformats.cpp b/src/qteletextmaker/loadformats.cpp index ed8ee2e..2d98c85 100644 --- a/src/qteletextmaker/loadformats.cpp +++ b/src/qteletextmaker/loadformats.cpp @@ -227,6 +227,9 @@ bool LoadT42Format::load(QFile *inFile, QList& 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& 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& 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,8 +447,15 @@ bool LoadT42Format::load(QFile *inFile, QList& subPages, QVariantHash } else if (!pageBodyPacketsFound) { m_error = "X/0 found, but no page body packets were found."; return false; - } else - return true; + } + + 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; } diff --git a/src/qteletextmaker/saveformats.cpp b/src/qteletextmaker/saveformats.cpp index af08901..faf0afd 100644 --- a/src/qteletextmaker/saveformats.cpp +++ b/src/qteletextmaker/saveformats.cpp @@ -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> 0) & 0xff] ^ hamming_24_18_forward[1][(toEncode >> 8) & 0xff] ^ hamming_24_18_forward_2[(toEncode >> 16) & 0x03]); - packet[c] = Byte_0; + Byte_0 = (hamming_24_18_forward[0][(toEncode >> 0) & 0xff] ^ hamming_24_18_forward[1][(toEncode >> 8) & 0xff] ^ hamming_24_18_forward_2[(toEncode >> 16) & 0x03]); + packet[c] = Byte_0; - D5_D11 = (toEncode >> 4) & 0x7f; - D12_D18 = (toEncode >> 11) & 0x7f; + D5_D11 = (toEncode >> 4) & 0x7f; + D12_D18 = (toEncode >> 11) & 0x7f; - P5 = 0x80 & ~(hamming_24_18_parities[0][D12_D18] << 2); - packet[c+1] = D5_D11 | P5; + P5 = 0x80 & ~(hamming_24_18_parities[0][D12_D18] << 2); + packet[c+1] = D5_D11 | P5; - P6 = 0x80 & ((hamming_24_18_parities[0][Byte_0] ^ hamming_24_18_parities[0][D5_D11]) << 2); - packet[c+2] = D12_D18 | P6; - } + P6 = 0x80 & ((hamming_24_18_parities[0][Byte_0] ^ hamming_24_18_parities[0][D5_D11]) << 2); + packet[c+2] = D12_D18 | P6; + } return packet; } @@ -440,14 +459,21 @@ bool SaveEP1Format::getWarnings(const PageBase &subPage) QByteArray SaveEP1Format::format18BitPacket(QByteArray packet) { - for (int c=1; c> 5); - packet[c+1] = packet.at(c+1) & 0x1f; - // Address of termination marker is 7f instead of 3f - if (packet.at(c+1) == 0x1f && packet.at(c) == 0x3f) - packet[c] = 0x7f; - } + for (int c=1; c> 5); + packet[c+1] = packet.at(c+1) & 0x1f; + // Address of termination marker is 7f instead of 3f + if (packet.at(c+1) == 0x1f && packet.at(c) == 0x3f) + packet[c] = 0x7f; + } return packet; } diff --git a/src/qteletextmaker/x26dockwidget.cpp b/src/qteletextmaker/x26dockwidget.cpp index ea30bf6..d8587fc 100644 --- a/src/qteletextmaker/x26dockwidget.cpp +++ b/src/qteletextmaker/x26dockwidget.cpp @@ -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, diff --git a/src/qteletextmaker/x26model.cpp b/src/qteletextmaker/x26model.cpp index 6b79ff3..8e424ac 100644 --- a/src/qteletextmaker/x26model.cpp +++ b/src/qteletextmaker/x26model.cpp @@ -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)); diff --git a/src/qteletextmaker/x26model.h b/src/qteletextmaker/x26model.h index 8eb87fe..4b62b48 100644 --- a/src/qteletextmaker/x26model.h +++ b/src/qteletextmaker/x26model.h @@ -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 },