/* * 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; }