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.
This commit is contained in:
239
levelonepage.cpp
239
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<outLine.size(); c++)
|
||||
outLine[c] = outLine.at(c) | 0x40;
|
||||
*outStream << outLine << endl;
|
||||
}
|
||||
};
|
||||
|
||||
*outStream << QString("PN,%1%2").arg(pageNumber, 3, 16, QChar('0')).arg(subPageNumber & 0xff, 2, 16, QChar('0')) << endl;
|
||||
*outStream << QString("SC,%1").arg(subPageNumber, 4, 16, QChar('0')) << endl;
|
||||
*outStream << QString("PS,%1").arg(0x8000 | controlBitsToPS(), 4, 16, QChar('0')) << endl;
|
||||
*outStream << QString("CT,%1,%2").arg(m_cycleValue).arg(m_cycleType==CTcycles ? 'C' : 'T') << endl;
|
||||
//TODO RE and maybe FLOF?
|
||||
// TODO RE and maybe FLOF?
|
||||
|
||||
if (packetNeeded(28, 0))
|
||||
*outStream << "OL,28," << x28toTTI(0) << endl;
|
||||
if (packetNeeded(28, 4))
|
||||
*outStream << "OL,28," << x28toTTI(4) << endl;
|
||||
// BUG FL commands may clash with X/27/0 packets that specify links manually (e.g. with subcodes)
|
||||
writePacketsWithDesignationCodes(27);
|
||||
writePacketsWithDesignationCodes(28);
|
||||
writePacketsWithDesignationCodes(26);
|
||||
|
||||
if (!localEnhance.isEmpty()) {
|
||||
int tripletNumber = 0;
|
||||
X26Triplet lastTriplet = localEnhance.at(localEnhance.size()-1);
|
||||
int terminatorNeeded = true; // Becomes false if a termination marker (without a "...follow") is already at the end
|
||||
|
||||
if (lastTriplet.mode() == 0x1f && lastTriplet.address() == 0x3f)
|
||||
if (lastTriplet.data() & 0x01)
|
||||
terminatorNeeded = false;
|
||||
else
|
||||
// Last termination marker has "follow" set but nothing follows, so write another one afterwards
|
||||
lastTriplet.setData(lastTriplet.data() | 0x01);
|
||||
else {
|
||||
// No termination marker there, so make up one
|
||||
lastTriplet.setAddress(0x3f);
|
||||
lastTriplet.setMode(0x1f);
|
||||
lastTriplet.setData(0x07);
|
||||
}
|
||||
|
||||
for (int d=0; d<16; d++) {
|
||||
*outStream << "OL,26," << (char)(d | 0x40);
|
||||
for (int t=0; t<13; t++) {
|
||||
if (tripletNumber < localEnhance.size()) {
|
||||
*outStream << (char)(0x40 | localEnhance.at(tripletNumber).address());
|
||||
*outStream << (char)(0x40 | (localEnhance.at(tripletNumber).mode() | ((localEnhance.at(tripletNumber).data() & 1) << 5)));
|
||||
*outStream << (char)(0x40 | (localEnhance.at(tripletNumber).data() >> 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<outLine.size(); c++)
|
||||
if (outLine.at(c) < 0x20) {
|
||||
outLine[c] = outLine.at(c) | 0x40;
|
||||
outLine.insert(c, 0x1b);
|
||||
c++;
|
||||
}
|
||||
*outStream << outLine << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int TeletextPage::controlBitsToPS() const
|
||||
@@ -275,61 +322,6 @@ int TeletextPage::controlBitsToPS() const
|
||||
return pageStatus;
|
||||
}
|
||||
|
||||
QString TeletextPage::x28toTTI(int designationCode)
|
||||
{
|
||||
QString result;
|
||||
int x28Triplets[13] = {0};
|
||||
|
||||
int offset;
|
||||
|
||||
switch (designationCode) {
|
||||
case 0:
|
||||
offset = 16;
|
||||
break;
|
||||
case 4:
|
||||
offset = 0;
|
||||
break;
|
||||
}
|
||||
result.append(designationCode + 0x40);
|
||||
|
||||
x28Triplets[0] = ((m_secondCharSet & 1) << 17) | (m_secondNOS << 14) | (m_defaultCharSet << 10) | (m_defaultNOS << 7);
|
||||
x28Triplets[1] = (m_secondCharSet >> 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;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
49
pagebase.cpp
49
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user