mirror of
https://github.com/fadden/ciderpress.git
synced 2024-12-22 20:29:51 +00:00
aa3145856c
Focusing on the diskimg library this time, which deals with a lot of filesystem structures that have specific widths. This is still a bit lax in places, e.g. using "long" for lengths. Should either specify a bit width or use di_off_t. Also, added "override" keyword where appropriate. Also, bumped library version to 5.0.0.
321 lines
9.4 KiB
C++
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;
|
|
}
|