//	CDiskImagePro.c

#include "ADFS_O_Callbacks.h"
#include "ADFS_LogFile.h"
#include "GSOS_Icons.h"
#include "MemUtils.h"
#include "Utils.h"
#include "IC_FileIO.h"
#include "CDialogCopy.h"
#include "IC_Errors.h"
#include "ADFS_Icons.h"
#include "Pro_Utils.h"
#include "FSUtils.h"
#include "ProStructs.h"
#include "CFolderPro.h"
#include "ProFileTypes.h"
#include "CDiskPro.h"

OSErr			CDiskPro::IDiskPro(
	CDesktop		*desktop, 
	DiskImageRec	*imageRec
) {
	OSErr			err = noErr;
	Pro_DirHeader	*dirHeader;
	short			stackIndexS;
	
	i_blockBuf			= imageRec->image.proBlock;
	i_recentBlockNum	= Pro_kNoBlock;
	i_iconH				= NULL;
	
	i_blockStackIndex								= 0;
	i_blockStackA[i_blockStackIndex].ownerB			= FALSE;
	i_blockStackA[i_blockStackIndex].blockBuf		= i_blockBuf;
	i_blockStackA[i_blockStackIndex].recentBlockNum	= i_recentBlockNum;
	
	for (stackIndexS = 1; stackIndexS < kPro_BlockStackSize; stackIndexS++) {
		i_blockStackA[stackIndexS].ownerB			= FALSE;
		i_blockStackA[stackIndexS].blockBuf			= NULL;
		i_blockStackA[stackIndexS].recentBlockNum	= Pro_kNoBlock;
	}

	i_cachedBitmapBlocksRefCountS	= 0;
	i_cachedBitmapBlocks			= 0;
	
	if (sizeof(Pro_ExtendedIndexBlock) != sizeof(Pro_Block)) {
		AlertID("Forked block is wrong size!", sizeof(Pro_ExtendedIndexBlock));
	}

	if (!err) err = _inherited::IDisk(desktop, imageRec);
	
	if (!err) {
		dirHeader = GetMyEntry();
		if (!dirHeader) err = IC_Err_CANT_READ_PRODOS_BLOCK;
	}
	
	if (!err) {
		i_blocksInVolume	= GetRboShort(dirHeader->headerType.volume.totalBlocks);

		if (NewObject(i_rootDir.pro, CFolderPro, err)) {
			err = i_rootDir.pro->IFolderPro(
				this, NULL, Pro_kDirStartBlock, 0, 0);
		}
	}	
	
	return err;
}

void			CDiskPro::Dismount(void)
{
	if (!i_disposingB) {
		//	to calc checksums
		FlushMemDisk(FALSE);
		ImageRec_VolRec(i_imageRec).image.skipCheckSumB = FALSE;
		FlushMemDisk(TRUE);
	}
	
	_inherited::Dismount();
}	

void			CDiskPro::Dispose(void)
{
	_inherited::Dispose();
}

OSErr		CDiskPro::BuildFileTypeMenu(void)
{
	OSErr		err = noErr;
	
	i_fileTypeMenu = Pro_BuildFileTypeMenu();
		
	return err;
}

short		CDiskPro::MenuItemToFileType(short menuItem, ushort *auxType)
{
	short	fileType = 0;
	
	Pro_MenuItemToFileAndAux(
		i_fileTypeMenu, menuItem, &fileType, auxType);
	
	return fileType;
}

short			CDiskPro::FileTypeToMenuItem(Byte fileType, short auxType)
{
	return Pro_FileTypeToIndex(fileType, auxType);
}

char			*CDiskPro::GetName(char *buf)
{
	_inherited::GetName(buf);
	
	if (buf[0] == 0) {
		Pro_DirHeader	*dirHeader = GetMyEntry();
		
		buf[0] = 0;
		
		if (dirHeader) {
			Pro_GetDirName(dirHeader, buf);
		}
	}
	
	return buf;
}

char			*CDiskPro::GetDescription(char *buf)
{
	strcpy(buf, "ProDOS Disk");
	return buf;
}



ulong			CDiskPro::GetVolumeSize(void)
{
	ulong		bytes = (ulong)GetTotalBlocks() * Pro_kBytesPerBlock;
	
	return bytes;
}

ulong		CDiskPro::GetVolumeMaxFileSize(ushort pro_fileTypeS)
{
	//	file type doesn't matter, all files have this max limit
	return 0x00FFFFFF;
}

ulong		CDiskPro::CalcBytesUsedByFile(ulong fileSize)
{
	ulong	filePlusExtents;
	
	if (fileSize <= sizeof(Pro_Block)) {
		//	eof <= 0x200
		//	Pro_Storage_SEEDLING
		filePlusExtents = fileSize;
	} else if (fileSize <= sizeof(Pro_Block) * sizeof(Pro_HalfBlock)) {
		//	eof <= 0x20000
		//	Pro_Storage_SAPLING
		//	file gets an extra index block
		filePlusExtents = fileSize + sizeof(Pro_Block);
	} else {
		ulong	subIndexBlocksL = (fileSize >> 17);	//	div by 512 * 256 = 0x20000
		
		//	eof <= 0xFFFFFF
		//	Pro_Storage_TREE
		//	file gets an extra master index block, 
		//	plus up to 0x7F sub-index blocks
		ASSERT(subIndexBlocksL <= 0x7F);
		
		filePlusExtents = fileSize + sizeof(Pro_Block)	//	master index block
			+ (sizeof(Pro_Block) * subIndexBlocksL);	//	+ sub index blocks
	}
	
	return filePlusExtents;
}

OSErr		CDiskPro::PushBlock(void)
{
	OSErr		err = noErr;
	
	if (!IS_ImageRec_IN_MEMORY(i_imageRec)) {
		if (i_blockStackIndex == (kPro_BlockStackSize - 1)) {
			ReportErrorStr(-1, "Out of blocks on stack!");
			err = IC_Err_OUT_OF_MEMORY;
		} else {
			i_blockStackA[i_blockStackIndex].recentBlockNum = i_recentBlockNum;
			i_blockStackIndex++;
			i_blockStackA[i_blockStackIndex].blockBuf		= (Pro_Block *)TrackNewPtrClear("block stack buf", sizeof(Pro_Block));
			i_blockStackA[i_blockStackIndex].recentBlockNum = Pro_kNoBlock;
			
			if (i_blockStackA[i_blockStackIndex].blockBuf == NULL) {
				err = IC_Err_OUT_OF_MEMORY;
				ReportError(err);
				i_blockStackIndex--;
			} else {
				i_blockStackA[i_blockStackIndex].ownerB		= TRUE;
		
				i_recentBlockNum			= i_blockStackA[i_blockStackIndex].recentBlockNum;
				i_blockBuf					= i_blockStackA[i_blockStackIndex].blockBuf;
				i_imageRec->image.proBlock	= i_blockBuf;
			}
		}
	}
		
	return err;
}

OSErr		CDiskPro::PopBlock(void)
{
	OSErr		err = noErr;

	if (!IS_ImageRec_IN_MEMORY(i_imageRec)) {
		if (i_blockStackIndex == 0) {
			ReportErrorStr(-1, "Underflow blocks on stack!");
			err = IC_Err_OUT_OF_MEMORY;
		} else {
			if (i_blockStackA[i_blockStackIndex].ownerB) {
				TrackDisposePtr((Ptr)i_blockStackA[i_blockStackIndex].blockBuf);
				i_blockStackA[i_blockStackIndex].ownerB			= FALSE;
			}

			i_blockStackA[i_blockStackIndex].blockBuf		= NULL;
			i_blockStackA[i_blockStackIndex].recentBlockNum	= Pro_kNoBlock;	
			i_blockStackIndex--;
			
			i_recentBlockNum			= i_blockStackA[i_blockStackIndex].recentBlockNum;
			i_blockBuf					= i_blockStackA[i_blockStackIndex].blockBuf;
			i_imageRec->image.proBlock	= i_blockBuf;
		}
	}
	
	return err;
}

OSErr		CDiskPro::SetGenericBlock(ushort blockNumS, Gen_Block *blockP)
{
	OSErr				err			= noErr;
	Pro_Block			*prevBlockP = i_blockBuf;
	Pro_BlockNum		prevBlockS	= i_recentBlockNum;
	
	i_recentBlockNum	= blockNumS;
	i_blockBuf			= (Pro_Block *)blockP;

	err = SetBlock();

	i_recentBlockNum	= prevBlockS;
	i_blockBuf			= prevBlockP;

	return err;
}

OSErr		CDiskPro::GetGenericBlock(ushort blockNumS, Gen_Block **blockP)
{
	return GetBlock(blockNumS, (Pro_Block **)blockP);
}

OSErr		CDiskPro::GetBlock(Pro_BlockNum blockNum, Pro_Block **blockP)
{
	OSErr		err = noErr;
	
	if (blockNum >= GetTotalBlocks()) {
		err = IC_Err_READ_ILLEGAL_FILE_BLOCK;
		ReportError(err);
	} else {
		if (i_recentBlockNum != blockNum) {
		
			if (i_blockStackIndex > 0) {
				Boolean			wasOwnerB = i_blockStackA[i_blockStackIndex].ownerB;
				short			stackIndexS;
				
				i_blockStackA[i_blockStackIndex].ownerB = TRUE; //	try to own it now

				for (stackIndexS = 0; stackIndexS < i_blockStackIndex; stackIndexS++) {
					if (i_blockStackA[stackIndexS].recentBlockNum == blockNum) {

						//	is not
						i_blockStackA[i_blockStackIndex].ownerB = FALSE;	//	still not owner
						
						if (wasOwnerB) {
							//	was
							TrackDisposePtr((Ptr)i_blockStackA[i_blockStackIndex].blockBuf);
						}

						//	was not
						i_blockStackA[i_blockStackIndex].blockBuf		= i_blockStackA[stackIndexS].blockBuf;
						i_blockStackA[i_blockStackIndex].recentBlockNum	= i_blockStackA[stackIndexS].recentBlockNum;
						break;
					}
				}
				
				if (i_blockStackA[i_blockStackIndex].ownerB) {
					//	is
					
					if (wasOwnerB) {
						//	was
						i_blockStackA[i_blockStackIndex].recentBlockNum = i_recentBlockNum;
					} else {
						//	was not
						i_blockStackA[i_blockStackIndex].blockBuf		= (Pro_Block *)TrackNewPtrClear("block stack buf", sizeof(Pro_Block));
						i_blockStackA[i_blockStackIndex].recentBlockNum	= Pro_kNoBlock;
					}
				}
				
				i_recentBlockNum			= i_blockStackA[i_blockStackIndex].recentBlockNum;
				i_blockBuf					= i_blockStackA[i_blockStackIndex].blockBuf;
				i_imageRec->image.proBlock	= i_blockBuf;
			}
		}
		
		if (i_recentBlockNum != blockNum) {
			err = Pro_GetBlock(i_imageRec, blockNum, &i_blockBuf);

			if (err) ReportError(err);

			i_blockStackA[i_blockStackIndex].recentBlockNum = i_recentBlockNum = blockNum;
		}
		
		if (!err) *blockP = i_blockBuf;
	}

	return err;
}

OSErr			CDiskPro::SetBlock(void)
{
	OSErr		err = noErr;

	if (IS_ImageRec_IN_MEMORY(i_imageRec)) {
		if (i_flushMemLevel == 1) {
			err = WriteImage(i_imageRec);
		}
	} else {
		ulong		image_offsetUL = 0;
		
		switch (ImageRec_DiskType(i_imageRec)) {

			case DiskType_onDisk_DiskCopy: {
				image_offsetUL = sizeof(Disk_DiskCopy_Header);
				break;
			}

			case DiskType_onDisk_2img: {
				image_offsetUL = ImageRec_VolRec(i_imageRec).image.header.twoimg.img_start_offset;
				break;
			}
		}
		
		if (i_recentBlockNum != Pro_kNoBlock) {
			err = WriteChunk(
				i_imageRec, i_blockBuf, 
				image_offsetUL + (i_recentBlockNum * sizeof(Pro_Block)), 
				sizeof(Pro_Block)
			);
		}
	}
	
	if (err) {
		ReportError(err);
	}
	
	return err;
}

ADFS_IconType			CDiskPro::GetIconType(void)
{
	short	icon;
	
	if (IS_ImageRec_IN_MEMORY(i_imageRec)) {
		icon = ADFS_Icon_PRO_SPRO + GetSectorOrderForIcon();
	} else {
		ulong	volSize = GetVolumeSize();
		
		if (volSize <= kPro_Disk800_Size) {
			icon = ADFS_Icon_PRO_800;
		} else if (volSize <= kPro_Disk1440_Size) {
			icon = ADFS_Icon_PRO_1440;
		} else {
			icon = ADFS_Icon_PRO_HARD_DISK;
		}
	}
	
	return icon;
}

/********************************************************/
/*	volume bit map stuff								*/
/********************************************************/
OSErr			CDiskPro::GetBitMapBlocksNum(
	Pro_BlockNum	*startBlockS, 
	Pro_BlockNum	*lastBlockS, 
	ulong			*blocksInVolumeL0)
{
	OSErr			err			= noErr;
	Pro_DirHeader	*dirHeader	= NULL;
	ulong			totalBlocks	= 0;
	
	err = PushBlock();
	
	if (!err) {
		dirHeader	= GetMyEntry();
		if (!dirHeader) err = IC_Err_CANT_READ_PRODOS_BLOCK;
	}
	
	if (!err) {
		*startBlockS	= GetRboShort(dirHeader->headerType.volume.bitMap);
		totalBlocks		= GetRboShort(dirHeader->headerType.volume.totalBlocks);
		(void)PopBlock();

		*lastBlockS	= *startBlockS + (totalBlocks >> 12);
		
		if (blocksInVolumeL0) {
			*blocksInVolumeL0 = totalBlocks;
		}
	}
	
	return err;
}

OSErr			CDiskPro::CacheBitmapBlocks(void)
{
	OSErr			err				= noErr;
	
	if (i_cachedBitmapBlocksRefCountS++ == 0) {
		Pro_BlockNum	bitMapBlock;
//		Pro_BlockNum	curBlock		= 0;
//		short			blocksPushed	= 0;
		Pro_Block		*bitMapBlockP;
		Pro_BlockNum	lastBitMabBlock;
		
		err = GetBitMapBlocksNum(&bitMapBlock, &lastBitMabBlock, NULL);
		
		if (!err) {
			
			//	add one more for a buffer AFTER the last bitmap block
			lastBitMabBlock++;
			
			for (; !err && bitMapBlock <= lastBitMabBlock; bitMapBlock++) {
				err = PushBlock();
				
				if (bitMapBlock < lastBitMabBlock) {
					if (!err) err = GetBlock(bitMapBlock, &bitMapBlockP);
				}
				
				if (!err) {
					i_cachedBitmapBlocks++;
				}
			}
		}
	}
	
	return err;
}

OSErr			CDiskPro::UnCacheBitmapBlocks(void)
{
	OSErr		err = noErr;
	
	err = ASSERT(i_cachedBitmapBlocksRefCountS > 0);
	
	if (!err && --i_cachedBitmapBlocksRefCountS == 0) {
		short		lastBitMabBlock = i_cachedBitmapBlocks;
		
		for (; i_cachedBitmapBlocks; i_cachedBitmapBlocks--) {
			
			if (i_cachedBitmapBlocks < lastBitMabBlock) {
				if (!err) err = SetBlock();
			}
			
			(void)PopBlock();
		}
	}
	
	return err;
}

/****************************************************/
static	OSErr	Pro_ResetBitMapCB(
	CDiskPro		*thiz, 
	Pro_BlockNum	block, 
	Boolean			*free, 
	Boolean			*done, 
	void			*data
) {
	Pro_BlockNum	*lastBitmapBlockIndexP = (Pro_BlockNum *)data;
	
	*free = block > *lastBitmapBlockIndexP;
	*done = TRUE;
	
	return noErr;
}

enum	{
	Pro_VBM_Select_NONE, 
	Pro_VBM_Select_GET, 
	Pro_VBM_Select_GET_NEXT, 
	Pro_VBM_Select_SET, 
	Pro_VBM_Select_COUNT, 
	Pro_VBM_Select_NUMTYPES
};
typedef	short	Pro_VBM_SelectType;

typedef struct {
	Pro_VBM_SelectType		selectType;
	Pro_BlockNum			block;
	Boolean					free;
} FreeBlockCBData;

static	OSErr	FreeBlockCB(
	CDiskPro		*thiz, 
	Pro_BlockNum	block, 
	Boolean			*free, 
	Boolean			*done, 
	void			*data
) {
	OSErr				err = noErr;
	FreeBlockCBData		*cbData = (FreeBlockCBData *)data;
	
	switch (cbData->selectType) {
		
		case Pro_VBM_Select_GET: {
			if (cbData->block == block) {
				cbData->free	= *free;
				*done			= TRUE;
			}
			break;
		}
		
		case Pro_VBM_Select_GET_NEXT: {
			if (*free) {
				cbData->free	= TRUE;
				cbData->block	= block;
				*done			= TRUE;
			}
			break;
		}

		case Pro_VBM_Select_SET: {
			if (cbData->block == block) {
				*done = TRUE;
				
				if (block < 7) {
					ReportError(err = IC_Err_READ_ILLEGAL_DISK_BLOCK);
				} else {
					*free = cbData->free;
				}
			}
			break;
		}

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

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

	return err;
}

UInt32		OSBitFlipBigToHostInt32(UInt32 valL)
{
	#if TARGET_RT_LITTLE_ENDIAN
		UInt32	oldValL = valL;

		valL = 0;
		
		for (int bitS = 0; bitS < 32; bitS++) {
			valL = (valL << 1) | (oldValL & 0x01);
			oldValL >>= 1;
		}
	#endif
	
	return valL;
}

OSErr			CDiskPro::ForEachBitmapBlock(
	ForEachBitmapBlockCB	ForEachBitmapBlockUserCB, 
	void					*data)
{
	OSErr			err				= noErr;
	Pro_BlockNum	curBlock		= 0;
	Boolean			done			= FALSE;
	ulong			*curLong;
	Pro_BitMapBlock	*vBitMap;
	Pro_BlockNum	bitMapBlock, lastBitMabBlock;
	ulong			mask, loop1, loop2, totalBlocksL;
	Boolean			wasFree, isFree;

	err = GetBitMapBlocksNum(&bitMapBlock, &lastBitMabBlock, &totalBlocksL);

	for (; !done && bitMapBlock <= lastBitMabBlock; bitMapBlock++) {

		err = GetBlock(bitMapBlock, (Pro_Block **)&vBitMap);
		
		if (ForEachBitmapBlockUserCB == Pro_ResetBitMapCB) {
			memclr(vBitMap, sizeof(Pro_Block));
		}
		
		if (err) {
			done	= TRUE;
		} else {
			for (loop1 = 0; !done && loop1 < Pro_kLongsPerBlock; loop1++) {
				curLong		= &((*vBitMap)[loop1]);
				*curLong	= OSSwapBigToHostInt32(*curLong);
				mask		= 0x80000000;
				
				for (loop2 = 0; !done && loop2 < 32; loop2++) {
					isFree = wasFree = ((*curLong) & mask) != Pro_kUsedBlock;
					
					err = (*ForEachBitmapBlockUserCB)(this, curBlock, &isFree, &done, data);

					if (!err) {
						if (done) {
							if (isFree != wasFree) {
								if (isFree) {
									*curLong = (*curLong) | mask;
								} else {
									*curLong = (*curLong) & (~mask);
								}
								
								if (!i_cachedBitmapBlocks) {
									err = SetBlock();
								}
							}
							
							if (ForEachBitmapBlockUserCB == Pro_ResetBitMapCB) {
								done = FALSE;
							}
						}
						
						if (!done) {
							mask = mask >> 1;
							curBlock++;
						}
					}

					if (!done) {
						done = curBlock == totalBlocksL;
					}

					if (err) {
						done = TRUE;
					}
				}

				*curLong = OSSwapHostToBigInt32(*curLong);
			}
		}
	}

	return err;		
}

/**********************************************************/
ulong		CDiskPro::GetVolumeBytesUsed(void)
{
	OSErr				err = noErr;
	FreeBlockCBData		cbData;
	
	cbData.selectType	= Pro_VBM_Select_COUNT;
	cbData.block		= 0;
	cbData.free			= FALSE;
	
	err = PushBlock();

	if (!err) {
		OSErr		err2;
		
		err = ForEachBitmapBlock(FreeBlockCB, &cbData);

		err2 = PopBlock();
		if (!err) err = err2;
	}
	
	return cbData.block * sizeof(Pro_Block);
}

/**********************************************************/
OSErr	CDiskPro::IsBlockFree(Pro_BlockNum test_block, Boolean *is_freeBP)
{
	OSErr				err = noErr;
	FreeBlockCBData		cbData;
//	Pro_BlockNum		blockNum = 0;
	
	cbData.selectType	= Pro_VBM_Select_GET;
	cbData.free			= FALSE;
	cbData.block		= test_block;

	*is_freeBP			= 0;
	
	err = PushBlock();

	if (!err) {
		OSErr		err2;
		
		err = ForEachBitmapBlock(FreeBlockCB, &cbData);

		err2 = PopBlock();
		if (!err) err = err2;
	}
	
	if (err == noErr) {
		*is_freeBP = cbData.free;
	}
	
	return err;
}

OSErr	CDiskPro::GetFreeBlock(Pro_BlockNum *freeBlock)
{
	OSErr				err = noErr;
	FreeBlockCBData		cbData;
//	Pro_BlockNum		blockNum = 0;
	
	cbData.selectType	= Pro_VBM_Select_GET_NEXT;
	cbData.free			= FALSE;
	cbData.block		= 0;

	*freeBlock			= 0;
	
	err = PushBlock();

	if (!err) {
		OSErr		err2;
		
		err = ForEachBitmapBlock(FreeBlockCB, &cbData);

		err2 = PopBlock();
		if (!err) err = err2;
	}
	
	if (err == noErr) {
		if (cbData.free) {
			*freeBlock = cbData.block;
		} else {
			err = IC_Err_DISK_FULL;
		}
	}
	
	return err;
}

OSErr			CDiskPro::FreeBlock(Pro_BlockNum block)
{
	OSErr				err = noErr;
	FreeBlockCBData		cbData;
	
	cbData.selectType	= Pro_VBM_Select_SET;
	cbData.free			= TRUE;
	cbData.block		= block;
	
	err = PushBlock();

	if (!err) {
		OSErr		err2;
		
		err = ForEachBitmapBlock(FreeBlockCB, &cbData);

		err2 = PopBlock();
		if (!err) err = err2;
	}
	
	return err;
}

OSErr			CDiskPro::ReserveBlock(Pro_BlockNum block)
{
	OSErr				err = noErr;
	FreeBlockCBData		cbData;
	
	cbData.selectType	= Pro_VBM_Select_SET;
	cbData.free			= FALSE;
	cbData.block		= block;
	
	err = PushBlock();

	if (!err) {
		OSErr		err2;
		
		err = ForEachBitmapBlock(FreeBlockCB, &cbData);

		err2 = PopBlock();
		if (!err) err = err2;
	}
	
	return err;
}

OSErr			CDiskPro::ReserveNextFreeBlock(Pro_BlockNum *freeBlock)
{
	OSErr				err = noErr;
	FreeBlockCBData		cbData;
//	Pro_BlockNum		blockNum = 0;
	
	cbData.selectType	= Pro_VBM_Select_GET_NEXT;
	cbData.free			= FALSE;
	cbData.block		= 0;
	
	*freeBlock			= 0;
	
	err = PushBlock();

	if (!err) {
		OSErr		err2;
		
		err = ForEachBitmapBlock(FreeBlockCB, &cbData);

		if (err == noErr) {
			if (cbData.free) {
				*freeBlock = cbData.block;
			} else {
				err = IC_Err_DISK_FULL;
			}
		}
		
		if (!err) {
			cbData.selectType	= Pro_VBM_Select_SET;
			cbData.free			= FALSE;

			err = ForEachBitmapBlock(FreeBlockCB, &cbData);
		}
	
		err2 = PopBlock();
		if (!err) err = err2;
	}

	return err;
}

/**********************************************************/
OSErr			CDiskPro::FlushEntry(void)
{
	OSErr		err = noErr;
	
	if (i_myEntryCachedB) {
		if (!err) err = PushBlock();
						
		if (!err) {
			OSErr				err2;
			Pro_DirBlock		*dirBlock;
			OSErr				err = GetBlock(Pro_kDirStartBlock, (Pro_Block **)&dirBlock);
						
			if (!err) {
				dirBlock->blockType.key.header = i_myEntry;
				err = SetBlock();
			}

			err2 = PopBlock();
			if (!err) err = err2;
		}
	}
	
	return err;
}

Pro_DirHeader	*CDiskPro::GetMyEntry(void)
{
	Pro_DirHeader	*entryP = NULL;
	
	if (!i_myEntryCachedB) {
		Pro_DirBlock	*dirBlock;
		OSErr			err = GetBlock(Pro_kDirStartBlock, (Pro_Block **)&dirBlock);
		
		if (!err) {
			i_myEntry			= dirBlock->blockType.key.header;
			i_myEntryCachedB	= TRUE;
			entryP				= &i_myEntry;
		}
	} else {
		entryP = &i_myEntry;
	}
	
	return entryP;
}

DateTimeRec		*CDiskPro::GetCreatedTime(DateTimeRec *dt)
{
	Pro_DirHeader		*dirEntry = GetMyEntry();
	
	if (dirEntry) {
		Pro_DateTime		dateTime;

		dateTime = dirEntry->createdTime;
		Pro_GetDateTime(&dateTime, dt);
	} else {
		_inherited::GetCreatedTime(dt);
	}
	
	return dt;
}

DateTimeRec		*CDiskPro::GetModifiedTime(DateTimeRec *dt)
{
	Pro_DirHeader		*dirEntry = GetMyEntry();
	
	if (dirEntry) {
		Pro_DateTime		dateTime;

		dateTime = dirEntry->modifiedTime;
		Pro_GetDateTime(&dateTime, dt);
	} else {
		_inherited::GetModifiedTime(dt);
	}
	
	return dt;
}

void			CDiskPro::SetCreatedTime(DateTimeRec *dt)
{
	Pro_DateTime		dateTime;
	Pro_DirHeader		*dirEntry = GetMyEntry();

	Pro_SetDateTime(dt, &dateTime);
	
	if (dirEntry) {
		dirEntry->createdTime = dateTime;
		FlushEntry();
	}
}

void			CDiskPro::SetModifiedTime(DateTimeRec *dt)
{
	Pro_DateTime		dateTime;
	Pro_DirHeader		*dirEntry = GetMyEntry();

	Pro_SetDateTime(dt, &dateTime);
	
	if (dirEntry) {
		dirEntry->modifiedTime = dateTime;
		FlushEntry();
	}
}

OSErr			CDiskPro::FlushMemDisk(Boolean flushB)
{
	return _inherited::FlushMemDisk(flushB);
}

void			CDiskPro::GetAccessBits(Gen_AccessBits *bits)
{
	Pro_DirHeader	*dirHeader = GetMyEntry();
	
	if (dirHeader) {
		*bits = *(Gen_AccessBits *)&dirHeader->access;
	}
}

void			CDiskPro::SetAccessBits(Gen_AccessBits *bits)
{
	Pro_DirHeader	*dirHeader = GetMyEntry();
	
	if (dirHeader) {
		dirHeader->access = *(Pro_AccessPriv *)bits;
		FlushEntry();
	}
	
	_inherited::SetAccessBits(bits);
}

void			CDiskPro::SetTwirled(Boolean twirledB)
{
	OSErr			err = noErr;
	
	#if Pro_kUseOldTwirlDown
		Pro_DirBlock	*dirBlockP;
		
		err	= GetBlock(Pro_kDirStartBlock, (Pro_Block **)&dirBlockP);
		
		if (!err) {
			dirBlockP->twirledDownB = twirledB;
		}
		
		if (!err && !IsLocked()) {
			(void)SetBlock();
		}
	#else
		Pro_DirHeader	*dirHeaderP = GetMyEntry();
		
		if (dirHeaderP) {
			dirHeaderP->access.twirledDownB = twirledB;
			
			if (!IsLocked()) {
				FlushEntry();
			}
		}
	#endif
	
	_inherited::SetTwirled(twirledB);
}

Boolean			CDiskPro::GetTwirled(void)
{
	Boolean			twirledB	= FALSE;

	#if Pro_kUseOldTwirlDown
		Pro_DirBlock	*dirBlockP;
		OSErr			err			= GetBlock(Pro_kDirStartBlock, (Pro_Block **)&dirBlockP);
		
		if (!err) {
			twirledB = dirBlockP->twirledDownB;
		}
	#else
		Pro_DirHeader	*dirHeaderP = GetMyEntry();
		
		if (dirHeaderP) {
			twirledB = dirHeaderP->access.twirledDownB;
		}
	#endif
	
	return twirledB;
}

void		CDiskPro::SetName(char *buf)
{
	Pro_DirHeader	*dirHeader = GetMyEntry();
	
	if (dirHeader) {
		Pro_SetDirName(dirHeader, buf);
		Pro_GetDirName(dirHeader, buf);
		_inherited::SetName(buf);
		FlushEntry();
	}
}

static	OSErr	Pro_ZeroBlockCB(
	CDiskPro		*thiz, 
	Pro_BlockNum	block, 
	Boolean			*free, 
	Boolean			*done, 
	void			*data
) {
	OSErr				err = noErr;
	
	if (*free) {
		err = thiz->PushBlock();

		if (!err) {
			OSErr			err2;
			Pro_Block		*blockP;
			CDialogCopy		*copyDialogP = (CDialogCopy *)data;
			
			err = thiz->GetBlock(block, &blockP);
			if (!err) {
				memclr(blockP, sizeof(Pro_Block));
				thiz->SetBlock();
			}
		
			if (copyDialogP) {
				//	error means I'm done
				//if ((block & 0xF) == 0) {
					err = copyDialogP->SetProgress(block);
				//}
			}

			err2 = thiz->PopBlock();
			if (!err) err = err2;
		}
	}

	return err;
}

OSErr	ZeroEntryCB(
	CFolderPro			*thiz, 
	Pro_DirEntry		*entry, 
	Pro_BlockNum		block, 
	Pro_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data);
	
OSErr	ZeroEntryCB(
	CFolderPro			*thiz, 
	Pro_DirEntry		*entry, 
	Pro_BlockNum		block, 
	Pro_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data)
{
	OSErr				err			= noErr;

	/*
		ForEachEntry() special cases for this particlar callback
		and callse SetBlock().  eventually I should break up
		ForEachEntry() into ForEachDirBlock() and ForEachEntry()
		and provide a way to say this dir item is now dirty
		
		don't forget that CEntry caches the entry info too
	*/
	
	memclr(entry, sizeof(Pro_DirEntry));

	return err;
}

OSErr		CDiskPro::ZeroUnused(void)
{
	OSErr			err = noErr;
	OSErr			err2;
	Pro_DirHeader	*dirHeader;
	
	(void)FlushMemDisk(FALSE);

	if (!err) err = CacheBitmapBlocks();
	if (!err) {
		dirHeader		= GetMyEntry();
		if (!dirHeader) err = IC_Err_CANT_READ_PRODOS_BLOCK;
	}
	
	if (!err) {
		Pro_BlockNum	totalBlocks		= GetRboShort(dirHeader->headerType.volume.totalBlocks);
		CDialogCopy		*copyDialogP	= ShowCopyDialog();
		char			buf[256];
		
		if (copyDialogP) {
			copyDialogP->SetItemRemain(0);
			copyDialogP->SetMaxProgress(totalBlocks);
			copyDialogP->SetProgStrings("Zeroes", "Space", GetName(buf));

			strcpy(
				copyDialogP->i_itemStrTable[DCpy_ItemStr_ITEM_REMAIN_STAT].itemStr, 
				"Disks remaining to Zeroed:");

			strcpy(
				copyDialogP->i_itemStrTable[DCpy_ItemStr_BYTES_COPIED_STAT].itemStr, 
				"Blocks Zeroed:");
			
			copyDialogP->SetTitle("Zeroing Unused Blocks");
		}

		err = ForEachBitmapBlock(Pro_ZeroBlockCB, copyDialogP);
		
		if (!err) err = i_rootDir.pro->ForEachEntry(ZeroEntryCB, NULL);

		err2 = UnCacheBitmapBlocks();
		if (!err) err = err2;
		
		copyDialogP->HideCopyDialog();
	}
	
	err2 = FlushMemDisk(TRUE);
	if (!err) err = err2;
	
	return err;
}

OSErr		CDiskPro::ExpandToSize(long bytesOnDiskL)
{
	OSErr				err = noErr;
	Pro_DirHeader		*dirHeader;
	long				img_start_offset	= 0;
	
	ASSERT(ImageRec_OrigOrder(i_imageRec) == FSType_PRO);
	ASSERT(i_imageRec->curOrder == FSType_PRO);
	ASSERT(i_imageRec->osType == FSType_PRO);

	if (!err) {
		//	Convert from memory to disk
		
		switch (ImageRec_DiskType(i_imageRec)) {

			case DiskType_inMemory_Raw: {
				ImageRec_DiskType(i_imageRec) = DiskType_onDisk_Raw;
				break;
			}

			case DiskType_inMemory_DiskCopy: {
				ImageRec_DiskType(i_imageRec) = DiskType_onDisk_DiskCopy;
				img_start_offset = sizeof(Disk_DiskCopy_Header);
				break;
			}

			case DiskType_inMemory_2img: {
				ImageRec_DiskType(i_imageRec) = DiskType_onDisk_2img;
				img_start_offset = ImageRec_VolRec(i_imageRec).image.header.twoimg.img_start_offset;
				break;
			}

			default: {
				ASSERT(0);
				break;
			}
		}
		
		TrackDisposePtr((Ptr)i_imageRec->image.pro);
		err = MemError();
	}
	
	if (!err) {
		i_imageRec->image.proBlock = (Pro_Block *)TrackNewPtr("block buf (expand)", sizeof(Pro_Block));
		i_blockBuf			= i_imageRec->image.proBlock;
		i_recentBlockNum	= Pro_kNoBlock;
		i_blockStackA[i_blockStackIndex].blockBuf		= i_blockBuf;
		i_blockStackA[i_blockStackIndex].recentBlockNum	= i_recentBlockNum;
		err = MemError();
	}

	if (!err) err = FSpSetEOF(
		&ImageRec_VolRec(i_imageRec).image.fileSpec, (long)bytesOnDiskL + img_start_offset);
		
	dirHeader = GetMyEntry();
	if (!dirHeader) err = IC_Err_CANT_READ_PRODOS_BLOCK;

	if (!err) {
		//	must be less than 32 megs
		ASSERT(bytesOnDiskL <= Pro_kBlocksPerDisk32M * Pro_kBytesPerBlock);
		i_blocksInVolume = bytesOnDiskL / Pro_kBytesPerBlock;
		dirHeader->headerType.volume.totalBlocks = SetRboShort(i_blocksInVolume);
		FlushEntry();
	}
	
	if (!err) err = CacheBitmapBlocks();
	if (!err) {
		OSErr			err2;
		Pro_BlockNum	bitMapBlock, lastBitMabBlock;
		
		err = GetBitMapBlocksNum(&bitMapBlock, &lastBitMabBlock, NULL);
		if (!err) err = ForEachBitmapBlock(Pro_ResetBitMapCB, &lastBitMabBlock);

		err2 = UnCacheBitmapBlocks();
		if (!err) err = err2;
	}
		
	if (!err) {
		ASSERT(!IS_ImageRec_IN_MEMORY(i_imageRec));

		//	may want to just copy it to the end of the file??
		if (ImageRec_DiskType(i_imageRec) == DiskType_onDisk_DiskCopy) {
			ImageRec_VolRec(i_imageRec).image.header.diskCopy.dataSize 		= bytesOnDiskL;
			ImageRec_VolRec(i_imageRec).image.header.diskCopy.tagSize			= 0;
			ImageRec_VolRec(i_imageRec).image.header.diskCopy.tagChecksum		= 0;
		} else if (ImageRec_DiskType(i_imageRec) == DiskType_onDisk_2img) {
			ASSERT(ImageRec_VolRec(i_imageRec).image.header.twoimg.format == IMG_Format_PRO);
			ImageRec_VolRec(i_imageRec).image.header.twoimg.blocks			= bytesOnDiskL / Pro_kBytesPerBlock;
			ImageRec_VolRec(i_imageRec).image.header.twoimg.img_len			= bytesOnDiskL;
			ImageRec_VolRec(i_imageRec).image.header.twoimg.comment			= 0;
			ImageRec_VolRec(i_imageRec).image.header.twoimg.comment_len		= 0;
			ImageRec_VolRec(i_imageRec).image.header.twoimg.creator_data		= 0;
			ImageRec_VolRec(i_imageRec).image.header.twoimg.creator_data_len	= 0;
		}
	}
	
	if (!err) err = WriteImage(i_imageRec);
	
	UnCacheFolderSizes();
	
	return err;
}


ushort			CDiskPro::GetSectorsPerBlock(void)
{
	return Pro_kSectorsPerBlock;
}

ushort			CDiskPro::GetBlocksPerTrack(void)
{
	return Pro_kBlocksPerTrack;
}

typedef struct {
	Boolean			getAsBlocksB;
	Boolean			mysteryB;
	Pro_BlockNum	*numFreeSP;
	Pro_BlockNum	*blockA0;
} Pro_GetBlockMapRec;

static	OSErr	Pro_GetBlockMap(
	CDiskPro		*thiz, 
	Pro_BlockNum	block, 
	Boolean			*free, 
	Boolean			*done, 
	void			*data
) {
	Pro_GetBlockMapRec		*blockMapP = (Pro_GetBlockMapRec *)data;
	
	if (blockMapP->mysteryB) {
		Gen_AllocType		allocType;
		
		if (blockMapP->getAsBlocksB) {
			ASSERT(thiz->i_mapRec.map.blockA);
			allocType = thiz->i_mapRec.map.blockA[block].allocType;
		} else {
			ushort	trackS, sectorsA[Pro_kSectorsPerBlock];
			
			ASSERT(thiz->i_mapRec.map.disk140A);
			thiz->BlockToTrackSectors(block, &trackS, sectorsA);
			allocType = thiz->i_mapRec.map.disk140A->track[trackS].sector[sectorsA[0]].allocType;
		}
		
		if (
			!(*free)
			&& (
				allocType == Gen_Alloc_NONE
				|| allocType == Gen_Alloc_MYSTERY
			)
		) {
			Boolean		takenB = FALSE;
			
			if (allocType == Gen_Alloc_NONE) {
				ushort		allocTypeS, curBlockS;
				
				for (
					allocTypeS = Gen_Alloc_NONE;
					!takenB && allocTypeS < Gen_Alloc_NUMTYPES; 
					allocTypeS++
				) {
					for (
						curBlockS = 0;
						!takenB && curBlockS < thiz->i_diskMysteryAllocP->type[allocTypeS].totalS; 
						curBlockS++
					) {
						takenB = thiz->i_diskMysteryAllocP->type[allocTypeS]
							.u.short_blocksA[curBlockS] == block;
					}
				}
			}

			if (!takenB) {
				if (blockMapP->blockA0) {
					blockMapP->blockA0[*(blockMapP->numFreeSP)] = block;
				}
				
				(*(blockMapP->numFreeSP))++;
			}
		}
	} else {
		if (*free) {
			if (blockMapP->blockA0) {
				blockMapP->blockA0[*(blockMapP->numFreeSP)] = block;
			}
			
			(*(blockMapP->numFreeSP))++;
		}
	}
	
	return noErr;
}

#define		GetEmptyBlocks(_max, _list)		GetUnAllocBlocks(getAsBlocksB, FALSE, _max, _list)
#define		GetMysteryBlocks(_max, _list)	GetUnAllocBlocks(getAsBlocksB, TRUE, _max, _list)

OSErr		CDiskPro::GetUnAllocBlocks(
	Boolean			getAsBlocksB,
	Boolean			mysteryB, 
	Pro_BlockNum	*maxEmptyS, 
	Pro_BlockNum	*blockListA)
{
	OSErr					err = noErr;
	Pro_GetBlockMapRec		blockMapRec;
	
	blockMapRec.getAsBlocksB	= getAsBlocksB;
	blockMapRec.mysteryB		= mysteryB;
	blockMapRec.numFreeSP		= maxEmptyS;
	blockMapRec.blockA0			= blockListA;
	err = ForEachBitmapBlock(Pro_GetBlockMap, &blockMapRec);
	
	return err;
}

OSErr		CDiskPro::GetEntryAlloc(
	Boolean			getAsBlocksB, 
	Gen_EntryAlloc	**sectorListH)
{
	OSErr			err = noErr;
	Pro_BlockNum	startBlockS, lastBlockS, totalBlocksS;
	Pro_BlockNum	*blockNumP, curBlockS, maxEmptyS;
	
	*sectorListH	= (Gen_EntryAlloc *)TrackNewPtrClear(
		"entry sectors, for disk", sizeof(Gen_EntryAlloc));
	
	if (*sectorListH == NULL) err = memFullErr;
	
	if (!err) {		
		(**sectorListH).allocSize = getAsBlocksB ? Gen_AllocSize_SHORT : Gen_AllocSize_SECTORS;
		
		blockNumP = (Pro_BlockNum *)TrackNewPtrClear(
			"entry sectors, boot blocks", 
			sizeof(Pro_BlockNum) * Pro_kNumBootBlocks);
		
		if (blockNumP == NULL) err = memFullErr;
	}
	
	if (!err) {
		(**sectorListH).type[Gen_Alloc_BOOT].totalS	= Pro_kNumBootBlocks;
		(**sectorListH).type[Gen_Alloc_BOOT].u.short_blocksA	= blockNumP;
		for (curBlockS = 0; curBlockS < Pro_kNumBootBlocks; curBlockS++) {
			blockNumP[curBlockS] = curBlockS;
		}

		blockNumP = (Pro_BlockNum *)TrackNewPtrClear(
			"entry sectors, directory blocks", 
			sizeof(Pro_BlockNum) * Pro_kNumDirBlocks);

		if (blockNumP == NULL) err = memFullErr;
	}
	
	if (!err) {
		(**sectorListH).type[Gen_Alloc_DIRECTORY].totalS		= Pro_kNumDirBlocks;
		(**sectorListH).type[Gen_Alloc_DIRECTORY].u.short_blocksA	= blockNumP;
		for (curBlockS = 0; curBlockS < Pro_kNumDirBlocks; curBlockS++) {
			blockNumP[curBlockS] = curBlockS + Pro_kNumBootBlocks;
		}
	}

	if (!err) err = GetBitMapBlocksNum(&startBlockS, &lastBlockS, NULL);
			
	if (!err) {
		totalBlocksS = (lastBlockS - startBlockS + 1);
		
		blockNumP = (Pro_BlockNum *)TrackNewPtrClear(
			"entry sectors, bitmap blocks", 
			sizeof(Pro_BlockNum) * totalBlocksS);
		
		if (blockNumP == NULL) err = memFullErr;
	}
	
	if (!err) {
		(**sectorListH).type[Gen_Alloc_V_BITMAP].totalS		= totalBlocksS;
		(**sectorListH).type[Gen_Alloc_V_BITMAP].u.short_blocksA	= blockNumP;
		for (curBlockS = 0; curBlockS < totalBlocksS; curBlockS++) {
			blockNumP[curBlockS] = curBlockS + startBlockS;
		}
	}
	
	maxEmptyS = 0;
	if (!err) err = GetEmptyBlocks(&maxEmptyS, NULL);
			
	if (!err && maxEmptyS) {
		blockNumP = (Pro_BlockNum *)TrackNewPtrClear(
			"entry sectors, free blocks", 
			sizeof(Pro_BlockNum) * maxEmptyS);
		
		if (blockNumP == NULL) err = memFullErr;

		if (!err) {
			(**sectorListH).type[Gen_Alloc_NONE].totalS	= maxEmptyS;
			(**sectorListH).type[Gen_Alloc_NONE].u.short_blocksA	= blockNumP;

			maxEmptyS = 0;
			if (!err) err = GetEmptyBlocks(&maxEmptyS, blockNumP);
		}
	}

	i_diskMysteryAllocP = *sectorListH;
	maxEmptyS = 0;
	
	if (!err) err = GetMysteryBlocks(&maxEmptyS, NULL);
	
	if (!err && maxEmptyS) {
		blockNumP = (Pro_BlockNum *)TrackNewPtrClear(
			"entry sectors, mystery blocks", 
			sizeof(Pro_BlockNum) * maxEmptyS);
		
		if (blockNumP == NULL) err = memFullErr;

		if (!err) {
			(**sectorListH).type[Gen_Alloc_MYSTERY].totalS			= maxEmptyS;
			(**sectorListH).type[Gen_Alloc_MYSTERY].u.short_blocksA	= blockNumP;

			maxEmptyS = 0;
			if (!err) err = GetMysteryBlocks(&maxEmptyS, blockNumP);
		}
	}	
	
	i_diskMysteryAllocP = NULL;

	if (!err && !getAsBlocksB) {
		err = EntryBlocksToSectors(*sectorListH);
	}
	
	if (err) {
		DisposeEntryAlloc(*sectorListH);
		*sectorListH = NULL;
	}
	
	return err;
}


Boolean					CDiskPro::SupportsForks(void)
{
	return TRUE;
}

Boolean			CDiskPro::IsLocked(void)
{
	OSErr		err = noErr;
	Boolean		isLockedB	= FALSE;
	
	err = PushBlock();

	if (!err) {
		OSErr			err2;
		
		isLockedB = _inherited::IsLocked();
		
		err2 = PopBlock();
		if (!err) err = err2;
	}
	
	if (err) ReportError(err);
	
	return isLockedB;
}

OSErr		CDiskPro::UpdateVolDirVers(void)
{
	OSErr				err = noErr;
	Pro_DirHeader		*dirEntry = GetMyEntry();

	if (dirEntry) {
		dirEntry->version = 0x05;
		dirEntry->access.backup = 1;
		err = FlushEntry();
	}
	
	return noErr;
}

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

	if (!err) {
		CFolderPro	*folderP	= i_rootDir.pro;
		CCopyTree	*copyDestP	= NULL;
		CCopyTree	*copyTreeP	= NULL;
		DateTimeRec	nowTime;
		
		UpdateVolDirVers();

		//	name the disk				
		GetTime(&nowTime);
		SetCreatedTime(&nowTime);
		
		if (recP->newDiskRecP->bytesOnDiskL > Gen_kBytesPerDisk) {

			if (recP->newDiskRecP->bootableB) {
				recP->copyDialogP->IncrementProgress(FALSE, 
					gProgStrs[ADFS_NewDiskProg_SAVE_BOOT]);

				if (!err) err = GetNewCopyTree(
					CCT_Copy_ENTRY, (Ptr)folderP, &copyDestP);
					
				if (!err) err = GetNewCopyTree(
					CCT_Copy_META_TREE, (Ptr)copyDestP, &copyTreeP);
				
				if (!err) {
					if (!err) err = copyTreeP->AddEntry(
						NULL, CCT_Copy_ENTRY, 
						(Ptr)folderP->GetIndEntry(0), NULL);
					
					if (!err) err = copyTreeP->AddEntry(
						NULL, CCT_Copy_ENTRY, 
						(Ptr)folderP->GetIndEntry(1), NULL);
						
					if (!err) err = copyTreeP->AddEntry(
						NULL, CCT_Copy_ENTRY, 
						(Ptr)folderP->GetIndEntry(2), NULL);
						
					if (!err) err = copyTreeP->Copy(FALSE);

					copyTreeP->Dispose();
					copyTreeP = NULL;
				}
			}
		}

		if (!recP->newDiskRecP->bootableB || recP->newDiskRecP->bytesOnDiskL > Gen_kBytesPerDisk) {
			CEntry		*entryP;
			
			recP->copyDialogP->IncrementProgress(FALSE, gProgStrs[ADFS_NewDiskProg_DELETE_BOOT]);
			
			if (recP->newDiskRecP->osType == FSType_HYB) {
				entryP = folderP->GetIndEntry(4);
				if (entryP) entryP->Delete();

				entryP = folderP->GetIndEntry(3);
				if (entryP) entryP->Delete();
			}
			
			entryP = folderP->GetIndEntry(2);
			if (entryP) entryP->Delete();
			
			entryP = folderP->GetIndEntry(1);
			if (entryP) entryP->Delete();
			
			entryP = folderP->GetIndEntry(0);
			if (entryP) entryP->Delete();
			
			if (recP->newDiskRecP->bytesOnDiskL == Gen_kBytesPerDisk) {
				recP->copyDialogP->IncrementProgress(FALSE, gProgStrs[ADFS_NewDiskProg_ZERO]);
				ZeroUnused();
			}
		}
		
		if (!err && recP->newDiskRecP->bytesOnDiskL > Gen_kBytesPerDisk) {
			recP->copyDialogP->IncrementProgress(FALSE, gProgStrs[ADFS_NewDiskProg_EXPAND]);

			ASSERT(!recP->newDiskRecP->format.nibblizedB);
			err = ExpandToSize(recP->newDiskRecP->bytesOnDiskL);
			
			if (!err) {
				if (recP->newDiskRecP->bootableB) {
					recP->copyDialogP->IncrementProgress(FALSE, gProgStrs[ADFS_NewDiskProg_RESTORE_BOOT]);
					err = copyDestP->Copy(FALSE);
				}
			}
				
			if (!err) {
				recP->copyDialogP->IncrementProgress(FALSE, gProgStrs[ADFS_NewDiskProg_UPDATE_TYPE]);
				err = UpdateFileType(
					i_imageRec, 
					&recP->newDiskRecP->format, 
					recP->newDiskRecP->bytesOnDiskL);
			}
		}
		
		if (copyDestP) copyDestP->Dispose();
	}
	
	return err;
}

Boolean		CDiskPro::IsDeletedEntry(
	Pro_DirEntry *entryP, 
	ulong		 blocksInVolumeL, 
	Pro_BlockNum headerBlockS)
{
	OSErr		err			= noErr;
	Boolean		deletedB	= TRUE;
	
	err = PushBlock();

	if (!err) {
		OSErr	err2;
		
		deletedB = Pro_IsDeleted(
			i_imageRec, 
			entryP, 
			blocksInVolumeL, 
			headerBlockS);
		
		err2 = PopBlock();
		if (!err) err = err2;
	}

	return deletedB;
}

Boolean		CDiskPro::IsValidEntry(
	Pro_DirEntry *entryP, 
	ulong		 blocksInVolumeL, 
	Pro_BlockNum headerBlockS)
{
	OSErr		err		= noErr;
	Boolean		validB	= TRUE;
	
	err = PushBlock();

	if (!err) {
		OSErr	err2;
		
		validB = Pro_IsValid(
			i_imageRec, 
			entryP, 
			blocksInVolumeL, 
			headerBlockS);
		
		err2 = PopBlock();
		if (!err) err = err2;
	}

	return validB;
}

void		CDiskPro::AddIconFile(CFilePro *fileP)
{
	GSOS_AddIconFile(fileP);
}

Boolean		CDiskPro::HasCustomIcon(ushort fileType, ushort auxType, char *nameZ)
{
	return GSOS_HasCustomIcon(fileType, auxType, nameZ);
}

Handle		CDiskPro::GetCustomIcon(ushort fileType, ushort auxType, char *nameZ)
{
	return GSOS_GetCustomIcon(fileType, auxType, nameZ);
}

void		CDiskPro::UpdateMenus(void)
{
	_inherited::UpdateMenus();
	
	EnableCommand(cmdDumpDisk);
}

void		CDiskPro::DumpDisk(void)
{
	Pro_DumpFolderRec	dumpRec;
	OSErr				err;

	dumpRec.levelS = 0;
	
	if (gLogfile == NULL) {
		ADFS_OpenLogFile();
	}
	
	err = i_rootDir.pro->DumpFolder(&dumpRec);
	
	ADFS_Log_Tabs(0);

	ADFS_CloseLogFile();
	ADFS_LaunchLogFile();
}

Boolean		CDiskPro::DoCommand(long command)
{
	Boolean		handledB = TRUE;
	
	switch (command) {
	
		case cmdDumpDisk: {
			DumpDisk();
			break;
		}

		default: {
			handledB = _inherited::DoCommand(command);
			break;
		}
	}

	return handledB;
}
