/* * 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, uint8_t* 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; uint8_t 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) { LOGI(" UNI/Wide DOS header test failed"); 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]) { LOGI(" WideDOS detected self-reference on cat (%d,%d)", catTrack, catSect); break; } catTrack = sctBuf[1]; catSect = sctBuf[2]; iterations++; // watch for infinite loops } if (iterations >= DiskFSDOS33::kMaxCatalogSectors) { dierr = kDIErrDirectoryLoop; LOGI(" WideDOS directory links cause a loop"); goto bail; } LOGI(" WideDOS foundGood=%d off=%d swap=%d", 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; LOGI(" UNIDOS checking first half (imageOrder=%d)", imageOrder); dierr = TestImageHalf(pImg, 0, imageOrder, &goodCount1); if (dierr != kDIErrNone) return dierr; LOGI(" UNIDOS checking second half (imageOrder=%d)", 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)) { LOGI(" WideDOS test: bestCount=%d for order=%d", bestCount, bestOrder); assert(bestOrder != DiskImg::kSectorOrderUnknown); *pOrder = bestOrder; *pFormat = DiskImg::kFormatUNIDOS; return kDIErrNone; } LOGI(" UNIDOS didn't find valid FS"); 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) { LOGI(" WideDOS ignoring volume (numBlocks=%ld)", 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)) { LOGI(" UNI/Wide test: bestCount=%d for order=%d", bestCount, bestOrder); assert(bestOrder != DiskImg::kSectorOrderUnknown); *pOrder = bestOrder; *pFormat = DiskImg::kFormatDOS33; // up to the caller to adjust numTracks/numSectPerTrack return kDIErrNone; } LOGI(" UNI/Wide didn't find valid FS"); 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 { LOGI(" UNIDOS not scanning for sub-volumes"); } SetVolumeUsageMap(); return kDIErrNone; } /* * Open up one of the DOS 3.3 sub-volumes. */ DIError DiskFSUNIDOS::OpenSubVolume(int idx) { DIError dierr = kDIErrNone; DiskFS* pNewFS = NULL; DiskImg* pNewImg = NULL; pNewImg = new DiskImg; if (pNewImg == NULL) { dierr = kDIErrMalloc; goto bail; } dierr = pNewImg->OpenImage(fpImg, kExpectedTracks * idx, 0, kExpectedTracks * kExpectedSectors); if (dierr != kDIErrNone) { LOGI(" UNISub: OpenImage(%d,0,%d) failed (err=%d)", kExpectedTracks * idx, kExpectedTracks * kExpectedSectors, dierr); goto bail; } dierr = pNewImg->AnalyzeImage(); if (dierr != kDIErrNone) { LOGI(" UNISub: analysis failed (err=%d)", dierr); goto bail; } if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) { LOGI(" UNISub: 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(" UNISub: OpenAppropriateDiskFS failed"); dierr = kDIErrUnsupportedFSFmt; goto bail; } /* load the files from the sub-image */ dierr = pNewFS->Initialize(pNewImg, kInitFull); if (dierr != kDIErrNone) { LOGE(" 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; }