ciderpress/diskimg/OzDOS.cpp

321 lines
9.4 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Implementation of DiskFSOzDOS class.
*
* It would make life MUCH EASIER to have the DiskImg recognize this as
* a file format and just rearrange the blocks into linear order for us,
* but unfortunately that's not going to happen.
*/
#include "StdAfx.h"
#include "DiskImgPriv.h"
/*
* ===========================================================================
* DiskFSOzDOS
* ===========================================================================
*/
const int kExpectedNumBlocks = 1600;
const int kExpectedTracks = 50; // 50 tracks of 32 sectors == 400K
const int kExpectedSectors = 32;
const int kVTOCTrack = 17;
const int kVTOCSector = 0;
const int kSctSize = 256;
const int kCatalogEntrySize = 0x23; // length in bytes of catalog entries
const int kCatalogEntriesPerSect = 7; // #of entries per catalog sector
const int kMaxTSPairs = 0x7a; // 122 entries for 256-byte sectors
const int kTSOffset = 0x0c; // first T/S entry in a T/S list
const int kMaxTSIterations = 32;
const int kMaxCatalogIterations = 64;
/*
* Read a track/sector, adjusting for 32-sector disks being treated as
* if they were 16-sector.
*/
static DIError ReadTrackSectorAdjusted(DiskImg* pImg, int track, int sector,
int sectorOffset, uint8_t* buf, DiskImg::SectorOrder imageOrder)
{
track *= 4;
sector = sector * 2 + sectorOffset;
while (sector >= 16) {
track++;
sector -= 16;
}
return pImg->ReadTrackSectorSwapped(track, sector, buf, imageOrder,
DiskImg::kSectorOrderDOS);
}
/*
* Test for presence of 400K OzDOS 3.3 volumes.
*/
static DIError TestImageHalf(DiskImg* pImg, int sectorOffset,
DiskImg::SectorOrder imageOrder)
{
DIError dierr = kDIErrNone;
uint8_t sctBuf[kSctSize];
int numTracks, numSectors;
int catTrack, catSect;
int foundGood = 0;
int iterations = 0;
assert(sectorOffset == 0 || sectorOffset == 1);
dierr = ReadTrackSectorAdjusted(pImg, kVTOCTrack, kVTOCSector,
sectorOffset, sctBuf, imageOrder);
if (dierr != kDIErrNone)
goto bail;
catTrack = sctBuf[0x01];
catSect = sctBuf[0x02];
numTracks = sctBuf[0x34];
numSectors = sctBuf[0x35];
if (!(sctBuf[0x27] == kMaxTSPairs) ||
/*!(sctBuf[0x36] == 0 && sctBuf[0x37] == 1) ||*/ // bytes per sect
!(numTracks == kExpectedTracks) ||
!(numSectors == 32) ||
!(catTrack < numTracks && catSect < numSectors) ||
0)
{
LOGI(" OzDOS header test %d failed", sectorOffset);
dierr = kDIErrFilesystemNotFound;
goto bail;
}
/*
* Walk through the catalog track to try to figure out ordering.
*/
while (catTrack != 0 && catSect != 0 && iterations < kMaxCatalogIterations)
{
dierr = ReadTrackSectorAdjusted(pImg, catTrack, catSect,
sectorOffset, sctBuf, imageOrder);
if (dierr != kDIErrNone)
goto bail_ok; /* allow it if not fully broken */
if (sctBuf[1] == catTrack && sctBuf[2] == catSect-1)
foundGood++;
catTrack = sctBuf[1];
catSect = sctBuf[2];
iterations++; // watch for infinite loops
}
if (iterations >= kMaxCatalogIterations) {
dierr = kDIErrDirectoryLoop;
goto bail;
}
bail_ok:
LOGI(" OzDOS foundGood=%d off=%d swap=%d", foundGood, sectorOffset,
imageOrder);
/* foundGood hits 3 even when swap is wrong */
if (foundGood > 4)
dierr = kDIErrNone;
else
dierr = kDIErrFilesystemNotFound;
bail:
return dierr;
}
/*
* Test both of the DOS partitions.
*/
static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder)
{
DIError dierr;
LOGI(" OzDOS checking first half (swap=%d)", imageOrder);
dierr = TestImageHalf(pImg, 0, imageOrder);
if (dierr != kDIErrNone)
return dierr;
LOGI(" OzDOS checking second half (swap=%d)", imageOrder);
dierr = TestImageHalf(pImg, 1, imageOrder);
if (dierr != kDIErrNone)
return dierr;
return kDIErrNone;
}
/*
* Test to see if the image is a OzDOS volume.
*/
/*static*/ DIError DiskFSOzDOS::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder,
DiskImg::FSFormat* pFormat, FSLeniency leniency)
{
/* only on 800K disks (at the least, insist on numTracks being even) */
if (pImg->GetNumBlocks() != kExpectedNumBlocks)
return kDIErrFilesystemNotFound;
/* if a value is specified, try that first -- useful for OverrideFormat */
if (*pOrder != DiskImg::kSectorOrderUnknown) {
if (TestImage(pImg, *pOrder) == kDIErrNone) {
LOGI(" OzDOS accepted FirstTry value");
return kDIErrNone;
}
}
DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax];
DiskImg::GetSectorOrderArray(ordering, *pOrder);
for (int i = 0; i < DiskImg::kSectorOrderMax; i++) {
if (ordering[i] == DiskImg::kSectorOrderUnknown)
continue;
if (TestImage(pImg, ordering[i]) == kDIErrNone) {
*pOrder = ordering[i];
*pFormat = DiskImg::kFormatOzDOS;
return kDIErrNone;
}
}
LOGI(" OzDOS didn't find valid FS");
return kDIErrFilesystemNotFound;
}
#if 0
/*
* Test to see if the image is a 'wide' (32-sector) DOS3.3 volume, i.e.
* half of a OzDOS volume.
*/
/*static*/ DIError DiskFS::TestOzWideDOS33(const DiskImg* pImg,
DiskImg::SectorOrder* pOrder)
{
DIError dierr = kDIErrNone;
/* only on 400K disks (at the least, insist on numTracks being even) */
if (pImg->GetNumBlocks() != kExpectedNumBlocks/2)
return kDIErrFilesystemNotFound;
/* if a value is specified, try that first -- useful for OverrideFormat */
if (*pOrder != DiskImg::kSectorOrderUnknown) {
if (TestImageHalf(pImg, 0, *pOrder) == kDIErrNone) {
LOGI(" WideDOS accepted FirstTry value");
return kDIErrNone;
}
}
if (TestImageHalf(pImg, 0, DiskImg::kSectorOrderDOS) == kDIErrNone) {
*pOrder = DiskImg::kSectorOrderDOS;
} else if (TestImageHalf(pImg, 0, DiskImg::kSectorOrderProDOS) == kDIErrNone) {
*pOrder = DiskImg::kSectorOrderProDOS;
} else if (TestImageHalf(pImg, 0, DiskImg::kSectorOrderPhysical) == kDIErrNone) {
*pOrder = DiskImg::kSectorOrderPhysical;
} else {
LOGI(" FS didn't find valid 'wide' DOS3.3");
return kDIErrFilesystemNotFound;
}
return kDIErrNone;
}
#endif
/*
* Set up our sub-volumes.
*/
DIError DiskFSOzDOS::Initialize(void)
{
DIError dierr = kDIErrNone;
if (fScanForSubVolumes != kScanSubDisabled) {
dierr = OpenSubVolume(0);
if (dierr != kDIErrNone)
return dierr;
dierr = OpenSubVolume(1);
if (dierr != kDIErrNone)
return dierr;
} else {
LOGI(" OzDOS not scanning for sub-volumes");
}
SetVolumeUsageMap();
return kDIErrNone;
}
/*
* Open up one of the DOS 3.3 sub-volumes.
*/
DIError DiskFSOzDOS::OpenSubVolume(int idx)
{
DIError dierr = kDIErrNone;
DiskFS* pNewFS = NULL;
DiskImg* pNewImg = NULL;
pNewImg = new DiskImg;
if (pNewImg == NULL) {
dierr = kDIErrMalloc;
goto bail;
}
// open the full 800K; SetPairedSectors cuts it in half
dierr = pNewImg->OpenImage(fpImg, 0, 0,
2 * kExpectedTracks * kExpectedSectors);
if (dierr != kDIErrNone) {
LOGI(" OzSub: OpenImage(%d,0,%d) failed (err=%d)",
0, 2 * kExpectedTracks * kExpectedSectors, dierr);
goto bail;
}
assert(idx == 0 || idx == 1);
pNewImg->SetPairedSectors(true, 1-idx);
LOGI(" OzSub: testing for recognizable volume in idx=%d", idx);
dierr = pNewImg->AnalyzeImage();
if (dierr != kDIErrNone) {
LOGI(" OzSub: analysis failed (err=%d)", dierr);
goto bail;
}
if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown ||
pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown)
{
LOGI(" OzSub: unable to identify filesystem");
dierr = kDIErrUnsupportedFSFmt;
goto bail;
}
/* open a DiskFS for the sub-image */
LOGI(" UNISub %d succeeded!", idx);
pNewFS = pNewImg->OpenAppropriateDiskFS();
if (pNewFS == NULL) {
LOGI(" OzSub: OpenAppropriateDiskFS failed");
dierr = kDIErrUnsupportedFSFmt;
goto bail;
}
/* load the files from the sub-image */
dierr = pNewFS->Initialize(pNewImg, kInitFull);
if (dierr != kDIErrNone) {
LOGE(" OzSub: error %d reading list of files from disk", dierr);
goto bail;
}
/* if this really is DOS 3.3, override the "volume name" */
if (pNewImg->GetFSFormat() == DiskImg::kFormatDOS33) {
DiskFSDOS33* pDOS = (DiskFSDOS33*) pNewFS; /* eek, a downcast */
pDOS->SetDiskVolumeNum(idx+1);
}
/*
* Success, add it to the sub-volume list.
*/
AddSubVolumeToList(pNewImg, pNewFS);
bail:
if (dierr != kDIErrNone) {
delete pNewFS;
delete pNewImg;
}
return dierr;
}