From a1e0986e5ca0deb6c9182a3f86896204b1f85f95 Mon Sep 17 00:00:00 2001 From: "G.K.MacGregor" Date: Tue, 15 Sep 2020 22:37:50 +0100 Subject: [PATCH] Port saving pages to packet handling framework Any unhandled packets within pages are now loaded and saved intact albeit with no way to edit them. File handling is now two separate processes: parsing TTI files into packets, and then interpreting the packets into how pages are displayed. This will allow us to import other file formats, and to deal with page functions other than Level 1 Teletext pages. --- levelonepage.cpp | 239 ++++++++++++++++++++++------------------------- levelonepage.h | 2 +- pagebase.cpp | 49 ++++++++-- pagebase.h | 7 +- 4 files changed, 162 insertions(+), 135 deletions(-) diff --git a/levelonepage.cpp b/levelonepage.cpp index b227e90..7dd3327 100644 --- a/levelonepage.cpp +++ b/levelonepage.cpp @@ -61,6 +61,77 @@ void TeletextPage::clearPage() // If clearPage() is called outside constructor, we need to implement localEnhance.clear(); } +QByteArray TeletextPage::packet(int packetNumber, int designationCode) +{ + QByteArray result(40, 0x00); + + if (packetNumber <= 24) { + for (int c=0; c<40; c++) + result[c] = m_level1Page[packetNumber][c]; + return result; + } + + if (packetNumber == 26) { + if (!packetNeeded(26, designationCode)) + return result; // Blank result + + int enhanceListPointer; + X26Triplet lastTriplet; + + for (int i=0; i<13; i++) { + enhanceListPointer = designationCode*13+i; + + if (enhanceListPointer < localEnhance.size()) { + result[i*3+1] = localEnhance.at(enhanceListPointer).address(); + result[i*3+2] = localEnhance.at(enhanceListPointer).mode() | ((localEnhance.at(enhanceListPointer).data() & 1) << 5); + result[i*3+3] = localEnhance.at(enhanceListPointer).data() >> 1; + + // If this is the last triplet, get a copy to repeat to the end of the packet + if (enhanceListPointer == localEnhance.size()-1) { + lastTriplet = localEnhance.at(enhanceListPointer); + // If the last triplet was NOT a Termination Marker, make up one + if (lastTriplet.mode() != 0x1f || lastTriplet.address() != 0x3f) { + lastTriplet.setAddress(0x3f); + lastTriplet.setMode(0x1f); + lastTriplet.setData(0x07); + } + } + } else { + // We've gone past the end of the triplet list, so repeat the Termination Marker to the end + result[i*3+1] = lastTriplet.address(); + result[i*3+2] = lastTriplet.mode() | ((lastTriplet.data() & 1) << 5); + result[i*3+3] = lastTriplet.data() >> 1; + } + } + return result; + } + + // TODO packet 27 + + if (packetNumber == 28 && (designationCode == 0 || designationCode == 4)) { + int CLUToffset = (designationCode == 0) ? 16 : 0; + + result[1] = 0x00; + result[2] = ((m_defaultCharSet & 0x3) << 4) | (m_defaultNOS << 1); + result[3] = ((m_secondCharSet & 0x1) << 5) | (m_secondNOS << 2) | (m_defaultCharSet >> 2); + result[4] = (m_sidePanelStatusL25 << 5) | (m_rightSidePanelDisplayed << 4) | (m_leftSidePanelDisplayed << 3) | (m_secondCharSet >> 1); + result[5] = m_sidePanelColumns | ((m_CLUT[CLUToffset] & 0x300) >> 4); + + for (int c=0; c<16; c++) { + result[c*2+6] = ((m_CLUT[CLUToffset+c] & 0x0f0) >> 2) | ((m_CLUT[CLUToffset+c] & 0xf00) >> 10); + result[c*2+7] = ((m_CLUT[CLUToffset+c+1] & 0x300) >> 4) | (m_CLUT[CLUToffset+c] & 0x00f); + } + + result[37] = ((m_defaultScreenColour & 0x03) << 4) | (m_CLUT[CLUToffset+15] & 0x00f); + result[38] = ((m_defaultRowColour & 0x07) << 3) | (m_defaultScreenColour >> 2); + result[39] = (m_colourTableRemap << 3) | (m_blackBackgroundSubst << 2) | (m_defaultRowColour >> 3); + + return result; + } + + return PageBase::packet(packetNumber, designationCode); +} + bool TeletextPage::setPacket(int packetNumber, QByteArray packetContents) { if (packetNumber <= 24) { @@ -99,6 +170,9 @@ bool TeletextPage::setPacket(int packetNumber, int designationCode, QByteArray p return true; } + + // TODO packet 27 + if (packetNumber == 28 && (designationCode == 0 || designationCode == 4)) { int CLUToffset = (designationCode == 0) ? 16 : 0; @@ -113,7 +187,7 @@ bool TeletextPage::setPacket(int packetNumber, int designationCode, QByteArray p m_sidePanelColumns = packetContents.at(5) & 0xf; for (int c=0; c<16; c++) - m_CLUT[CLUToffset+c] = ((packetContents.at(c*2+5) << 4) & 0x300) | ((packetContents.at(c*2+6) << 10) & 0xc00) | ((packetContents.at(c*2+6) << 2) & 0x0f0) | (packetContents.at(c*2+7) & 0xf); + m_CLUT[CLUToffset+c] = ((packetContents.at(c*2+5) << 4) & 0x300) | ((packetContents.at(c*2+6) << 10) & 0xc00) | ((packetContents.at(c*2+6) << 2) & 0x0f0) | (packetContents.at(c*2+7) & 0x00f); m_defaultScreenColour = (packetContents.at(37) >> 4) | ((packetContents.at(38) << 2) & 0x1c); m_defaultRowColour = ((packetContents.at(38)) >> 3) | ((packetContents.at(39) << 3) & 0x18); @@ -123,7 +197,7 @@ bool TeletextPage::setPacket(int packetNumber, int designationCode, QByteArray p return true; } - qDebug("LevelOnePage unhandled packet X%d/%d", packetNumber, designationCode); + qDebug("LevelOnePage unhandled packet X/%d/%d", packetNumber, designationCode); return PageBase::setPacket(packetNumber, designationCode, packetContents); } @@ -135,7 +209,12 @@ bool TeletextPage::packetNeeded(int packetNumber, int designationCode) const return true; return false; } - // TODO packets 26 and 27 + + if (packetNumber == 26) + return ((localEnhance.size()+12) / 13) > designationCode; + + // TODO packet 27 + if (packetNumber == 28) { if (designationCode == 0) { if (m_leftSidePanelDisplayed || m_rightSidePanelDisplayed || m_defaultScreenColour !=0 || m_defaultRowColour !=0 || m_blackBackgroundSubst || m_colourTableRemap !=0 || m_defaultCharSet != 0 || m_secondCharSet != 0xf) @@ -152,7 +231,7 @@ bool TeletextPage::packetNeeded(int packetNumber, int designationCode) const return false; } } - return true; + return PageBase::packetNeeded(packetNumber, designationCode); } void TeletextPage::loadPagePacket(QByteArray &inLine) @@ -169,8 +248,8 @@ void TeletextPage::loadPagePacket(QByteArray &inLine) inLine.remove(0, secondCommaPosition+1); if (lineNumber <= 25) { for (int c=0; c<40; c++) { - // trimmed() helpfully removes CRLF line endings from the just-read line - // but it also (un)helpfully removes spaces at end of a line, so put them back + // trimmed() helpfully removes CRLF line endings from the just-read line for us + // But it also (un)helpfully removes spaces at the end of a 40 character line, so put them back if (c >= inLine.size()) inLine.append(' '); if (inLine.at(c) & 0x80) @@ -192,78 +271,46 @@ void TeletextPage::loadPagePacket(QByteArray &inLine) } } -// This will be gradually be converted to just getting the raw packets from the Page class and writing out a TTI file. void TeletextPage::savePage(QTextStream *outStream, int pageNumber, int subPageNumber) { -// int pageStatus = 0x8000 | (controlBits[0] << 14) | ((defaultPageNOS & 1) << 9) | ((defaultPageNOS & 2) << 7) | ((defaultPageNOS & 4) << 5); -// for (int i=1; i<8; i++) -// pageStatus |= controlBits[i] << i; + auto writePacketsWithDesignationCodes=[&](int packetNumber) + { + for (int i=0; i<=16; i++) + if (packetNeeded(packetNumber, i)) { + QByteArray outLine = packet(packetNumber, i); + + *outStream << QString("OL,%1,").arg(packetNumber); + outLine[0] = i | 0x40; + for (int c=1; c> 1)); - } else { - *outStream << (char)(0x40 | lastTriplet.address()); - *outStream << (char)(0x40 | (lastTriplet.mode() | ((lastTriplet.data() & 1) << 5))); - *outStream << (char)(0x40 | (lastTriplet.data() >> 1)); - terminatorNeeded = false; - } - tripletNumber++; - } - *outStream << endl; - // If the last triplet of the last X26 row wasn't a termination marker, - // terminatorNeeded ensures we write an additional X26 row full of termination markers. - if (!terminatorNeeded && tripletNumber >= localEnhance.size()) - break; - } - } for (int r=1; r<25; r++) if (packetNeeded(r)) { - QString rowString; + QByteArray outLine = packet(r); - rowString.append(QString("OL,%1,").arg(r)); - for (int c=0; c<40; c++) { - unsigned char myChar = m_level1Page[r][c]; - if (myChar < 32) { - rowString.append((char)0x1b); - rowString.append((char)(myChar+0x40)); - } else - rowString.append((char)myChar); - } - *outStream << rowString << endl; - } + *outStream << QString("OL,%1,").arg(r); + for (int c=0; c> 1) | (m_leftSidePanelDisplayed << 3) | (m_rightSidePanelDisplayed << 4) | (m_sidePanelStatusL25 << 5) | (m_sidePanelColumns << 6); - - for (int c=0; c<16; c++){ - int r = (m_CLUT[offset+c] & 0xF00) >> 8; - int g = (m_CLUT[offset+c] & 0xF0) >> 4; - int b = m_CLUT[offset+c] & 0xF; - - int rtr = ((c * 12) + 28) / 18; - int rsh = ((c * 12) + 28) % 18; - x28Triplets[rtr] |= (r << rsh); - if (rsh == 16) - x28Triplets[rtr+1] |= (r >> 2) & 3; - - int gtr = ((c * 12) + 32) / 18; - int gsh = ((c * 12) + 32) % 18; - x28Triplets[gtr] |= (g << gsh); - if (gsh == 16) - x28Triplets[gtr+1] |= (g >> 2) & 3; - - int btr = ((c * 12) + 36) / 18; - int bsh = ((c * 12) + 36) % 18; - x28Triplets[btr] |= (b << bsh); - if (bsh == 16) - x28Triplets[btr+1] |= (b >> 2) & 3; - } - - x28Triplets[12] |= (m_defaultScreenColour << 4) | (m_defaultRowColour << 9) | (m_blackBackgroundSubst << 14) | (m_colourTableRemap << 15); - - for (int i=0; i<13; i++) { - result.append(0x40 | (x28Triplets[i] & 0x3F)); - result.append(0x40 | ((x28Triplets[i] & 0xFC0) >> 6)); - result.append(0x40 | ((x28Triplets[i] & 0x3F000) >> 12)); - } - - return result; -} - QString TeletextPage::exportURLHash(QString pageHash) { int hashDigits[1167]={0}; @@ -349,9 +341,6 @@ QString TeletextPage::exportURLHash(QString pageHash) for (int i=0; i<1167; i++) pageHash.append(base64[hashDigits[i]]); -// CLUTChangedResult = CLUTChanged(); -// if (leftSidePanel || rightSidePanel || defaultScreenColour !=0 || defaultRowColour !=0 || blackBackgroundSubst || colourTableRemap !=0 || CLUTChangedResult) { - if (packetNeeded(28,0) || packetNeeded(28,4)) { QString x28StringBegin, x28StringEnd; diff --git a/levelonepage.h b/levelonepage.h index 075a37e..1191e9d 100644 --- a/levelonepage.h +++ b/levelonepage.h @@ -42,6 +42,7 @@ public: TeletextPage(); + QByteArray packet(int, int=0); bool setPacket(int, QByteArray); bool setPacket(int, int, QByteArray); bool packetNeeded(int, int=0) const; @@ -91,7 +92,6 @@ public: protected: int controlBitsToPS() const; - QString x28toTTI(int); private: unsigned char m_level1Page[25][40]; diff --git a/pagebase.cpp b/pagebase.cpp index 4eeebc2..1989388 100644 --- a/pagebase.cpp +++ b/pagebase.cpp @@ -26,17 +26,37 @@ PageBase::PageBase() m_pageFunction = PFLOP; m_packetCoding = PC7bit; // We use nullptrs to keep track of allocated packets, so initialise them this way - for (int i=0; i<89; i++) + for (int i=0; i<90; i++) m_packets[i] = nullptr; } PageBase::~PageBase() { - for (int i=0; i<89; i++) + for (int i=0; i<90; i++) if (m_packets[i] != nullptr) delete m_packets[i]; } +QByteArray PageBase::packet(int packetNumber, int designationCode) const +{ + int packetArrayIndex = packetNumber; + + if (packetNumber >= 26) + packetArrayIndex += (packetNumber - 26) * 16 + designationCode; + if (m_packets[packetArrayIndex] == nullptr) + return QByteArray(); // Blank result + return *m_packets[packetArrayIndex]; +} + +bool PageBase::packetNeeded(int packetNumber, int designationCode) const +{ + int packetArrayIndex = packetNumber; + + if (packetNumber >= 26) + packetArrayIndex += (packetNumber - 26) * 16 + designationCode; + return m_packets[packetArrayIndex] != nullptr; +} + bool PageBase::setPacket(int packetNumber, QByteArray packetContents) { return setPacket(packetNumber, 0, packetContents); @@ -44,12 +64,27 @@ bool PageBase::setPacket(int packetNumber, QByteArray packetContents) bool PageBase::setPacket(int packetNumber, int designationCode, QByteArray packetContents) { - int packetArrayNumber = packetNumber; + int packetArrayIndex = packetNumber; + if (packetNumber >= 26) - packetArrayNumber += (packetNumber - 26) * 16; - if (m_packets[packetArrayNumber] == nullptr) - m_packets[packetArrayNumber] = new QByteArray(40, 0x00); - *m_packets[packetArrayNumber] = packetContents; + packetArrayIndex += (packetNumber - 26) * 16 + designationCode; + if (m_packets[packetArrayIndex] == nullptr) + m_packets[packetArrayIndex] = new QByteArray(40, 0x00); + *m_packets[packetArrayIndex] = packetContents; + + return true; +} + +bool PageBase::deletePacket(int packetNumber, int designationCode) +{ + int packetArrayIndex = packetNumber; + + if (packetNumber >= 26) + packetArrayIndex += (packetNumber - 26) * 16 + designationCode; + if (m_packets[packetArrayIndex] != nullptr) { + delete m_packets[packetArrayIndex]; + m_packets[packetArrayIndex] = nullptr; + } return true; } diff --git a/pagebase.h b/pagebase.h index 3f97e98..cdb7a95 100644 --- a/pagebase.h +++ b/pagebase.h @@ -35,18 +35,21 @@ public: PageBase(); ~PageBase(); + QByteArray packet(int, int=0) const; + bool packetNeeded(int, int=0) const; bool setPacket(int, QByteArray); bool setPacket(int, int, QByteArray); + bool deletePacket(int, int=0); + PageFunctionEnum pageFunction() const { return m_pageFunction; } bool setPageFunction(PageFunctionEnum); PacketCodingEnum packetCoding(int=0, int=0) const; bool setPacketCoding(PacketCodingEnum); - bool packetNeeded(int, int=0) const { return true; } private: PageFunctionEnum m_pageFunction; PacketCodingEnum m_packetCoding; - QByteArray *m_packets[89]; // X/1 to X/25, plus 16 packets for X/26, another 16 for X/27, for X28 and for X/29 + QByteArray *m_packets[90]; // X/0 to X/25, plus 16 packets for X/26, another 16 for X/27, for X28 and for X/29 }; #endif