mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-11 10:04:36 +00:00
366 lines
9.6 KiB
C++
366 lines
9.6 KiB
C++
|
/*
|
||
|
* CiderPress
|
||
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
||
|
* See the file LICENSE for distribution terms.
|
||
|
*/
|
||
|
/*
|
||
|
* Implementation of DiskFSUNIDOS class.
|
||
|
*
|
||
|
* The "UNIDOS" filesystem doesn't actually hold files. Instead, it holds
|
||
|
* two 400K DOS 3.3 volumes on an 800K disk.
|
||
|
*
|
||
|
* We do have a test here for "wide" DOS 3.3, which is largely a clone of
|
||
|
* the standard DOS 3.3 test. The trick is that we have to adjust our
|
||
|
* detection to account for 32-sector tracks, and do so while the object
|
||
|
* is still in a state where it believes it has 16 sectors per track.
|
||
|
*/
|
||
|
#include "StdAfx.h"
|
||
|
#include "DiskImgPriv.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
* ===========================================================================
|
||
|
* DiskFSUNIDOS
|
||
|
* ===========================================================================
|
||
|
*/
|
||
|
|
||
|
const int kExpectedNumBlocks = 1600;
|
||
|
const int kExpectedTracks = 50;
|
||
|
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 trackOffset, unsigned char* buf, DiskImg::SectorOrder imageOrder)
|
||
|
{
|
||
|
track += trackOffset;
|
||
|
track *= 2;
|
||
|
if (sector >= 16) {
|
||
|
track++;
|
||
|
sector -= 16;
|
||
|
}
|
||
|
return pImg->ReadTrackSectorSwapped(track, sector, buf, imageOrder,
|
||
|
DiskImg::kSectorOrderDOS);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Test for presence of 400K DOS 3.3 volumes.
|
||
|
*/
|
||
|
static DIError
|
||
|
TestImageHalf(DiskImg* pImg, int trackOffset, DiskImg::SectorOrder imageOrder,
|
||
|
int* pGoodCount)
|
||
|
{
|
||
|
DIError dierr = kDIErrNone;
|
||
|
unsigned char sctBuf[kSctSize];
|
||
|
int numTracks, numSectors;
|
||
|
int catTrack, catSect;
|
||
|
int foundGood = 0;
|
||
|
int iterations = 0;
|
||
|
|
||
|
*pGoodCount = 0;
|
||
|
|
||
|
dierr = ReadTrackSectorAdjusted(pImg, kVTOCTrack, kVTOCSector,
|
||
|
trackOffset, 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)
|
||
|
{
|
||
|
WMSG0(" UNI/Wide DOS header test failed\n");
|
||
|
dierr = kDIErrFilesystemNotFound;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
foundGood++; // score one for a valid-looking VTOC
|
||
|
|
||
|
/*
|
||
|
* Walk through the catalog track to try to figure out ordering.
|
||
|
*/
|
||
|
while (catTrack != 0 && catSect != 0 &&
|
||
|
iterations < DiskFSDOS33::kMaxCatalogSectors)
|
||
|
{
|
||
|
dierr = ReadTrackSectorAdjusted(pImg, catTrack, catSect,
|
||
|
trackOffset, sctBuf, imageOrder);
|
||
|
if (dierr != kDIErrNone) {
|
||
|
dierr = kDIErrNone;
|
||
|
break; /* allow it if earlier stuff was okay */
|
||
|
}
|
||
|
|
||
|
if (catTrack == sctBuf[1] && catSect == sctBuf[2] +1)
|
||
|
foundGood++;
|
||
|
else if (catTrack == sctBuf[1] && catSect == sctBuf[2]) {
|
||
|
WMSG2(" WideDOS detected self-reference on cat (%d,%d)\n",
|
||
|
catTrack, catSect);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
catTrack = sctBuf[1];
|
||
|
catSect = sctBuf[2];
|
||
|
iterations++; // watch for infinite loops
|
||
|
}
|
||
|
if (iterations >= DiskFSDOS33::kMaxCatalogSectors) {
|
||
|
dierr = kDIErrDirectoryLoop;
|
||
|
WMSG0(" WideDOS directory links cause a loop\n");
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
WMSG3(" WideDOS foundGood=%d off=%d swap=%d\n", foundGood,
|
||
|
trackOffset, imageOrder);
|
||
|
*pGoodCount = foundGood;
|
||
|
|
||
|
bail:
|
||
|
return dierr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Test both of the DOS partitions.
|
||
|
*/
|
||
|
static DIError
|
||
|
TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder, int* pGoodCount)
|
||
|
{
|
||
|
DIError dierr;
|
||
|
int goodCount1, goodCount2;
|
||
|
|
||
|
*pGoodCount = 0;
|
||
|
|
||
|
WMSG1(" UNIDOS checking first half (imageOrder=%d)\n", imageOrder);
|
||
|
dierr = TestImageHalf(pImg, 0, imageOrder, &goodCount1);
|
||
|
if (dierr != kDIErrNone)
|
||
|
return dierr;
|
||
|
|
||
|
WMSG1(" UNIDOS checking second half (imageOrder=%d)\n", imageOrder);
|
||
|
dierr = TestImageHalf(pImg, kExpectedTracks, imageOrder, &goodCount2);
|
||
|
if (dierr != kDIErrNone)
|
||
|
return dierr;
|
||
|
|
||
|
if (goodCount1 > goodCount2)
|
||
|
*pGoodCount = goodCount1;
|
||
|
else
|
||
|
*pGoodCount = goodCount2;
|
||
|
|
||
|
return kDIErrNone;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Test to see if the image is a UNIDOS volume.
|
||
|
*/
|
||
|
/*static*/ DIError
|
||
|
DiskFSUNIDOS::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;
|
||
|
|
||
|
DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax];
|
||
|
|
||
|
DiskImg::GetSectorOrderArray(ordering, *pOrder);
|
||
|
|
||
|
DiskImg::SectorOrder bestOrder = DiskImg::kSectorOrderUnknown;
|
||
|
int bestCount = 0;
|
||
|
|
||
|
for (int i = 0; i < DiskImg::kSectorOrderMax; i++) {
|
||
|
int goodCount = 0;
|
||
|
|
||
|
if (ordering[i] == DiskImg::kSectorOrderUnknown)
|
||
|
continue;
|
||
|
if (TestImage(pImg, ordering[i], &goodCount) == kDIErrNone) {
|
||
|
if (goodCount > bestCount) {
|
||
|
bestCount = goodCount;
|
||
|
bestOrder = ordering[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bestCount >= 4 ||
|
||
|
(leniency == kLeniencyVery && bestCount >= 2))
|
||
|
{
|
||
|
WMSG2(" WideDOS test: bestCount=%d for order=%d\n", bestCount, bestOrder);
|
||
|
assert(bestOrder != DiskImg::kSectorOrderUnknown);
|
||
|
*pOrder = bestOrder;
|
||
|
*pFormat = DiskImg::kFormatUNIDOS;
|
||
|
return kDIErrNone;
|
||
|
}
|
||
|
|
||
|
WMSG0(" UNIDOS didn't find valid FS\n");
|
||
|
return kDIErrFilesystemNotFound;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Test to see if the image is a 'wide' (32-sector) DOS3.3 volume, i.e.
|
||
|
* half of a UNIDOS volume (usually found embedded in ProDOS).
|
||
|
*
|
||
|
* Trying all possible formats is important here, because the wrong value for
|
||
|
* swap can return a "good" value of 7 (much less than the expected 30, but
|
||
|
* above a threshold of reasonableness).
|
||
|
*/
|
||
|
/*static*/ DIError
|
||
|
DiskFSUNIDOS::TestWideFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder,
|
||
|
DiskImg::FSFormat* pFormat, FSLeniency leniency)
|
||
|
{
|
||
|
/* only on 400K "disks" */
|
||
|
if (pImg->GetNumBlocks() != kExpectedNumBlocks/2) {
|
||
|
WMSG1(" WideDOS ignoring volume (numBlocks=%ld)\n",
|
||
|
pImg->GetNumBlocks());
|
||
|
return kDIErrFilesystemNotFound;
|
||
|
}
|
||
|
|
||
|
DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax];
|
||
|
|
||
|
DiskImg::GetSectorOrderArray(ordering, *pOrder);
|
||
|
|
||
|
DiskImg::SectorOrder bestOrder = DiskImg::kSectorOrderUnknown;
|
||
|
int bestCount = 0;
|
||
|
|
||
|
for (int i = 0; i < DiskImg::kSectorOrderMax; i++) {
|
||
|
int goodCount = 0;
|
||
|
|
||
|
if (ordering[i] == DiskImg::kSectorOrderUnknown)
|
||
|
continue;
|
||
|
if (TestImageHalf(pImg, 0, ordering[i], &goodCount) == kDIErrNone) {
|
||
|
if (goodCount > bestCount) {
|
||
|
bestCount = goodCount;
|
||
|
bestOrder = ordering[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bestCount >= 4 ||
|
||
|
(leniency == kLeniencyVery && bestCount >= 2))
|
||
|
{
|
||
|
WMSG2(" UNI/Wide test: bestCount=%d for order=%d\n", bestCount, bestOrder);
|
||
|
assert(bestOrder != DiskImg::kSectorOrderUnknown);
|
||
|
*pOrder = bestOrder;
|
||
|
*pFormat = DiskImg::kFormatDOS33;
|
||
|
// up to the caller to adjust numTracks/numSectPerTrack
|
||
|
return kDIErrNone;
|
||
|
}
|
||
|
|
||
|
WMSG0(" UNI/Wide didn't find valid FS\n");
|
||
|
return kDIErrFilesystemNotFound;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Set up our sub-volumes.
|
||
|
*/
|
||
|
DIError
|
||
|
DiskFSUNIDOS::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 {
|
||
|
WMSG0(" UNIDOS not scanning for sub-volumes\n");
|
||
|
}
|
||
|
|
||
|
SetVolumeUsageMap();
|
||
|
|
||
|
return kDIErrNone;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Open up one of the DOS 3.3 sub-volumes.
|
||
|
*/
|
||
|
DIError
|
||
|
DiskFSUNIDOS::OpenSubVolume(int idx)
|
||
|
{
|
||
|
DIError dierr = kDIErrNone;
|
||
|
DiskFS* pNewFS = nil;
|
||
|
DiskImg* pNewImg = nil;
|
||
|
|
||
|
pNewImg = new DiskImg;
|
||
|
if (pNewImg == nil) {
|
||
|
dierr = kDIErrMalloc;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
dierr = pNewImg->OpenImage(fpImg, kExpectedTracks * idx, 0,
|
||
|
kExpectedTracks * kExpectedSectors);
|
||
|
if (dierr != kDIErrNone) {
|
||
|
WMSG3(" UNISub: OpenImage(%d,0,%d) failed (err=%d)\n",
|
||
|
kExpectedTracks * idx, kExpectedTracks * kExpectedSectors, dierr);
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
dierr = pNewImg->AnalyzeImage();
|
||
|
if (dierr != kDIErrNone) {
|
||
|
WMSG1(" UNISub: analysis failed (err=%d)\n", dierr);
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown ||
|
||
|
pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown)
|
||
|
{
|
||
|
WMSG0(" UNISub: unable to identify filesystem\n");
|
||
|
dierr = kDIErrUnsupportedFSFmt;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
/* open a DiskFS for the sub-image */
|
||
|
WMSG1(" UNISub %d succeeded!\n", idx);
|
||
|
pNewFS = pNewImg->OpenAppropriateDiskFS();
|
||
|
if (pNewFS == nil) {
|
||
|
WMSG0(" UNISub: OpenAppropriateDiskFS failed\n");
|
||
|
dierr = kDIErrUnsupportedFSFmt;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
/* load the files from the sub-image */
|
||
|
dierr = pNewFS->Initialize(pNewImg, kInitFull);
|
||
|
if (dierr != kDIErrNone) {
|
||
|
WMSG1(" UNISub: 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;
|
||
|
}
|