//	CDiskDos.c

#include "CDesktop.h"
#include "ADFS_O_Callbacks.h"
#include "Utils.h"
#include "MemUtils.h"
#include "ProStructs.h"
#include "ADFS_Icons.h"
#include "DosCatalog.h"
#include "DosStructs.h"
#include "CFolderDos.h"
#include "IC_FileIO.h"
#include "CDiskDos.h"

OSErr		CDiskDos::IDiskDos(
	CDesktop		*desktop, 
	DiskImageRec	*imageRec
) {
	OSErr				err		= noErr;
	Dos_VtocSector		*vtocP;
	
	i_myEntryCachedB	= FALSE;
	i_cachedSectorsP	= NULL;
	
	if (!err) {
		i_blocksInVolume = 0;

		err = _inherited::IDisk(desktop, imageRec);
	}
	
	if (!err) {
		vtocP				= GetMyEntry();
		i_blocksInVolume	= (ulong)vtocP->perDisk.track * (ulong)vtocP->perDisk.sector;
		
		if (!vtocP) err = IC_Err_READ_ILLEGAL_TRACK_SECTOR;
	}

	if (!err) {

		if (!err) {
			if (NewObject(i_rootDir.dos, CFolderDos, err)) {
				err = i_rootDir.dos->IFolderDos(this, vtocP->dirSector);
			}
		}
	}
	
	return err;
}

void		CDiskDos::Dispose(void)
{
	if (!i_disposingB) {
//		i_rootDir.dos->Dispose();

		if (i_myEntryCachedB) {
			(void)DisposeSector(Dos_gVtocSector);		//	finally dispose last ref
		}
		
		Purge(FALSE);		
		ASSERT(i_cachedSectorsP == NULL);
		
		if (i_cachedSectorsP != NULL) {
			(void)Purge(TRUE);
		}
	}
	
	_inherited::Dispose();
}

OSErr			CDiskDos::FlushEntry(void)
{
	OSErr		err = noErr;
	
	err = ASSERT(i_myEntryCachedB);
	if (!err) err = SetSector(Dos_gVtocSector);
		
	return err;
}

Dos_VtocSector	*CDiskDos::GetMyEntry(void)
{
	Dos_VtocSector	*vtocP	= NULL;
	OSErr			err		= noErr;
	
	err = GetSector(Dos_gVtocSector, (Dos_Sector **)&vtocP);

	if (!err) {
		if (!i_myEntryCachedB) {
			//	just leave it allocated
			i_myEntryCachedB	= TRUE;
		} else {
			err = DisposeSector(Dos_gVtocSector);
		}
	}
	
	return vtocP;
}

OSErr		CDiskDos::BuildFileTypeMenu(void)
{
	OSErr		err = noErr;
	
	i_fileTypeMenu = GetMenuHandle(207);
	
	if (!i_fileTypeMenu) {
		i_fileTypeMenu = GetMenu(207);
		if (!i_fileTypeMenu) {
			ReportError(err = IC_Err_COULDNT_LOAD_DLG_ITEM);
		}
		
		if (!err) {
			Dos_BuildFileTypeMenu(i_fileTypeMenu);
		}
	}
	
	return err;
}

short		CDiskDos::FileTypeToMenuItem(Byte fileType, short auxType)
{
	Dos_FileDescType	descType = Dos_FileTypeToDescType(fileType);
	
	return descType + 1;
}

short		CDiskDos::MenuItemToFileType(short menuItem, ushort *auxType)
{
	short	fileType;
	
	if (menuItem == 1) {
		fileType = Dos_FileType_TXT;
	} else {
		fileType = 1 << (menuItem - 2);
	}
	
	return fileType;
}

/*
Gen_BlockNum	CDiskDos::GetTotalBlocks(void)
{
	Purge(TRUE);
	
	return 800k;
}

OSErr			CDiskDos::SetGenericBlock(ushort blockNumS, Gen_Block *blockP)
{
	OSErr				err			= noErr;
	Pas_Block			*prevBlockP = i_blockBuf;
	Pas_BlockNum		prevBlockS	= i_recentBlockNum;
	
	i_recentBlockNum	= blockNumS;
	i_blockBuf			= (Pas_Block *)blockP;

	err = SetBlock();

	i_recentBlockNum	= prevBlockS;
	i_blockBuf			= prevBlockP;

	return err;
}

OSErr			CDiskDos::GetGenericBlock(ushort blockNumS, Gen_Block **blockP)
{
	return GetBlock(blockNumS, (Pro_Block **)blockP);
}
*/
//	flushes dirty sectors
OSErr		CDiskDos::Flush(void)
{
	OSErr				err		= noErr;
	Dos_SectorStackRef	*curP	= i_cachedSectorsP;
	
	i_pending_flushB = FALSE;

	while (!err && curP) {
	
		//	make it a disk struct record
		//	if not interleaved, queue them up (using ptrandhand) and do a big setchunk
	
		#ifdef DEBUG
			//	it's possible that directories are NOT on track $11
			//	so this is only for debugging.
			//	we cache and leave referenced the entire directory track
			if (curP->sectorSpec.track != 0x11) {
				ASSERT(curP->refCount == 0);
			}
		#endif

		if (curP->dirtyB) {
			err = Dos_SetSector(i_imageRec, curP->sectorSpec, &curP->sector);
			curP->dirtyB = FALSE;
		}

		curP = curP->nextP;
	}
	
	return err;
}

//	purges sectors with a zero refcount
//	if criticalB = FALSE, 
//		only non dirty sectors
//	else
//		write dirty sectors and purge them too
OSErr		CDiskDos::Purge(Boolean criticalB)
{
	OSErr				err		= noErr;
	Dos_SectorStackRef	*curP	= i_cachedSectorsP;
	Dos_SectorStackRef	*nextP;
	
	if (criticalB) {
		err = Flush();
	}

	while (!err && curP) {
	
		nextP = curP->nextP;

		if (!err && !curP->dirtyB && curP->refCount == 0) {
			err = SectorStack_Remove(curP);
		}

		curP = nextP;
	}
	
	return err;
}

Dos_SectorStackRef	*CDiskDos::SectorStack_Find(Dos_SectorSpec sectorSpec)
{
	Dos_SectorStackRef	*foundP	= NULL;
	Dos_SectorStackRef	*curP = i_cachedSectorsP;
//	Boolean				doneB = FALSE;
	
	while (curP) {
		if (Gen_EqualSector(curP->sectorSpec, sectorSpec)) {
			foundP = curP;
			curP = NULL;
		} else {
			curP = curP->nextP;
		}
	}
	
	return foundP;
}

Dos_SectorSpec	g_debug_sector = Dos_gInvalidSector;

OSErr		CDiskDos::SectorStack_Add(
	Dos_SectorSpec		sectorSpec, 
	Dos_SectorStackRef	**sectorRefPP, 
	Dos_Sector			*copySectorP, 
	Boolean				emptySectorB)
{
	OSErr		err			= noErr;
	char		sectorAC[256];
	
	sprintf(
		sectorAC, 
		"DOS SectorSpec: $%.2hX $%.2hX", 
		sectorSpec.track, sectorSpec.sector);

	*sectorRefPP = (Dos_SectorStackRef *)TrackNewPtrClear(sectorAC, sizeof(Dos_SectorStackRef));
	if (*sectorRefPP == NULL) err = memFullErr;
			
	if (!err) {
		Dos_Sector		*sectorP = &((*sectorRefPP)->sector);

		(*sectorRefPP)->sectorSpec	= sectorSpec;
		(*sectorRefPP)->refCount++;
		
		if (!emptySectorB) {
			if (copySectorP) {
				*sectorP = *copySectorP;
			} else {				
				err = Dos_GetSector(i_imageRec, sectorSpec, &sectorP);
			}
		}
		
		if (err) {
			TrackDisposePtr((Ptr)*sectorRefPP);
		}
	}
	
	if (!err) {
		if (i_cachedSectorsP != NULL) {
			(*sectorRefPP)->nextP = i_cachedSectorsP;
			i_cachedSectorsP->prevP = *sectorRefPP;
		}

		i_cachedSectorsP = *sectorRefPP;
	}
	
	return err;
}


OSErr	CDiskDos::SectorStack_Remove(Dos_SectorStackRef *sectorRefP)
{
	OSErr	err	 = noErr;
	
	err = ASSERT(sectorRefP);
	if (!err) err = ASSERT(sectorRefP->refCount == 0);
	
	if (!err) {
		if (sectorRefP->prevP) {
			sectorRefP->prevP->nextP = sectorRefP->nextP;
		} else {
			i_cachedSectorsP = sectorRefP->nextP;
		}
			
		if (sectorRefP->nextP) {
			sectorRefP->nextP->prevP = sectorRefP->prevP;
		}
		
		TrackDisposePtr((Ptr)sectorRefP);
	}
	
	return err;
}

OSErr		CDiskDos::DisposeSector(Dos_SectorSpec sectorSpec)
{
	OSErr		err = noErr;

	if (!IS_ImageRec_IN_MEMORY(i_imageRec)) {
		Dos_SectorStackRef	*sectorRefP = SectorStack_Find(sectorSpec);
		
		err = ASSERT(sectorRefP != NULL);
		
		if (!err && --sectorRefP->refCount < 0) {
			ReportErrorStr(err = 1, "Disposing an unallocated sector!");
		}

		if (
			!Dos_IsInvalidSector(g_debug_sector)
			&& Dos_EqualSector(sectorSpec, g_debug_sector)
		) {
			DebugStr("\pExiting because of programmer break.");
		}
	}

	return err;
}

OSErr	CDiskDos::GetSector(Dos_SectorSpec sectorSpec, Dos_Sector **sectorPP, Boolean refB)
{
	OSErr		err = noErr;
	
	if (IS_ImageRec_IN_MEMORY(i_imageRec)) {
		err = Dos_GetSector(i_imageRec, sectorSpec, sectorPP);
	} else {
		Dos_SectorStackRef	*sectorRefP = SectorStack_Find(sectorSpec);
		
		if (sectorRefP == NULL) {
			err = SectorStack_Add(sectorSpec, &sectorRefP, NULL, refB);
		} else {
			if (refB) {
				err = ASSERT(sectorRefP->refCount == 0);
				memclr(&sectorRefP->sector, sizeof(Dos_Sector));
			}
			
			sectorRefP->refCount++;
		}
		
		if (!err) {
			*sectorPP = &sectorRefP->sector;
		}

		if (
			!Dos_IsInvalidSector(g_debug_sector)
			&& Dos_EqualSector(sectorSpec, g_debug_sector)
		) {
			DebugStr("\pExiting because of programmer break.");
		}
	}
	
	return err;
}

OSErr		CDiskDos::SetSector(Dos_SectorSpec sectorSpec, Dos_Sector *sectorP)
{
	OSErr		err = noErr;
	
	if (!IS_ImageRec_IN_MEMORY(i_imageRec) || i_flushMemLevel == 1) {
	
		if (!IS_ImageRec_IN_MEMORY(i_imageRec)) {
			Dos_SectorStackRef		*sectorRefP = SectorStack_Find(sectorSpec);

			if (sectorP == NULL) {
				err = ASSERT(sectorRefP);
			} else {
				if (sectorRefP == NULL) {
					err = SectorStack_Add(sectorSpec, &sectorRefP, sectorP);
				}
			}
			
			if (!err) {
				sectorRefP->dirtyB	= TRUE;
				i_pending_flushB	= TRUE;

				gDesktop->SetPendingFlush();
			}
		} else {
			if (!err) err = Dos_SetSector(i_imageRec, sectorSpec, sectorP);
		}
	}
	
	return err;
}

OSErr		CDiskDos::SetAndDisposeSector(Dos_SectorSpec sector, Dos_Sector *sectorP)
{
	OSErr	err = SetSector(sector, sectorP);
	OSErr	err2 = DisposeSector(sector);
	
	if (!err) err = err2;
	
	return err;
}
/****************************************************/

ADFS_IconType		CDiskDos::GetIconType(void)
{
	short		icon = 0;
	
	if (IS_ImageRec_IN_MEMORY(i_imageRec)) {
		icon = ADFS_Icon_DOS_SPRO + GetSectorOrderForIcon();
	} else {
		icon = ADFS_Icon_DOS_800;
	}
	
	return icon;
}

char		*CDiskDos::GetDescription(char *buf)
{
	strcpy(buf, "DOS 3.3 Disk");
	return buf;
}

/****************************************************/
enum	{
	Dos_VBM_Select_NONE, 
	Dos_VBM_Select_GET_NEXT, 
	Dos_VBM_Select_GET, 
	Dos_VBM_Select_SET, 
	Dos_VBM_Select_COUNT, 
	Dos_VBM_Select_NUMTYPES
};
typedef	short	Dos_VBM_SelectType;

typedef struct {
	Dos_VBM_SelectType		selectType;
	Dos_SectorSpec			sectorSpec;
	ulong					sectorCount;
	Boolean					free;
} Dos_FreeSectorCBData;

static	OSErr	FreeSectorCB(
	CDiskDos		*thiz, 
	Dos_SectorSpec	*sectorSpec, 
	Boolean			*free, 
	Boolean			*done, 
	void			*data)
{
	OSErr					err		= noErr;
	Dos_FreeSectorCBData	*cbData = (Dos_FreeSectorCBData *)data;
	
	switch (cbData->selectType) {

		case Dos_VBM_Select_GET_NEXT: {
			if (*free) {
				cbData->free		= TRUE;
				cbData->sectorSpec	= *sectorSpec;
				*done				= TRUE;
			}
			break;
		}

		case Dos_VBM_Select_GET: {
			if (Dos_EqualSector(cbData->sectorSpec, *sectorSpec)) {
				cbData->free		= *free;
				*done				= TRUE;
			}
			break;
		}

		case Dos_VBM_Select_SET: {
			if (Dos_EqualSector(cbData->sectorSpec, *sectorSpec)) {
				if (Dos_IsNullSector(cbData->sectorSpec)) {
					ReportErrorStr(-1, "Can't set Block 0!");
				}
				
				*free = cbData->free;
				*done = TRUE;
			}
			break;
		}

		case Dos_VBM_Select_COUNT: {
			if (*free == FALSE) {
				cbData->sectorCount++;
			}
			break;
		}

		default: {
			ReportErrorStr(-1, "Huh?");
			err = 1;
			break;
		}
	}

	return err;
}

static	OSErr		ScanSectors(
	CDiskDos				*thiz, 
	Dos_VtocSector			*vtoc, 
	ushort					startTrack, 
	ushort					endTrack, 
	ForEachBitmapSectorCB	UserCB, 
	void					*data, 
	Boolean					*userDoneB0)
{
	OSErr				err		= noErr;
	Boolean				done	= FALSE;
	Dos_SectorSpec		curSector;
	ulong				mask;
	ulong				*curSectorP;
	ushort				curTrack;
	ushort				loop2;
	Boolean				wasFree, isFree;
	Dos_SectorAlloc		sectorAlloc;

	if (endTrack > startTrack) {
		sectorAlloc.direction = 1;
	} else {
		sectorAlloc.direction = -1;
	}

	for (curTrack = startTrack; !done; curTrack += sectorAlloc.direction) {
		curSectorP		= &vtoc->trackBitmaps[curTrack].bitMap;
		*curSectorP		= OSSwapBigToHostInt32(*curSectorP);
		
		curSector.track	= sectorAlloc.lastTrack = curTrack;
		mask			= 0x80000000;
		
		for (loop2 = 0; !done && loop2 < vtoc->perDisk.sector; loop2++) {
			isFree = wasFree = ((*curSectorP) & mask) != Dos_kUsedSector;
			
			curSector.sector = (vtoc->perDisk.sector - 1) - loop2;
			
			err = (*UserCB)(thiz, &curSector, &isFree, &done, data);
			
			if (done && userDoneB0) {
				*userDoneB0 = TRUE;
			}

			if (!err) {
				if (done) {
					if (isFree != wasFree) {
						if (isFree) {
							*curSectorP = (*curSectorP) | mask;
						} else {
							vtoc->sectorAlloc = sectorAlloc;							
							*curSectorP = (*curSectorP) & (~mask);
						}
						
						err = thiz->FlushEntry();
					}
				} else {
					mask = mask >> 1;
				}
			}

			if (err) {
				done = TRUE;
			}
		}

		if (!err && !done) {
			done = curTrack == endTrack;
		}

		*curSectorP		= OSSwapHostToBigInt32(*curSectorP);
	}
	
	return err;
}

OSErr			CDiskDos::ForEachBitmapSector(
	ForEachBitmapSectorCB	UserCB, 
	void					*data)
{
	OSErr			err			= noErr;
	Dos_VtocSector	*vtoc		= NULL;
	
	vtoc = GetMyEntry();
	
	if (vtoc) {
		Boolean			userDoneB	= FALSE;
	
		if (!err) err = ScanSectors(
			this, vtoc, 
			Dos_kBitMapTrack, vtoc->perDisk.track - 1, 
			UserCB, data, &userDoneB);
			
		if (!err && !userDoneB) err = ScanSectors(
			this, vtoc, 
			Dos_kBitMapTrack - 1, 0, 
			UserCB, data, NULL);
	}
	
	return err;		
}

/**********************************************************/
ulong		CDiskDos::GetVolumeSize(void)
{
	return i_blocksInVolume * (ulong)Gen_kBytesPerSector;
}

ulong		CDiskDos::GetVolumeBytesUsed(void)
{
	OSErr					err = noErr;
	Dos_FreeSectorCBData	cbData;
	
	cbData.selectType	= Dos_VBM_Select_COUNT;
	cbData.sectorCount	= 0;
	cbData.free			= FALSE;
	
	if (!err) {
		err = ForEachBitmapSector(FreeSectorCB, &cbData);
	}
	
	return cbData.sectorCount * sizeof(Dos_Sector);
}

/**********************************************************/
OSErr	CDiskDos::IsFreeSector(Dos_SectorSpec sectorSpec, Boolean *isFreeB)
{
	OSErr					err = noErr;
	Dos_FreeSectorCBData	cbData;
	
	cbData.selectType	= Dos_VBM_Select_GET;
	cbData.free			= FALSE;
	cbData.sectorSpec	= sectorSpec;
	
	if (!err) {
		err = ForEachBitmapSector(FreeSectorCB, &cbData);
	}
	
	*isFreeB = FALSE;
	
	if (err == noErr) {
		*isFreeB = cbData.free;
	}
	
	return err;
}

OSErr	CDiskDos::GetFreeSector(Dos_SectorSpec *freeSectorSpec)
{
	OSErr					err = noErr;
	Dos_FreeSectorCBData	cbData;
	
	cbData.selectType	= Dos_VBM_Select_GET_NEXT;
	cbData.free			= FALSE;
	cbData.sectorSpec	= Dos_gEmptySector;
	*freeSectorSpec		= Dos_gEmptySector;
	
	if (!err) {
		err = ForEachBitmapSector(FreeSectorCB, &cbData);
	}
	
	if (err == noErr) {
		if (cbData.free) {
			*freeSectorSpec = cbData.sectorSpec;
		} else {
			err = IC_Err_DISK_FULL;
		}
	}
	
	return err;
}

OSErr			CDiskDos::FreeSector(Dos_SectorSpec sectorSpec)
{
	OSErr					err = noErr;
	Dos_FreeSectorCBData	cbData;
	
	cbData.selectType		= Dos_VBM_Select_SET;
	cbData.free				= TRUE;
	cbData.sectorSpec		= sectorSpec;
	
	if (!err) {
		err = ForEachBitmapSector(FreeSectorCB, &cbData);
	}
	
	return err;
}

OSErr			CDiskDos::ReserveSector(Dos_SectorSpec sectorSpec)
{
	OSErr					err = noErr;
	Dos_FreeSectorCBData	cbData;
	
	cbData.selectType		= Dos_VBM_Select_SET;
	cbData.free				= FALSE;
	cbData.sectorSpec		= sectorSpec;
	
	if (!err) {
		err = ForEachBitmapSector(FreeSectorCB, &cbData);
	}
	
	return err;
}

OSErr			CDiskDos::ReserveNextFreeSector(Dos_SectorSpec *freeSectorSpec)
{
	OSErr					err = noErr;

	err = GetFreeSector(freeSectorSpec);
	if (!err) err = ReserveSector(*freeSectorSpec);
	
	return err;
}

//	scans TS list of file, allocating new TSList as necessary, returning first
//	free (empty) sectorSpec.  does NOT allocate a new sector and point the 
//	sector spec at it!  (ie: can be used to simply create the first TSList for
//	a new file)
OSErr			CDiskDos::Dos_GetNextFreeTSSectorAlloc(
	Dos_DirEntry	*entryP, 
	Dos_SectorSpec	*curSectorSpecP, 
	Dos_SectorSpec	**sectorSpecH, 
	Boolean			*allocdBP0)
{
	OSErr				err = noErr;
	Dos_TSListSector	*tsListP;
	Boolean				foundB = FALSE, allocB;
	ushort				tsListNum = 0;
	Dos_SectorSpec		nextSectorSpec;
	
	allocB = Dos_IsNullSector(entryP->firstTSSector);
	if (allocB) {
		err = ReserveNextFreeSector(&entryP->firstTSSector);
	}

	*curSectorSpecP = entryP->firstTSSector;
	err = GetSector(*curSectorSpecP, (Dos_Sector **)&tsListP);
	
	if (!err && allocB) {
		memclr(tsListP, sizeof(Dos_Sector));
		tsListP->sectorBaseNum = SetRboShort(tsListNum * Dos_kMaxSectorSpecs);
		err = SetSector(*curSectorSpecP, (Dos_Sector *)tsListP);
	}
	
	do {
		ushort	loop;
		
		for (loop = 0; !err && !foundB && loop < Dos_kMaxSectorSpecs; loop++) {
			if (Dos_IsNullSector(tsListP->fileSectors[loop])) {
				*sectorSpecH = &tsListP->fileSectors[loop];
				foundB = TRUE;
			}
		}
		
		if (!foundB) {
			allocB = Dos_IsNullSector(tsListP->nextTsSector);
			
			if (allocB) {
				err = ReserveNextFreeSector(&tsListP->nextTsSector);
				if (!err) err = SetSector(*curSectorSpecP, (Dos_Sector *)tsListP);
			}
			
			nextSectorSpec = tsListP->nextTsSector;
			DisposeSector(*curSectorSpecP);
			*curSectorSpecP = nextSectorSpec;

			if (!err) err = GetSector(*curSectorSpecP, (Dos_Sector **)&tsListP);
	
			if (!err && allocB) {
				tsListNum++;
				memclr(tsListP, sizeof(Dos_Sector));
				tsListP->sectorBaseNum = SetRboShort(tsListNum * Dos_kMaxSectorSpecs);
				SetSector(*curSectorSpecP, (Dos_Sector *)tsListP);
			}
		}
	} while (!err && !foundB);
	
	if (!err) {
		//	inc the file count to include the TSList if we alloc'd one
		if (allocB) {
			entryP->size = SetRboShort(GetRboShort(entryP->size) + 1);
		}

		if (allocdBP0) {
			*allocdBP0 = allocB;
		}
	}

	return err;
}

ulong		CDiskDos::GetVolumeMaxFileSize(ushort pro_fileTypeS)
{
	ulong		max_sizeL = _inherited::GetVolumeMaxFileSize(pro_fileTypeS);
	
	switch (pro_fileTypeS) {
		
		case Pro_FileType_INT:
		case Pro_FileType_BAS: {
			max_sizeL = 32767;
			break;
		}

		case Pro_FileType_BINA:
		case Pro_FileType_BIN: {
			max_sizeL = 65535;
			break;
		}
	}
	
	return max_sizeL;
}

ulong		CDiskDos::CalcBytesUsedByFile(ulong fileSize)
{
	ulong		filePlusExtents = fileSize;
	
	//	add 4 bytes to actual file size for binary files (assumed)
	fileSize += 4;

	filePlusExtents = fileSize;
		
	//	add sectors for each TS list
	filePlusExtents += sizeof(Dos_Sector) 
		* (1 + ((fileSize / sizeof(Dos_Sector)) / Dos_kMaxSectorSpecs));
	
	return filePlusExtents;
}

void			CDiskDos::SetTwirled(Boolean twirledB)
{
	Dos_VtocSector	*vtoc = GetMyEntry();
	
	if (vtoc) {
		vtoc->twirledDownB = twirledB;
		(void)FlushEntry();
	}
	
	_inherited::SetTwirled(twirledB);
}

Boolean			CDiskDos::GetTwirled(void)
{
	Dos_VtocSector	*vtoc		= NULL;
	Boolean			isTwirledB	= FALSE;
	
	vtoc = GetMyEntry();
	
	if (vtoc) {
		isTwirledB = (Boolean)vtoc->twirledDownB;
	}

	return isTwirledB;
}

static	OSErr	Dos_ZeroBlockCB(
	CDiskDos		*thiz, 
	Dos_SectorSpec	*sectorSpec, 
	Boolean			*free, 
	Boolean			*done, 
	void			*data
) {
	OSErr				err = noErr;
	
	if (*free) {
		Dos_Sector	*sectorP;
		
		err = thiz->GetSector(*sectorSpec, &sectorP);
		if (!err) {
			memclr(sectorP, sizeof(Dos_Sector));
			err = thiz->SetAndDisposeSector(*sectorSpec, sectorP);
			
			if (!err && sectorSpec->sector == 0) {
				err = thiz->Purge(TRUE);
			}
		}
	}

	return err;
}

static	OSErr	Dos_ZeroEntryCB(
	CFolderDos			*thiz, 
	Dos_DirEntry		*entry, 
	Dos_SectorSpec		sectorSpec, 
	Dos_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data
) {
	if (Dos_IsEmptyEntry(entry)) {
		memclr(entry, sizeof(Dos_DirEntry));

		thiz->i_cDisk.dos->SetSector(sectorSpec);
	}

	return noErr;
}

OSErr		CDiskDos::ZeroUnused(void)
{
	OSErr	err = noErr, err2;
	
	err = ASSERT(!i_show_deletedB);
	
	if (!err) err = FlushMemDisk(FALSE);
	
	if (!err) {
		err = ForEachBitmapSector(Dos_ZeroBlockCB, NULL);
		if (!err) err = i_rootDir.dos->Dos_ForEachEntry(Dos_ZeroEntryCB, NULL);

		err2 = FlushMemDisk(TRUE);
		if (!err) err = err2;
	}
	
	//	critical was done by FlushMemDisk() via Flush()
	err2 = Purge(FALSE);
	if (!err) err = err2;
	
	return err;
}

Gen_AllocSizeType		CDiskDos::GetAllocSize(void)
{
	Gen_AllocSizeType	allocSize;
	Dos_VtocSector		*vtoc = GetMyEntry();
	
	if (vtoc && vtoc->perDisk.sector == Dos_kSectorsPerTrack400) {
		allocSize = Gen_AllocSize_SECTORS400;
	} else {
		allocSize = Gen_AllocSize_SECTORS;
	}

	return allocSize;
}

static	OSErr	Dos_GetSectorMap(
	CDiskDos		*thiz, 
	Dos_SectorSpec	*sectorSpec, 
	Boolean			*free, 
	Boolean			*done, 
	void			*data
) {
	OSErr				err			= noErr;
	Gen_DiskBitUnion	*diskMapP	= (Gen_DiskBitUnion *)data;
	
	if (!(*free)) {
		if (thiz->GetAllocSize() == Gen_AllocSize_SECTORS400) {
			diskMapP->disk400.track[sectorSpec->track].sector[sectorSpec->sector].allocType
				= Gen_Alloc_FILE;
		} else {
			diskMapP->disk140.track[sectorSpec->track].sector[sectorSpec->sector].allocType
				= Gen_Alloc_FILE;
		}
	}

	return err;
}

typedef struct {
	ushort				*numDirS;
	Dos_SectorSpec		*dirListA0;
} Dos_GetDirMapRec;

static	OSErr			Dos_GetDirMapCB(
	CFolderDos			*thiz, 
	Dos_SectorSpec		sectorSpec, 
	Dos_DirSector		*dirSectorP, 
	Boolean				*doneBP, 
	void				*data)
{
	Dos_GetDirMapRec	*mrP = (Dos_GetDirMapRec *)data;
	
	if (mrP->dirListA0) {
		mrP->dirListA0[*mrP->numDirS] = sectorSpec;
	}
	
	(*mrP->numDirS)++;

	return noErr;
}

OSErr		CDiskDos::GetDiskDirectoryMap(
	Gen_DiskBitUnion	*diskMapP, 
	ushort				*numDirS, 
	Gen_SectorSpec		*dirListA0)
{
	Dos_GetDirMapRec	mr;
	
	mr.numDirS		= numDirS;
	mr.dirListA0	= dirListA0;
	
	return i_rootDir.dos->Dos_ForEachDirSector(Dos_GetDirMapCB, &mr);
}

#define		DISKMAP	(allocSize == Gen_AllocSize_SECTORS)			\
	? diskMapP->disk140.track[curSpec.track].sector[curSpec.sector].allocType	\
	: diskMapP->disk400.track[curSpec.track].sector[curSpec.sector].allocType

#define		MAPREC	(allocSize == Gen_AllocSize_SECTORS)			\
	? i_mapRec.map.disk140A->track[curSpec.track].sector[curSpec.sector].allocType	\
	: i_mapRec.map.disk400A->track[curSpec.track].sector[curSpec.sector].allocType
	
OSErr		CDiskDos::GetDiskSectorMap(
	Gen_DiskBitUnion	*diskMapP, 
	ushort				*numBootS, 
	Gen_SectorSpec		*bootListA0, 
	ushort				*numEmptyS, 
	Gen_SectorSpec		*emptyListA0, 
	ushort				*numMysteryS0, 
	Gen_SectorSpec		*mysteryListA0)
{
	OSErr				err = noErr;
	Gen_SectorSpec		curSpec;
	Dos_VtocSector		*vtoc = GetMyEntry();
	Gen_AllocSizeType	allocSize;
	
	if (!vtoc) err = IC_Err_READ_ILLEGAL_TRACK_SECTOR;
	
	if (!err) {
		if (vtoc->perDisk.sector == Dos_kSectorsPerTrack400) {
			allocSize = Gen_AllocSize_SECTORS400;
		} else {
			allocSize = Gen_AllocSize_SECTORS;
		}
	}

	if (!err) FOR_EACH_TRACK_SECTOR_SPEC(
		curSpec, 
		vtoc->perDisk.track, 
		vtoc->perDisk.sector
	) {
		Gen_AllocType	mapAllocType	= MAPREC;
		Gen_AllocType	diskAllocType	= DISKMAP;

		if (numMysteryS0) {				
			if (
				diskAllocType != Gen_Alloc_NONE
				&& (
					mapAllocType == Gen_Alloc_NONE
					|| mapAllocType == Gen_Alloc_MYSTERY
				)
			) {
				Boolean		takenB = FALSE;

				if (mapAllocType == Gen_Alloc_NONE) {
					ushort		allocTypeS, curSectorS;
					
					for (
						allocTypeS = Gen_Alloc_NONE;
						!takenB && allocTypeS < Gen_Alloc_NUMTYPES; 
						allocTypeS++
					) {
						for (
							curSectorS = 0;
							!takenB && curSectorS < i_diskMysteryAllocP->type[allocTypeS].totalS; 
							curSectorS++
						) {
							Gen_SectorSpec		allocSpec = i_diskMysteryAllocP->type[allocTypeS]
								.u.sectorsA[curSectorS];
								
							takenB = Dos_EqualSector(curSpec, allocSpec);
						}
					}
				}
			
				if (!takenB) {
					if (mysteryListA0) {
						mysteryListA0[*numMysteryS0] = curSpec;
					}
					
					(*numMysteryS0)++;
				}
			}
		} else {
			if (curSpec.track < 3) {
				if (
					mapAllocType == Gen_Alloc_BOOT
					|| (
						diskAllocType != Gen_Alloc_NONE
						&& mapAllocType == Gen_Alloc_NONE
					)
				) {
					if (bootListA0) {
						bootListA0[*numBootS] = curSpec;
					}
					
					(*numBootS)++;
				}
			}
			
			if (diskAllocType == Gen_Alloc_NONE) {
				if (emptyListA0) {
					emptyListA0[*numEmptyS] = curSpec;
				}
				
				(*numEmptyS)++;
			}
		}
	}
	
	return err;
}

OSErr		CDiskDos::GetEntryAlloc(
	Boolean				getAsBlocksB, 
	Gen_EntryAlloc		**sectorListH)
{
	OSErr				err = noErr;
	ushort				bootSectorsS;
	ushort				freeSectorsS;
	ushort				mystSectorsS;
	ushort				dirSectorsS;
	Gen_DiskBitUnion	vtocBitMap;
	Gen_SectorSpec		*sectorSpecP;
	
	err = ASSERT(getAsBlocksB == FALSE);
	
	if (!err) {
		*sectorListH	= (Gen_EntryAlloc *)TrackNewPtrClear(
			"entry sector header, for DOS disk", sizeof(Gen_EntryAlloc));
		
		if (*sectorListH == NULL) err = memFullErr;
	}
	
	if (!err) {
		(**sectorListH).allocSize = GetAllocSize();

		memclr(&vtocBitMap, sizeof(vtocBitMap));
		err = ForEachBitmapSector(
			Dos_GetSectorMap, &vtocBitMap);
						
		bootSectorsS = 0;
		freeSectorsS = 0;
		mystSectorsS = 0;
		if (!err) err = GetDiskSectorMap(
			&vtocBitMap, 
			&bootSectorsS, NULL, 
			&freeSectorsS, NULL, 
			NULL, NULL);
	}
	
	if (!err && bootSectorsS) {		
		sectorSpecP = (Gen_SectorSpec *)TrackNewPtrClear(
			"entry sectors, DOS boot sectors", 
			sizeof(Gen_SectorSpec) * bootSectorsS);
		
		if (sectorSpecP == NULL) err = memFullErr;

		if (!err) {
			(**sectorListH).type[Gen_Alloc_BOOT].totalS		= bootSectorsS;
			(**sectorListH).type[Gen_Alloc_BOOT].u.sectorsA	= sectorSpecP;
		}
	}

	if (!err && freeSectorsS) {		
		sectorSpecP = (Gen_SectorSpec *)TrackNewPtrClear(
			"entry sectors, DOS free sectors", 
			sizeof(Gen_SectorSpec) * freeSectorsS);
		
		if (sectorSpecP == NULL) err = memFullErr;

		if (!err) {
			(**sectorListH).type[Gen_Alloc_NONE].totalS		= freeSectorsS;
			(**sectorListH).type[Gen_Alloc_NONE].u.sectorsA	= sectorSpecP;
		}
	}

	if (!err) {
		bootSectorsS = 0;
		freeSectorsS = 0;
		mystSectorsS = 0;
		if (!err) err = GetDiskSectorMap(
			&vtocBitMap, 
			&bootSectorsS, (**sectorListH).type[Gen_Alloc_BOOT].u.sectorsA, 
			&freeSectorsS, (**sectorListH).type[Gen_Alloc_NONE].u.sectorsA,
			NULL, NULL);
	}

	if (!err) {		
		sectorSpecP = (Gen_SectorSpec *)TrackNewPtrClear(
			"entry sectors, DOS vbitmap sector", 
			sizeof(Gen_SectorSpec));
		
		if (sectorSpecP == NULL) err = memFullErr;
	}

	if (!err) {
		(**sectorListH).type[Gen_Alloc_V_BITMAP].totalS			= Dos_kNumBitMapSectors;
		(**sectorListH).type[Gen_Alloc_V_BITMAP].u.sectorsA		= sectorSpecP;
		
		*sectorSpecP = Dos_gVtocSector;

		dirSectorsS = 0;
		err = GetDiskDirectoryMap(
			&vtocBitMap, 
			&dirSectorsS, NULL);
	}

	if (!err) {		
		sectorSpecP = (Gen_SectorSpec *)TrackNewPtrClear(
			"entry sectors, DOS directory sectors", 
			sizeof(Gen_SectorSpec) * dirSectorsS);
		
		if (sectorSpecP == NULL) err = memFullErr;
	}

	if (!err) {
		(**sectorListH).type[Gen_Alloc_DIRECTORY].totalS		= dirSectorsS;
		(**sectorListH).type[Gen_Alloc_DIRECTORY].u.sectorsA	= sectorSpecP;
		
		dirSectorsS = 0;
		err = GetDiskDirectoryMap(
			&vtocBitMap, 
			&dirSectorsS, (**sectorListH).type[Gen_Alloc_DIRECTORY].u.sectorsA);

	}

	if (!err) {
		bootSectorsS = 0;
		freeSectorsS = 0;
		mystSectorsS = 0;
	
		i_diskMysteryAllocP = *sectorListH;

		if (!err) err = GetDiskSectorMap(
			&vtocBitMap, 
			&bootSectorsS, NULL, 
			&freeSectorsS, NULL, 
			&mystSectorsS, NULL);
	}

	if (!err && mystSectorsS) {
		sectorSpecP = (Gen_SectorSpec *)TrackNewPtrClear(
			"entry sectors, DOS mystery sectors", 
			sizeof(Gen_SectorSpec) * mystSectorsS);
		
		if (sectorSpecP == NULL) err = memFullErr;

		if (!err) {
			(**sectorListH).type[Gen_Alloc_MYSTERY].totalS		= mystSectorsS;
			(**sectorListH).type[Gen_Alloc_MYSTERY].u.sectorsA	= sectorSpecP;

			bootSectorsS = 0;
			freeSectorsS = 0;
			mystSectorsS = 0;
			if (!err) err = GetDiskSectorMap(
				&vtocBitMap, 
				&bootSectorsS, NULL, 
				&freeSectorsS, NULL,
				&mystSectorsS, (**sectorListH).type[Gen_Alloc_MYSTERY].u.sectorsA);
		}
	}
		
	i_diskMysteryAllocP = *sectorListH;

	if (err) {
		DisposeEntryAlloc(*sectorListH);
		*sectorListH = NULL;
	}
	
	return err;
}

OSErr		CDiskDos::GetSectorMap(Gen_AllocMap **blockMapH)
{
	OSErr		err			= noErr;
	
	*blockMapH	= (Gen_AllocMap *)TrackNewPtrClear(
		"block map header", sizeof(Gen_AllocMap));

	if (*blockMapH == NULL) {
		err = IC_Err_OUT_OF_MEMORY;
	} else {
		(**blockMapH).allocSize		= GetAllocSize();
		(**blockMapH).maxSizeS		= i_blocksInVolume;
		(**blockMapH).curSizeS		= 0;
		(**blockMapH).map.ptr		= TrackNewPtrClear(
			"sector map array", sizeof(Gen_AllocNodeRec) * (**blockMapH).maxSizeS);

		if ((**blockMapH).map.ptr == NULL) {
			DisposeBlockMap(*blockMapH);
			err = IC_Err_OUT_OF_MEMORY;
		} else {
			CEntry			*entryP;
			Dos_VtocSector	*vtoc = GetMyEntry();
			
			if (!vtoc) err = IC_Err_READ_ILLEGAL_TRACK_SECTOR;

			if (!err) {
				UpdateGlobals();
				
				entryP = i_rootDir.gen->GetIndEntry(0);

				if (entryP) {
					if (!err) err = GetEntryTopic(entryP)->O_ApplyToItems(
						O_Iterate_ALL, 
						ADFS_O_CB_COLLECT_BITMAP, 
						(O_CBDataP *)*blockMapH, 
						TRUE,	//	only do self (not sisters)
						TRUE);	//	always go deep
				}
				
				i_mapRec.map = (**blockMapH).map;

				if (!err) err = EntryDispatchCB(
					ADFS_O_CB_COLLECT_BITMAP, 
					(O_CBDataP)*blockMapH);

				i_mapRec.map.ptr = NULL;
			}
		}
	}
	
	return err;
}

char		*CDiskDos::GetName(char *buf)
{
	Dos_VtocSector	*vtoc		= NULL;
	
	buf[0] = 0;
	
	vtoc = GetMyEntry();
	
	if (vtoc) {
		if (vtoc->useVolumeName) {
			strcpy(buf, vtoc->volumeName);
		} else {
			ushort		volume	= vtoc->volumeNumber;
			
			sprintf(buf, "%hu", volume);
		}
	}	
	
	return buf;
}

void		CDiskDos::SetName(char *buf)
{
	Dos_VtocSector	*vtoc		= NULL;
	
	vtoc = GetMyEntry();
	
	if (vtoc) {
		long			strLenL;
		ushort			volNumS, curChar;
		Boolean			useStringB;

		useStringB = FALSE;
		
		strLenL = strlen(buf);
		
		if (strLenL == 0) {
			vtoc->useVolumeName = FALSE;
			GetName(buf);
		} else {
			if (strLenL <= 3) {
				for (curChar = 0; curChar < strLenL; curChar++) {
					if (!useStringB) {
						useStringB = !IS_DIGIT(buf[curChar]);
					}
				}
			} else {
				useStringB = TRUE;
			}

			if (!useStringB) {
				sscanf(buf, "%hu", &volNumS);
				
				if (volNumS > 254) {
					useStringB = TRUE;
				}
			}

			if (!useStringB) {
				vtoc->useVolumeName = FALSE;
				vtoc->volumeNumber = (Byte)volNumS;
			} else {
				vtoc->useVolumeName = TRUE;
				
				if (strLenL > Dos_kVolumeNameLen - 1) {
					strLenL = Dos_kVolumeNameLen - 1;
				}
				
				memcpy(vtoc->volumeName, buf, strLenL);
				vtoc->volumeName[strLenL] = 0;
			}
		}
		
		_inherited::SetName(buf);
		
		(void)FlushEntry();
	}	
}


OSErr		CDiskDos::NewDisk_Completion(ADFS_NewDiskCompletionRec *recP)
{
	OSErr		err = noErr;
	char		*nameZ = recP->diskNameZ, hybNameAC[256];
	
	if (recP->newDiskRecP->osType == FSType_HYB) {
		sprintf(hybNameAC, "DOS %s", recP->diskNameZ);
		recP->diskNameZ = hybNameAC;
	}
	
	err = _inherited::NewDisk_Completion(recP);
	recP->diskNameZ = nameZ;

	if (!err) {
		if (
			!recP->newDiskRecP->bootableB
			&& recP->newDiskRecP->osType == FSType_HYB
		) {
			CFolderDos	*folderP	= i_rootDir.dos;
			CEntry		*entryP;
			
			entryP = folderP->GetIndEntry(0);
			if (entryP) entryP->Delete();
		}
	}
	
	if (!err) err = ZeroUnused();
	
	return err;
}

