More work on diskfiles

This commit is contained in:
Mark Long
2025-10-03 17:20:37 -05:00
parent f5618e435c
commit 80ff9e9b3d
11 changed files with 357 additions and 256 deletions
+44 -26
View File
@@ -1,13 +1,26 @@
#include "CatalogSector.h"
#include "Sector.h"
#include "AppleString.h"
#include "Sector.h"
#include <QDebug>
CatalogSector::CatalogSector(Sector* data) : m_data(data), m_next(0, 0)
namespace
{
// qDebug() << "### Start CatalogSector ctor";
constexpr int EntriesPerCatalogSector{7};
constexpr int EntryStride{0x23};
constexpr int EntryStartOffset{0x0B};
}
TSPair next(m_data->rawData()[0x01],m_data->rawData()[0x02]);
CatalogSector::CatalogSector(Sector* data) : m_data(data)
{
if (!m_data) {
qWarning("CatalogSector constructed with null Sector pointer");
return;
}
const auto& raw = m_data->rawData();
const TSPair next(static_cast<quint8>(raw.at(0x01)), static_cast<quint8>(raw.at(0x02)));
if (next.isValid() && next.track() == 17)
{
@@ -23,9 +36,9 @@ CatalogSector::CatalogSector(Sector* data) : m_data(data), m_next(0, 0)
//m_next.setTrack(m_data->rawData()[0x01]);
//m_next.setSector(m_data->rawData()[0x02]);
for (int idx = 0; idx<7; idx++)
for (int idx = 0; idx < EntriesPerCatalogSector; ++idx)
{
FileDescriptiveEntry fde = makeFDE(idx*0x23+0x0B);
const FileDescriptiveEntry fde = makeFDE((idx * EntryStride) + EntryStartOffset);
if (fde.firstTSListSector() != TSPair(0,0)) {
if (fde.firstTSListSector().isValid())
{
@@ -38,12 +51,13 @@ CatalogSector::CatalogSector(Sector* data) : m_data(data), m_next(0, 0)
// qDebug() << "### End CatalogSector ctor";
}
void CatalogSector::dumpFDEs() const {
for (int idx = 0; idx<7; idx++)
void CatalogSector::dumpFDEs() const
{
for (int idx = 0; idx < m_fdes.size(); ++idx)
{
FileDescriptiveEntry fde = m_fdes[idx];
const FileDescriptiveEntry& fde = m_fdes[idx];
if (fde.firstTSListSector() != TSPair(0,0)) {
qDebug() << "FDE #"<<idx;
qDebug().noquote() << QStringLiteral("FDE #%1").arg(idx);
fde.dump();
}
}
@@ -52,19 +66,20 @@ void CatalogSector::dumpFDEs() const {
FileDescriptiveEntry CatalogSector::makeFDE(int offset) const
{
FileDescriptiveEntry fde;
fde.firstTSListSector().setTrack(m_data->rawData()[offset + 0x00]);
fde.firstTSListSector().setSector(m_data->rawData()[offset + 0x01]);
fde.fileTypeAndFlags = m_data->rawData()[offset + 0x02];
fde.lengthInSectors = makeWord( m_data->rawData()[offset + 0x21],
m_data->rawData()[offset + 0x22]);
const auto& raw = m_data->rawData();
fde.firstTSListSector().setTrack(static_cast<quint8>(raw.at(offset + 0x00)));
fde.firstTSListSector().setSector(static_cast<quint8>(raw.at(offset + 0x01)));
fde.fileTypeAndFlags = static_cast<quint8>(raw.at(offset + 0x02));
fde.lengthInSectors = makeWord(static_cast<quint8>(raw.at(offset + 0x21)),
static_cast<quint8>(raw.at(offset + 0x22)));
if (fde.lengthInSectors > 16*35)
{
fde.lengthInSectors = -1;
}
for (int idx = 0x03; idx <= 0x20; idx++) {
fde.filename.append(m_data->rawData()[idx+offset]);
for (int idx = 0x03; idx <= 0x20; ++idx) {
fde.filename.append(raw.at(idx + offset));
}
if (fde.firstTSListSector().track() == 0xFF)
@@ -73,29 +88,32 @@ FileDescriptiveEntry CatalogSector::makeFDE(int offset) const
qDebug() << "File" << fde.filename.printable() << "is deleted";
fde.deleted = true;
qDebug() << fde.filename;
fde.firstTSListSector().setTrack(m_data->rawData()[offset + 0x20]);
qDebug() << " New track: " << (quint8) fde.firstTSListSector().track();
fde.firstTSListSector().setTrack(static_cast<quint8>(raw.at(offset + 0x20)));
qDebug() << " New track: " << static_cast<quint8>(fde.firstTSListSector().track());
qDebug() << " Sector: " << fde.firstTSListSector().sector();
}
return fde;
}
FileDescriptiveEntry& CatalogSector::getFDE(quint8 number) {
if (number >= m_fdes.length()) {
number = m_fdes.length() - 1;
FileDescriptiveEntry& CatalogSector::getFDE(quint8 number) noexcept
{
if (number >= static_cast<quint8>(m_fdes.length())) {
number = static_cast<quint8>(m_fdes.length() - 1);
}
return m_fdes[number];
}
const FileDescriptiveEntry& CatalogSector::getFDE(quint8 number) const {
if (number >= m_fdes.length()) {
number = m_fdes.length() - 1;
const FileDescriptiveEntry& CatalogSector::getFDE(quint8 number) const noexcept
{
if (number >= static_cast<quint8>(m_fdes.length())) {
number = static_cast<quint8>(m_fdes.length() - 1);
}
return m_fdes[number];
}
QList<FileDescriptiveEntry> CatalogSector::getFDEs() const {
QList<FileDescriptiveEntry> CatalogSector::getFDEs() const
{
return m_fdes;
}
+4 -6
View File
@@ -1,12 +1,10 @@
#pragma once
#include "FileDescriptiveEntry.h"
#include "Util.h"
#include "TSPair.h"
#include "Util.h"
#include <QtGlobal>
#include <QByteArray>
#include <QDebug>
#include <QList>
class Sector;
@@ -18,8 +16,8 @@ public:
explicit CatalogSector(Sector* sector);
// Const and non-const overloads for getFDE
[[nodiscard]] FileDescriptiveEntry& getFDE(quint8 number);
[[nodiscard]] const FileDescriptiveEntry& getFDE(quint8 number) const;
[[nodiscard]] FileDescriptiveEntry& getFDE(quint8 number) noexcept;
[[nodiscard]] const FileDescriptiveEntry& getFDE(quint8 number) const noexcept;
[[nodiscard]] QList<FileDescriptiveEntry> getFDEs() const;
+92 -82
View File
@@ -1,18 +1,26 @@
#include "DiskFile.h"
#include "TrackSectorList.h"
#include "ApplesoftFile.h"
#include "BinaryFile.h"
#include "CatalogSector.h"
#include "IntBasicFile.h"
#include "RelocatableFile.h"
#include "TextFile.h"
#include "TrackSectorList.h"
#include <QFile>
#include <QCryptographicHash>
#include <QDataStream>
#include <QFileInfo>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <array>
namespace
{
constexpr qsizetype BytesPerSector{Sector::BytesPerSector};
}
DiskFile::DiskFile(const QString& filename)
{
@@ -22,19 +30,13 @@ DiskFile::DiskFile(const QString& filename)
}
}
DiskFile::~DiskFile()
{
foreach (GenericFile *file, m_files)
{
delete file;
}
}
DiskFile::~DiskFile() = default;
bool DiskFile::read(const QString& filename)
{
m_fullImageName = filename;
m_imageName = QFileInfo(filename).fileName();
if (m_imageName.toUpper().contains(".D13"))
if (m_imageName.toUpper().contains(QStringLiteral(".D13")))
{
m_sectors_per_track = 13;
}
@@ -48,27 +50,29 @@ bool DiskFile::read(const QString& filename)
if (infile.open(QIODevice::ReadOnly))
{
QByteArray contents = infile.readAll();
const QByteArray contents = infile.readAll();
QDataStream qds(contents);
for (int track = 0; track < 35; track++)
qds.setByteOrder(QDataStream::LittleEndian);
for (qsizetype track = 0; track < TrackCount; ++track)
{
for (int sector = 0; sector < m_sectors_per_track; sector++)
for (quint8 sector = 0; sector < m_sectors_per_track; ++sector)
{
char buffer[256];
if (qds.readRawData(buffer,256) == 256)
std::array<char, BytesPerSector> buffer{};
if (qds.readRawData(buffer.data(), static_cast<int>(buffer.size())) == static_cast<int>(buffer.size()))
{
// qDebug() << "Track " << track << " Sector " << sector;
Sector sec;
sec.setTrackSector(track,sector);
if (!sec.setData(QByteArray(buffer,256))) {
qDebug() << "Failed to set sector data for Track " << track << " Sector " << sector;
sec.setTrackSector(static_cast<int>(track), sector);
if (!sec.setData(QByteArray(buffer.data(), static_cast<int>(buffer.size())))) {
qWarning().noquote() << QStringLiteral("Failed to set sector data for Track %1 Sector %2")
.arg(static_cast<int>(track))
.arg(sector);
return false;
}
m_contents[track][sector] = sec;
m_contents[static_cast<int>(track)][sector] = sec;
}
else
{
qDebug() << "Invalid sector read!";
qWarning("Invalid sector read!");
return false;
}
}
@@ -81,7 +85,9 @@ bool DiskFile::read(const QString& filename)
return true;
}
else
qDebug() << "Could not open file " << filename;
{
qWarning().noquote() << QStringLiteral("Could not open file %1").arg(filename);
}
return false;
}
@@ -90,12 +96,12 @@ VTOC DiskFile::getVTOC() const
return getSector(17,0).promoteToVTOC();
}
QList<CatalogSector> DiskFile::getCatalogSectors()
QList<CatalogSector> DiskFile::getCatalogSectors() const
{
// qDebug() << "### Start getCatalogSector";
QList<CatalogSector> retval;
VTOC vtoc = getVTOC();
const VTOC vtoc = getVTOC();
TSPair ts = vtoc.firstCatalogSector;
CatalogSector cs = getSector(ts).promoteToCatalogSector();
@@ -110,70 +116,74 @@ QList<CatalogSector> DiskFile::getCatalogSectors()
return retval;
}
GenericFile *DiskFile::getFile(FileDescriptiveEntry fde)
GenericFile *DiskFile::getFile(const FileDescriptiveEntry& fde)
{
GenericFile *retval = 0;
if (m_files.contains(fde))
if (const auto it = m_files.find(fde); it != m_files.end())
{
retval = m_files[fde];
return it->second.get();
}
if (!fde.firstTSListSector().isValid())
{
qWarning(" Not returning a file from invalid TSList!");
return nullptr;
}
const TrackSectorList tsl = getSector(fde.firstTSListSector()).promoteToTrackSectorList();
if (!fde.firstTSListSector().isValid())
{
qWarning(" Not returning a file from invalid TSList!");
return nullptr;
}
const QByteArray data = getDataFromTrackSectorList(tsl);
std::unique_ptr<GenericFile> file;
const QString fileType = fde.fileType();
if (fileType == QStringLiteral("A"))
{
file = std::make_unique<ApplesoftFile>(data);
}
else if (fileType == QStringLiteral("B"))
{
file = std::make_unique<BinaryFile>(data);
}
else if (fileType == QStringLiteral("R"))
{
file = std::make_unique<RelocatableFile>(data);
}
else if (fileType == QStringLiteral("T"))
{
file = std::make_unique<TextFile>(data);
}
else if (fileType == QStringLiteral("I"))
{
file = std::make_unique<IntBasicFile>(data);
}
else
{
if (!fde.firstTSListSector().isValid())
{
qWarning(" Not returning a file from invalid TSList!");
return nullptr;
}
TrackSectorList tsl = getSector(fde.firstTSListSector()).promoteToTrackSectorList();
if (!fde.firstTSListSector().isValid())
{
qWarning(" Not returning a file from invalid TSList!");
return nullptr;
}
QByteArray data = getDataFromTrackSectorList(tsl);
if (fde.fileType() == "A")
{
retval = new ApplesoftFile(data);
}
else if (fde.fileType() == "B")
{
retval = new BinaryFile(data);
}
else if (fde.fileType() == "R")
{
retval = new RelocatableFile(data);
}
else if ((fde.fileType() == "T"))
{
retval = new TextFile(data);
}
else if ((fde.fileType() == "I"))
{
retval = new IntBasicFile(data);
}
else
{
retval = new GenericFile(data);
}
m_files[fde] = retval;
file = std::make_unique<GenericFile>(data);
}
if (retval) { retval->setDiskFile(this); }
return retval;
if (!file)
{
return nullptr;
}
file->setDiskFile(this);
auto [it, inserted] = m_files.emplace(fde, std::move(file));
return it->second.get();
}
QByteArray DiskFile::getDataFromTrackSectorList(TrackSectorList tsl)
QByteArray DiskFile::getDataFromTrackSectorList(const TrackSectorList& tsl) const
{
QByteArray retval;
foreach(TSPair pair, tsl.getDataTSPairs())
for (const auto& pair : tsl.getDataTSPairs())
{
if (pair.isValid())
{
Sector sec = getSector(pair);
retval.append(sec.rawData());
const Sector& sec = getSector(pair);
retval.append(sec.rawData());
}
else
{
@@ -181,25 +191,25 @@ QByteArray DiskFile::getDataFromTrackSectorList(TrackSectorList tsl)
}
}
auto next = tsl.getNextTSList();
const auto next = tsl.getNextTSList();
if (next.isValid() && next != TSPair(0,0)) {
TrackSectorList nextTsl = getSector(tsl.getNextTSList()).promoteToTrackSectorList();
const TrackSectorList nextTsl = getSector(tsl.getNextTSList()).promoteToTrackSectorList();
retval.append(getDataFromTrackSectorList(nextTsl));
}
return retval;
}
QList<FileDescriptiveEntry> DiskFile::getAllFDEs()
QList<FileDescriptiveEntry> DiskFile::getAllFDEs() const
{
// qDebug() << "### Start getAllFDEs";
QList<FileDescriptiveEntry> retval;
QList<CatalogSector> sectors = getCatalogSectors();
const QList<CatalogSector> sectors = getCatalogSectors();
foreach (CatalogSector cs, sectors)
for (const auto& cs : sectors)
{
QList<FileDescriptiveEntry> fdes = cs.getFDEs();
const QList<FileDescriptiveEntry> fdes = cs.getFDEs();
retval.append(fdes);
}
// qDebug() << "### End getAllFDEs";
@@ -207,7 +217,7 @@ QList<FileDescriptiveEntry> DiskFile::getAllFDEs()
}
QString DiskFile::getMetaDataPath() const {
QString path = QString("%1.metadata/").arg(getFullDiskImageName());
const QString path = QStringLiteral("%1.metadata/").arg(getFullDiskImageName());
QDir dir(path);
dir.mkpath(".");
+35 -27
View File
@@ -1,56 +1,64 @@
#pragma once
#include "Util.h"
#include "TSPair.h"
#include "Sector.h"
#include "TSPair.h"
#include "Util.h"
#include "Vtoc.h"
#include <QByteArray>
#include <QList>
#include <QtGlobal>
#include <QMap>
#include <QString>
#include <QDebug>
#include <QCryptographicHash>
#include <map>
#include <memory>
class GenericFile;
class TrackSectorList;
class CatalogSector;
struct FileDescriptiveEntry;
class DiskFile
{
public:
explicit DiskFile(const QString& filename = "");
static constexpr qsizetype TrackCount{35};
explicit DiskFile(const QString& filename = {});
~DiskFile();
DiskFile(const DiskFile&) = delete;
DiskFile& operator=(const DiskFile&) = delete;
DiskFile(DiskFile&&) = delete;
DiskFile& operator=(DiskFile&&) = delete;
bool read(const QString& filename);
Sector& getSector(TSPair ts) { return getSector(ts.track(), ts.sector()); }
const Sector& getSector(TSPair ts) const { return getSector(ts.track(), ts.sector()); }
[[nodiscard]] Sector& getSector(const TSPair& ts) { return getSector(ts.track(), ts.sector()); }
[[nodiscard]] const Sector& getSector(const TSPair& ts) const { return getSector(ts.track(), ts.sector()); }
Sector& getSector(int track, int sector) {
return m_contents[track][sector];
}
const Sector& getSector(int track, int sector) const {
return m_contents[track][sector];
}
[[nodiscard]] Sector& getSector(int track, int sector) { return m_contents[track][sector]; }
[[nodiscard]] const Sector& getSector(int track, int sector) const { return m_contents[track][sector]; }
[[nodiscard]] VTOC getVTOC() const;
[[nodiscard]] QList<CatalogSector> getCatalogSectors();
[[nodiscard]] GenericFile* getFile(FileDescriptiveEntry fde);
[[nodiscard]] QByteArray getDataFromTrackSectorList(TrackSectorList tsl);
[[nodiscard]] QList<FileDescriptiveEntry> getAllFDEs();
[[nodiscard]] QList<CatalogSector> getCatalogSectors() const;
[[nodiscard]] GenericFile* getFile(const FileDescriptiveEntry& fde);
[[nodiscard]] QByteArray getDataFromTrackSectorList(const TrackSectorList& tsl) const;
[[nodiscard]] QList<FileDescriptiveEntry> getAllFDEs() const;
[[nodiscard]] QByteArray fileHash() const noexcept { return m_hash; }
[[nodiscard]] QString getDiskImageName() const noexcept { return m_imageName; }
[[nodiscard]] QString getFullDiskImageName() const noexcept { return m_fullImageName; }
[[nodiscard]] const QString& getDiskImageName() const noexcept { return m_imageName; }
[[nodiscard]] const QString& getFullDiskImageName() const noexcept { return m_fullImageName; }
[[nodiscard]] QString getMetaDataPath() const;
private:
QMap<int, QMap<int, Sector>> m_contents;
QMap<FileDescriptiveEntry, GenericFile*> m_files;
std::map<FileDescriptiveEntry, std::unique_ptr<GenericFile>> m_files;
QByteArray m_hash;
QString m_imageName;
QString m_fullImageName;
+31 -22
View File
@@ -1,36 +1,45 @@
#include "FileDescriptiveEntry.h"
#include <QString>
#include <QDebug>
#include <QString>
QString FileDescriptiveEntry::fileType() const {
if (fileTypeAndFlags & DOSIntegerBasicFile) { return "I"; }
if (fileTypeAndFlags & DOSApplesoftBasicFile) { return "A"; }
if (fileTypeAndFlags & DOSRelocatableFile) { return "R"; }
if (fileTypeAndFlags & DOSRawBinaryFile) { return "B"; }
if (fileTypeAndFlags & DOSTypeSFile) { return "S"; }
if (fileTypeAndFlags & DOSTypeAFile) { return "a"; }
if (fileTypeAndFlags & DOSTypeBFile) { return "b"; }
return "T";
QString FileDescriptiveEntry::fileType() const
{
if (fileTypeAndFlags & DOSIntegerBasicFile) { return QStringLiteral("I"); }
if (fileTypeAndFlags & DOSApplesoftBasicFile) { return QStringLiteral("A"); }
if (fileTypeAndFlags & DOSRelocatableFile) { return QStringLiteral("R"); }
if (fileTypeAndFlags & DOSRawBinaryFile) { return QStringLiteral("B"); }
if (fileTypeAndFlags & DOSTypeSFile) { return QStringLiteral("S"); }
if (fileTypeAndFlags & DOSTypeAFile) { return QStringLiteral("a"); }
if (fileTypeAndFlags & DOSTypeBFile) { return QStringLiteral("b"); }
return QStringLiteral("T");
}
void FileDescriptiveEntry::dump() const {
qDebug() << "First TS List Sector: Track: " << QString("%1").arg(firstTSListSector().track(),2,16,QChar('0')).toUpper()
<< " Sector: " << QString("%1").arg(firstTSListSector().sector(),2,16,QChar('0')).toUpper();
qDebug() << "File Type and Flags: " << QString::number((quint8)fileTypeAndFlags) << "(" << fileType() << "," << (isLocked()?"Locked":"Unlocked") << ")";
void FileDescriptiveEntry::dump() const
{
qDebug().noquote() << QStringLiteral("First TS List Sector: Track: %1 Sector: %2")
.arg(firstTSListSector().track(), 2, 16, QLatin1Char('0'))
.arg(firstTSListSector().sector(), 2, 16, QLatin1Char('0')).toUpper();
qDebug().noquote() << QStringLiteral("File Type and Flags: %1 (%2,%3)")
.arg(QString::number(static_cast<quint8>(fileTypeAndFlags)))
.arg(fileType())
.arg(isLocked() ? QStringLiteral("Locked") : QStringLiteral("Unlocked"));
qDebug() << "Filename: " << filename.printable();
qDebug() << "Length in Sectors: " << lengthInSectors;
qDebug().noquote() << QStringLiteral("Length in Sectors: %1").arg(lengthInSectors);
}
void FileDescriptiveEntry::catalog() const {
QString output = QString("%1 %2 %3 %4").arg(QString(isLocked()?"*":" "))
.arg(lengthInSectors,3,10,QChar('0'))
.arg(fileType())
.arg(filename.printable().trimmed());
qDebug() << output;
void FileDescriptiveEntry::catalog() const
{
const QString output = QStringLiteral("%1 %2 %3 %4")
.arg(isLocked() ? QStringLiteral("*") : QStringLiteral(" "))
.arg(lengthInSectors, 3, 10, QLatin1Char('0'))
.arg(fileType())
.arg(filename.printable().trimmed());
qDebug().noquote() << output;
}
void FileDescriptiveEntry::setFirstTSListSector(TSPair ts) {
void FileDescriptiveEntry::setFirstTSListSector(TSPair ts)
{
if (ts.isValid()) {
m_firstTSListSector = ts;
} else {
+40 -29
View File
@@ -1,54 +1,65 @@
#include "Sector.h"
#include <QString>
#include <QDebug>
#include <QString>
char& Sector::operator[](uint offset) {
if (offset > 255) {
offset = 255;
}
#include <algorithm>
char& Sector::operator[](qsizetype offset) noexcept
{
offset = std::clamp(offset, qsizetype{0}, BytesPerSector - 1);
return m_data[offset];
}
char Sector::at(uint offset) const {
if (offset > 255) {
offset = 255;
}
char Sector::at(qsizetype offset) const noexcept
{
offset = std::clamp(offset, qsizetype{0}, BytesPerSector - 1);
return m_data.at(offset);
}
bool Sector::setData(const QByteArray& data) {
if (data.length() != 256) return false;
bool Sector::setData(const QByteArray& data) noexcept
{
if (data.size() != BytesPerSector) {
return false;
}
m_data = data;
return true;
}
void Sector::dump() {
qDebug() << "Dumping Track " << track() << "Sector " << sector() << " ...";
for (int jdx = 0; jdx < 16; jdx++)
void Sector::dump() const
{
qDebug().noquote() << QStringLiteral("Dumping Track %1 Sector %2 ...")
.arg(track(), 2, 10, QLatin1Char('0'))
.arg(sector(), 2, 10, QLatin1Char('0'));
for (int row = 0; row < 16; ++row)
{
QString line;
line += QString("%1 (%2): ").arg(jdx*16,2,16,QChar('0')).arg(jdx*16,3,10,QChar(' '));
for (int idx = 0; idx < 16; idx++)
QString line = QStringLiteral("%1 (%2): ")
.arg(row * 16, 2, 16, QLatin1Char('0'))
.arg(row * 16, 3, 10, QLatin1Char(' '));
for (int col = 0; col < 16; ++col)
{
int offset = (jdx*16) + idx;
quint8 val = m_data[offset];
line += QString("%1 ").arg(val,2,16,QChar('0'));
if (idx == 7) line += " ";
const int offset = (row * 16) + col;
const quint8 value = static_cast<quint8>(m_data.at(offset));
line += QStringLiteral("%1 ").arg(value, 2, 16, QLatin1Char('0'));
if (col == 7) {
line += QLatin1Char(' ');
}
}
line = line.toUpper();
line += " ";
for (int idx = 0; idx < 16; idx++)
line += QStringLiteral(" ");
for (int col = 0; col < 16; ++col)
{
int offset = (jdx*16) + idx;
quint8 val = m_data[offset];
if (val > 127) { val -= 128; }
QChar ch(val);
line += QString("%1").arg(ch.isPrint()?ch:'.');
const int offset = (row * 16) + col;
quint8 value = static_cast<quint8>(m_data.at(offset));
if (value > 127U) {
value = static_cast<quint8>(value - 128U);
}
const QChar ch(value);
line += ch.isPrint() ? ch : QLatin1Char('.');
}
qDebug() << line;
qDebug().noquote() << line;
}
}
+18 -18
View File
@@ -5,31 +5,31 @@
#include "TrackSectorList.h"
#include <QByteArray>
#include <QtGlobal>
#include <array>
#include <cstddef>
class Sector
{
public:
static constexpr qsizetype BytesPerSector{256};
Sector() : m_data(256, '\0'), m_track{255}, m_sector{255}
{
}
Sector() = default;
[[nodiscard]] const VTOC promoteToVTOC() const {
return VTOC(*this);
}
[[nodiscard]] VTOC promoteToVTOC() const { return VTOC(*this); }
[[nodiscard]] CatalogSector promoteToCatalogSector() {
return CatalogSector(this);
}
[[nodiscard]] CatalogSector promoteToCatalogSector() { return CatalogSector(this); }
[[nodiscard]] CatalogSector promoteToCatalogSector() const { return CatalogSector(const_cast<Sector*>(this)); }
[[nodiscard]] TrackSectorList promoteToTrackSectorList() {
return TrackSectorList(this);
}
[[nodiscard]] TrackSectorList promoteToTrackSectorList() { return TrackSectorList(this); }
[[nodiscard]] TrackSectorList promoteToTrackSectorList() const { return TrackSectorList(const_cast<Sector*>(this)); }
[[nodiscard]] int sector() const noexcept { return m_sector; }
[[nodiscard]] int track() const noexcept { return m_track; }
void setTrackSector(int track, int sector) noexcept {
void setTrackSector(int track, int sector) noexcept
{
setTrack(track);
setSector(sector);
}
@@ -37,12 +37,12 @@ public:
void setTrack(int track) noexcept { m_track = track; }
void setSector(int sector) noexcept { m_sector = sector; }
[[nodiscard]] char& operator[](uint offset);
[[nodiscard]] char at(uint offset) const;
[[nodiscard]] char& operator[](qsizetype offset) noexcept;
[[nodiscard]] char at(qsizetype offset) const noexcept;
[[nodiscard]] bool setData(const QByteArray& data);
[[nodiscard]] bool setData(const QByteArray& data) noexcept;
void dump();
void dump() const;
[[nodiscard]] QByteArray data() const { return m_data; }
@@ -50,7 +50,7 @@ public:
[[nodiscard]] const QByteArray& rawData() const noexcept { return m_data; }
private:
QByteArray m_data;
QByteArray m_data{BytesPerSector, '\0'};
int m_track{0};
int m_sector{0};
};
+15 -6
View File
@@ -2,17 +2,26 @@
#include "Sector.h"
#include <QDebug>
TrackSectorList::TrackSectorList(Sector* data) : m_data(data)
{
m_next_tslist.setTrack(m_data->rawData()[0x01]);
m_next_tslist.setSector(m_data->rawData()[0x02]);
if (!m_data) {
qWarning("TrackSectorList constructed with null Sector pointer");
return;
}
m_sector_offset.setTrack(m_data->rawData()[0x05]);
m_sector_offset.setSector(m_data->rawData()[0x06]);
const auto& raw = m_data->rawData();
for (int idx = 0x0C; idx < 0xff; idx+=2)
m_next_tslist.setTrack(static_cast<quint8>(raw.at(0x01)));
m_next_tslist.setSector(static_cast<quint8>(raw.at(0x02)));
m_sector_offset.setTrack(static_cast<quint8>(raw.at(0x05)));
m_sector_offset.setSector(static_cast<quint8>(raw.at(0x06)));
for (int idx = 0x0C; idx < Sector::BytesPerSector; idx += 2)
{
TSPair ts(m_data->rawData()[idx],m_data->rawData()[idx+1]);
const TSPair ts(static_cast<quint8>(raw.at(idx)), static_cast<quint8>(raw.at(idx + 1)));
if (ts == TSPair(0,0) && ts.isValid()) {
break;
} else {
+3 -1
View File
@@ -1,7 +1,9 @@
#pragma once
#include "Util.h"
#include "TSPair.h"
#include "Util.h"
#include <QList>
class Sector;
+72 -37
View File
@@ -3,40 +3,58 @@
#include "Sector.h"
#include <QDebug>
#include <QString>
namespace
{
constexpr int FirstCatalogTrackOffset{0x01};
constexpr int FirstCatalogSectorOffset{0x02};
constexpr int DosVersionOffset{0x03};
constexpr int VolumeNumberOffset{0x06};
constexpr int MaxTsPairsOffset{0x27};
constexpr int LastTrackAllocatedOffset{0x30};
constexpr int DirectionOfAllocationOffset{0x31};
constexpr int TracksPerDiskOffset{0x34};
constexpr int SectorsPerDiskOffset{0x35};
constexpr int BytesPerSectorOffset{0x36};
constexpr int AllocationBitmapOffset{0x38};
}
VTOC::VTOC(const Sector& sectorData)
{
const auto& data = sectorData.data();
track = sectorData.track();
sector = sectorData.sector();
firstCatalogSector = TSPair(sectorData.data().at(0x01), sectorData.data().at(0x02));
dosVersion = sectorData.data().at(0x03);
volumeNumber = sectorData.data().at(0x06);
maxTSPairs = sectorData.data().at(0x27);
lastTrackAllocated = sectorData.data().at(0x30);
directionOfAllocation = (qint8) sectorData.data().at(0x31);
tracksPerDisk = sectorData.data().at(0x34);
sectorsPerDisk = sectorData.data().at(0x35);
bytesPerSector = makeWord(sectorData.data().at(0x36),
sectorData.data().at(0x37));
makeSectorsInUse(sectorData,tracksPerDisk,sectorsPerDisk);
firstCatalogSector = TSPair(static_cast<quint8>(data.at(FirstCatalogTrackOffset)),
static_cast<quint8>(data.at(FirstCatalogSectorOffset)));
dosVersion = static_cast<quint8>(data.at(DosVersionOffset));
volumeNumber = static_cast<quint8>(data.at(VolumeNumberOffset));
maxTSPairs = static_cast<quint8>(data.at(MaxTsPairsOffset));
lastTrackAllocated = static_cast<quint8>(data.at(LastTrackAllocatedOffset));
directionOfAllocation = static_cast<qint8>(data.at(DirectionOfAllocationOffset));
tracksPerDisk = static_cast<quint8>(data.at(TracksPerDiskOffset));
sectorsPerDisk = static_cast<quint8>(data.at(SectorsPerDiskOffset));
bytesPerSector = makeWord(static_cast<quint8>(data.at(BytesPerSectorOffset)),
static_cast<quint8>(data.at(BytesPerSectorOffset + 1)));
makeSectorsInUse(sectorData, tracksPerDisk, sectorsPerDisk);
}
void VTOC::makeSectorsInUse(const Sector& data, quint8 tracksPerDisk, quint8 sectorsPerDisk)
{
m_sectorsInUse.resize(tracksPerDisk * sectorsPerDisk);
const qsizetype totalSectors = static_cast<qsizetype>(tracksPerDisk) * sectorsPerDisk;
m_sectorsInUse.resize(totalSectors);
m_sectorsInUse.fill(false);
for (quint8 track = 0; track < tracksPerDisk; track++)
const auto& raw = data.data();
for (quint8 trackIndex = 0; trackIndex < tracksPerDisk; ++trackIndex)
{
quint8 baseaddr = (track * 4) + 0x38;
quint16 word = makeWord(data.data().at(baseaddr+1),
data.data().at(baseaddr));
for (quint8 sec = 0; sec < sectorsPerDisk; sec++)
const int baseaddr = (static_cast<int>(trackIndex) * 4) + AllocationBitmapOffset;
const quint16 word = makeWord(static_cast<quint8>(raw.at(baseaddr + 1)),
static_cast<quint8>(raw.at(baseaddr)));
for (quint8 sectorIndex = 0; sectorIndex < sectorsPerDisk; ++sectorIndex)
{
quint16 bitpos = (quint16) 0x01 << (quint16) sec;
bool inuse = !(word & bitpos);
m_sectorsInUse.setBit((track * sectorsPerDisk) + sec, inuse);
const quint16 bitpos = static_cast<quint16>(0x01U << sectorIndex);
const bool inuse = (word & bitpos) == 0U;
m_sectorsInUse.setBit((trackIndex * sectorsPerDisk) + sectorIndex, inuse);
}
}
}
@@ -49,29 +67,46 @@ bool VTOC::isSectorInUse(TSPair ts) const noexcept {
void VTOC::dump() const
{
qDebug() << "Dumping VTOC Track " << track << "Sector " << sector << " ...";
qDebug() << " Track number of first catalog sector: " << QString::number(firstCatalogSector.track());
qDebug() << " Sector number of first catalog sector: " << QString::number(firstCatalogSector.sector());
qDebug() << " Release number of DOS used to INIT disk: " << QString::number(dosVersion);
qDebug() << " Disk Volume Number: " << QString::number(volumeNumber);
qDebug() << " Max track/sector pairs that fit in t/s list sector (122=256): " << QString::number(maxTSPairs);
qDebug() << " Last track where sectors were allocated: " << QString::number(lastTrackAllocated);
qDebug() << " Direction of track allocations (+/- 1): " << QString::number(directionOfAllocation);
qDebug() << " Number tracks per disk: " << QString::number(tracksPerDisk);
qDebug() << " Number sectors per disk: " << QString::number(sectorsPerDisk);
qDebug() << " Number bytes/sector: " << QString::number(bytesPerSector);
qDebug() << " Track Usage (.=free, 0-F=used):";
for (quint8 track = 0; track < tracksPerDisk;track++)
qDebug().noquote() << QStringLiteral("Dumping VTOC Track %1 Sector %2 ...")
.arg(track)
.arg(sector);
qDebug().noquote() << QStringLiteral(" Track number of first catalog sector: %1")
.arg(firstCatalogSector.track());
qDebug().noquote() << QStringLiteral(" Sector number of first catalog sector: %1")
.arg(firstCatalogSector.sector());
qDebug().noquote() << QStringLiteral(" Release number of DOS used to INIT disk: %1")
.arg(dosVersion);
qDebug().noquote() << QStringLiteral(" Disk Volume Number: %1")
.arg(volumeNumber);
qDebug().noquote() << QStringLiteral(" Max track/sector pairs that fit in t/s list sector (122=256): %1")
.arg(maxTSPairs);
qDebug().noquote() << QStringLiteral(" Last track where sectors were allocated: %1")
.arg(lastTrackAllocated);
qDebug().noquote() << QStringLiteral(" Direction of track allocations (+/- 1): %1")
.arg(directionOfAllocation);
qDebug().noquote() << QStringLiteral(" Number tracks per disk: %1")
.arg(tracksPerDisk);
qDebug().noquote() << QStringLiteral(" Number sectors per disk: %1")
.arg(sectorsPerDisk);
qDebug().noquote() << QStringLiteral(" Number bytes/sector: %1")
.arg(bytesPerSector);
qDebug().noquote() << QStringLiteral(" Track Usage (.=free, 0-F=used):");
for (quint8 trackIndex = 0; trackIndex < tracksPerDisk; ++trackIndex)
{
qDebug() << " " << QString("Track %1:").arg((int) track,2,10,QChar('0')) << buildUseString(track);
qDebug().noquote() << QStringLiteral(" Track %1:")
.arg(static_cast<int>(trackIndex), 2, 10, QLatin1Char('0'))
<< buildUseString(trackIndex);
}
}
QString VTOC::buildUseString(quint8 track) const {
QString usestr;
for (qint8 sec = 0x0f; sec >= 0; sec--)
usestr.reserve(sectorsPerDisk);
for (qint8 sec = static_cast<qint8>(sectorsPerDisk) - 1; sec >= 0; --sec)
{
usestr.append(isSectorInUse(TSPair(track,sec))?QString::number(sec,16).toUpper():"-");
usestr.append(isSectorInUse(TSPair(track,static_cast<quint8>(sec)))
? QString::number(sec,16).toUpper()
: QStringLiteral("-"));
}
return usestr;
}
+3 -2
View File
@@ -3,11 +3,12 @@
#include "Util.h"
#include "TSPair.h"
#include <QtGlobal>
#include <QBitArray>
#include <QString>
#include <QtGlobal>
class Sector;
class QString;
struct VTOC