Separate metadata loading

"Metadata" is data which is stored in a teletext file format but is not part
of the page itself, such as DE description and CT cycle time in the TTI file
format.
This commit is contained in:
Gavin MacGregor
2025-05-25 12:54:14 +01:00
parent 3f93da8c1a
commit 395f3769cb
5 changed files with 89 additions and 48 deletions

View File

@@ -19,6 +19,7 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QList> #include <QList>
#include <QVariant>
#include "document.h" #include "document.h"
@@ -200,6 +201,39 @@ void TeletextDocument::unDeleteSubPageFromRecycle(int subPage)
m_recycleSubPages.removeLast(); m_recycleSubPages.removeLast();
} }
void TeletextDocument::loadMetaData(QVariantHash const &metadata)
{
bool valueOk;
if (const QString description = metadata.value("description").toString(); !description.isEmpty())
m_description = description;
if (const int pageNumber = metadata.value("pageNumber").toInt(&valueOk); valueOk)
m_pageNumber = pageNumber;
if (metadata.value("fastextAbsolute").toBool()) {
const int magazineFlip = m_pageNumber & 0x700;
for (auto &subPage : m_subPages)
for (int i=0; i<6; i++)
subPage->setFastTextLinkPageNumber(i, subPage->fastTextLinkPageNumber(i) ^ magazineFlip);
}
for (int i=0; i<numberOfSubPages(); i++) {
const QString subPageStr = QString("%1").arg(i, 3, QChar('0'));
if (int region = metadata.value("region" + subPageStr).toInt(&valueOk); valueOk)
subPage(i)->setDefaultCharSet(region);
if (int cycleValue = metadata.value("cycleValue" + subPageStr).toInt(&valueOk); valueOk)
subPage(i)->setCycleValue(cycleValue);
QChar cycleType = metadata.value("cycleType" + subPageStr).toChar();
if (cycleType == 'C')
subPage(i)->setCycleType(LevelOnePage::CTcycles);
else if (cycleType == 'T')
subPage(i)->setCycleType(LevelOnePage::CTseconds);
}
}
void TeletextDocument::setPageNumber(int pageNumber) void TeletextDocument::setPageNumber(int pageNumber)
{ {
// If the magazine number was changed, we need to update the relative magazine numbers in FastText // If the magazine number was changed, we need to update the relative magazine numbers in FastText

View File

@@ -24,6 +24,7 @@
#include <QList> #include <QList>
#include <QObject> #include <QObject>
#include <QUndoStack> #include <QUndoStack>
#include <QVariant>
#include "levelonepage.h" #include "levelonepage.h"
@@ -74,6 +75,7 @@ public:
void deleteSubPage(int subPageToDelete); void deleteSubPage(int subPageToDelete);
void deleteSubPageToRecycle(int subPageToRecycle); void deleteSubPageToRecycle(int subPageToRecycle);
void unDeleteSubPageFromRecycle(int subPage); void unDeleteSubPageFromRecycle(int subPage);
void loadMetaData(QVariantHash const &metadata);
int pageNumber() const { return m_pageNumber; } int pageNumber() const { return m_pageNumber; }
void setPageNumber(int pageNumber); void setPageNumber(int pageNumber);
void setPageNumberFromString(QString pageNumberString); void setPageNumberFromString(QString pageNumberString);

View File

@@ -24,24 +24,23 @@
#include <QFile> #include <QFile>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QTextStream> #include <QVariant>
#include "document.h" #include "document.h"
#include "hamming.h" #include "hamming.h"
#include "levelonepage.h" #include "levelonepage.h"
#include "pagebase.h" #include "pagebase.h"
bool LoadTTIFormat::load(QFile *inFile, TeletextDocument *document) bool LoadTTIFormat::load(QFile *inFile, TeletextDocument *document, QVariantHash *metadata)
{ {
m_warnings.clear(); m_warnings.clear();
m_error.clear(); m_error.clear();
QByteArray inLine; QByteArray inLine;
int pageNum = 0;
int currentSubPageNum = 0;
bool firstSubPageAlreadyFound = false; bool firstSubPageAlreadyFound = false;
bool pageBodyPacketsFound = false; bool pageBodyPacketsFound = false;
int cycleCommandsFound = 0;
int mostRecentCycleValue = -1;
LevelOnePage::CycleTypeEnum mostRecentCycleType;
LevelOnePage* loadingPage = document->subPage(0); LevelOnePage* loadingPage = document->subPage(0);
@@ -49,17 +48,27 @@ bool LoadTTIFormat::load(QFile *inFile, TeletextDocument *document)
inLine = inFile->readLine(160).trimmed(); inLine = inFile->readLine(160).trimmed();
if (inLine.isEmpty()) if (inLine.isEmpty())
break; break;
if (inLine.startsWith("DE,")) if (inLine.startsWith("DE,") && metadata != nullptr)
document->setDescription(QString(inLine.remove(0, 3))); metadata->insert("description", QString(inLine.remove(0, 3)));
if (inLine.startsWith("PN,")) { if (inLine.startsWith("PN,")) {
// When second and subsequent PN commands are found, firstSubPageAlreadyFound==true at this point if (!firstSubPageAlreadyFound) {
// This assumes that PN is the first command of a new subpage... // First PN command found, set the page number
if (firstSubPageAlreadyFound) { bool valueOk;
if (int pageNumRead = inLine.mid(3, 3).toInt(&valueOk, 16); valueOk)
if (pageNumRead >= 0x100 && pageNumRead <= 0x8ff) {
// Keep page number: to check if page is xFF if we load M/29
pageNum = pageNumRead;
if (metadata != nullptr)
metadata->insert("pageNumber", pageNum);
}
firstSubPageAlreadyFound = true;
} else {
// Subsequent PN command found; this assumes that PN is the first command of a new subpage
currentSubPageNum++;
document->insertSubPage(document->numberOfSubPages(), false); document->insertSubPage(document->numberOfSubPages(), false);
loadingPage = document->subPage(document->numberOfSubPages()-1); loadingPage = document->subPage(document->numberOfSubPages()-1);
} else {
document->setPageNumberFromString(inLine.mid(3,3));
firstSubPageAlreadyFound = true;
} }
} }
/* if (lineType == "SC,") { /* if (lineType == "SC,") {
@@ -82,37 +91,34 @@ bool LoadTTIFormat::load(QFile *inFile, TeletextDocument *document)
if (inLine.startsWith("RE,")) { if (inLine.startsWith("RE,")) {
bool regionValueOk; bool regionValueOk;
int regionValueRead = inLine.remove(0, 3).toInt(&regionValueOk); int regionValueRead = inLine.remove(0, 3).toInt(&regionValueOk);
if (regionValueOk) if (regionValueOk && metadata != nullptr)
loadingPage->setDefaultCharSet(regionValueRead); metadata->insert(QString("region%1").arg(currentSubPageNum, 3, QChar('0')), regionValueRead);
} }
if (inLine.startsWith("CT,") && (inLine.endsWith(",C") || inLine.endsWith(",T"))) { if (inLine.startsWith("CT,") && (inLine.endsWith(",C") || inLine.endsWith(",T"))) {
bool cycleValueOk; bool cycleValueOk;
int cycleValueRead = inLine.mid(3, inLine.size()-5).toInt(&cycleValueOk); int cycleValueRead = inLine.mid(3, inLine.size()-5).toInt(&cycleValueOk);
if (cycleValueOk) { if (cycleValueOk && metadata != nullptr) {
cycleCommandsFound++; metadata->insert(QString("cycleValue%1").arg(currentSubPageNum, 3, QChar('0')), cycleValueRead);
// House-keep CT command values, in case it's the only one within multiple subpages metadata->insert(QString("cycleType%1").arg(currentSubPageNum, 3, QChar('0')), inLine.at(inLine.size()-1));
mostRecentCycleValue = cycleValueRead;
loadingPage->setCycleValue(cycleValueRead);
mostRecentCycleType = inLine.endsWith("C") ? LevelOnePage::CTcycles : LevelOnePage::CTseconds;
loadingPage->setCycleType(mostRecentCycleType);
} }
} }
if (inLine.startsWith("FL,")) { if (inLine.startsWith("FL,")) {
bool fastTextLinkOk; bool fastTextLinkOk;
int fastTextLinkRead; int fastTextLinkRead;
QString flLine = QString(inLine.remove(0, 3)); QString flLine = QString(inLine.remove(0, 3));
if (flLine.count(',') == 5) if (flLine.count(',') == 5) {
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
fastTextLinkRead = flLine.section(',', i, i).toInt(&fastTextLinkOk, 16); fastTextLinkRead = flLine.section(',', i, i).toInt(&fastTextLinkOk, 16);
if (fastTextLinkOk) { if (fastTextLinkOk) {
if (fastTextLinkRead == 0) if (fastTextLinkRead == 0)
fastTextLinkRead = 0x8ff; fastTextLinkRead = 0x8ff;
// Stored as page link with relative magazine number, convert from absolute page number that was read else if (fastTextLinkRead >= 0x100 && fastTextLinkRead <= 0x8ff)
fastTextLinkRead ^= document->pageNumber() & 0x700;
fastTextLinkRead &= 0x7ff; // Fixes magazine 8 to 0
loadingPage->setFastTextLinkPageNumber(i, fastTextLinkRead); loadingPage->setFastTextLinkPageNumber(i, fastTextLinkRead);
} }
} }
if (metadata != nullptr)
metadata->insert(QString("fastextAbsolute"), true);
}
} }
if (inLine.startsWith("OL,")) { if (inLine.startsWith("OL,")) {
bool lineNumberOk; bool lineNumberOk;
@@ -162,7 +168,7 @@ bool LoadTTIFormat::load(QFile *inFile, TeletextDocument *document)
inLine[i] = inLine.at(i) & 0x3f; inLine[i] = inLine.at(i) & 0x3f;
// Import M/29 whole-magazine packets as X/28 per-page packets // Import M/29 whole-magazine packets as X/28 per-page packets
if (lineNumber == 29) { if (lineNumber == 29) {
if ((document->pageNumber() & 0xff) != 0xff) if ((pageNum & 0xff) != 0xff)
m_warnings.append(QString("M/29/%1 packet found, but page number was not xFF.").arg(designationCode)); m_warnings.append(QString("M/29/%1 packet found, but page number was not xFF.").arg(designationCode));
lineNumber = 28; lineNumber = 28;
} }
@@ -177,14 +183,6 @@ bool LoadTTIFormat::load(QFile *inFile, TeletextDocument *document)
return false; return false;
} }
// If there's more than one subpage but only one valid CT command was found, apply it to all subpages
// I don't know if this is correct
if (cycleCommandsFound == 1 && document->numberOfSubPages()>1)
for (int i=0; i<document->numberOfSubPages(); i++) {
document->subPage(i)->setCycleValue(mostRecentCycleValue);
document->subPage(i)->setCycleType(mostRecentCycleType);
}
return true; return true;
} }
@@ -194,7 +192,7 @@ bool LoadT42Format::readPacket()
return m_inFile->read((char *)m_inLine, 42) == 42; return m_inFile->read((char *)m_inLine, 42) == 42;
} }
bool LoadT42Format::load(QFile *inFile, TeletextDocument *document) bool LoadT42Format::load(QFile *inFile, TeletextDocument *document, QVariantHash *metadata)
{ {
int readMagazineNumber, readPacketNumber; int readMagazineNumber, readPacketNumber;
int foundMagazineNumber = -1; int foundMagazineNumber = -1;
@@ -259,10 +257,12 @@ bool LoadT42Format::load(QFile *inFile, TeletextDocument *document)
foundPageNumber = readPageNumber; foundPageNumber = readPageNumber;
firstPacket0Found = true; firstPacket0Found = true;
if (metadata != nullptr) {
if (foundMagazineNumber == 0) if (foundMagazineNumber == 0)
document->setPageNumber(0x800 | foundPageNumber); metadata->insert("pageNumber", 0x800 | foundPageNumber);
else else
document->setPageNumber((foundMagazineNumber << 8) | foundPageNumber); metadata->insert("pageNumber", (foundMagazineNumber << 8) | foundPageNumber);
}
document->subPage(0)->setControlBit(PageBase::C4ErasePage, m_inLine[5] & 0x08); document->subPage(0)->setControlBit(PageBase::C4ErasePage, m_inLine[5] & 0x08);
document->subPage(0)->setControlBit(PageBase::C5Newsflash, m_inLine[7] & 0x04); document->subPage(0)->setControlBit(PageBase::C5Newsflash, m_inLine[7] & 0x04);
@@ -435,7 +435,7 @@ bool LoadHTTFormat::readPacket()
} }
bool LoadEP1Format::load(QFile *inFile, TeletextDocument *document) bool LoadEP1Format::load(QFile *inFile, TeletextDocument *document, QVariantHash *metadata)
{ {
m_warnings.clear(); m_warnings.clear();
m_error.clear(); m_error.clear();

View File

@@ -25,7 +25,7 @@
#include <QFile> #include <QFile>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QTextStream> #include <QVariant>
#include "document.h" #include "document.h"
#include "levelonepage.h" #include "levelonepage.h"
@@ -36,7 +36,7 @@ class LoadFormat
public: public:
virtual ~LoadFormat() {}; virtual ~LoadFormat() {};
virtual bool load(QFile *inFile, TeletextDocument *document) =0; virtual bool load(QFile *inFile, TeletextDocument *document, QVariantHash *metadata = nullptr) =0;
virtual QString description() const =0; virtual QString description() const =0;
virtual QStringList extensions() const =0; virtual QStringList extensions() const =0;
@@ -55,7 +55,7 @@ protected:
class LoadTTIFormat : public LoadFormat class LoadTTIFormat : public LoadFormat
{ {
public: public:
bool load(QFile *inFile, TeletextDocument *document) override; bool load(QFile *inFile, TeletextDocument *document, QVariantHash *metadata = nullptr) override;
QString description() const override { return QString("MRG Systems TTI"); }; QString description() const override { return QString("MRG Systems TTI"); };
QStringList extensions() const override { return QStringList { "tti", "ttix" }; }; QStringList extensions() const override { return QStringList { "tti", "ttix" }; };
@@ -64,7 +64,7 @@ public:
class LoadT42Format : public LoadFormat class LoadT42Format : public LoadFormat
{ {
public: public:
bool load(QFile *inFile, TeletextDocument *document) override; bool load(QFile *inFile, TeletextDocument *document, QVariantHash *metadata = nullptr) override;
QString description() const override { return QString("t42 packet stream"); }; QString description() const override { return QString("t42 packet stream"); };
QStringList extensions() const override { return QStringList { "t42" }; }; QStringList extensions() const override { return QStringList { "t42" }; };
@@ -89,7 +89,7 @@ protected:
class LoadEP1Format : public LoadFormat class LoadEP1Format : public LoadFormat
{ {
public: public:
bool load(QFile *inFile, TeletextDocument *document) override; bool load(QFile *inFile, TeletextDocument *document, QVariantHash *metadata = nullptr) override;
QString description() const override { return QString("Softel EP1"); }; QString description() const override { return QString("Softel EP1"); };
QStringList extensions() const override { return QStringList { "ep1", "epx" }; }; QStringList extensions() const override { return QStringList { "ep1", "epx" }; };

View File

@@ -37,6 +37,7 @@
#include <QStatusBar> #include <QStatusBar>
#include <QToolBar> #include <QToolBar>
#include <QToolButton> #include <QToolButton>
#include <QVariant>
#include <iostream> #include <iostream>
#include "mainwindow.h" #include "mainwindow.h"
@@ -1068,7 +1069,11 @@ void MainWindow::loadFile(const QString &fileName)
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
if (loadingFormat->load(&file, m_textWidget->document())) { QVariantHash metadata;
if (loadingFormat->load(&file, m_textWidget->document(), &metadata)) {
m_textWidget->document()->loadMetaData(metadata);
if (m_saveFormats.isExportOnly(QFileInfo(file).suffix())) if (m_saveFormats.isExportOnly(QFileInfo(file).suffix()))
m_exportAutoFileName = fileName; m_exportAutoFileName = fileName;
else else