mirror of
https://github.com/markdavidlong/AppleSAWS.git
synced 2026-04-20 20:16:35 +00:00
More work on diskfiles
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(".");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Util.h"
|
||||
#include "TSPair.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
class Sector;
|
||||
|
||||
|
||||
@@ -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,11 +3,12 @@
|
||||
#include "Util.h"
|
||||
#include "TSPair.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QBitArray>
|
||||
#include <QString>
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
class Sector;
|
||||
class QString;
|
||||
|
||||
|
||||
struct VTOC
|
||||
|
||||
Reference in New Issue
Block a user