//	CFilePro.c

#include <stdio.h>
#include "MemUtils.h"
#include "CDiskMapWindow.h"
#include "Pro_Utils.h"
#include "ADFS_Strings.h"
#include "Utils.h"
#include "CCopyTree.h"
#include "ADFS_Icons.h"
#include "ProFileTypes.h"
#include "CFolderPro.h"
#include "CDiskPro.h"
#include "CFilePro.h"

OSErr		CFilePro::IFilePro(
	CDiskPro			*cDisk, 
	CFolderPro			*cParentFolderPro, 
	Pro_BlockNum		block, 
	Pro_EntryIndex		diskLocDirEntryIndex,	//	relative to cur dir sector
	Pro_EntryIndex		directoryIndex			//	relative to entire directory
) {
	OSErr				err = noErr;
	DiskLocSpecUnion	locSpec;
	
	locSpec.pro = SetRboShort(block);

	memclr(&i_dataForkEntry, sizeof(i_dataForkEntry));
	
	err = _inherited::IFile(
		cDisk, 
		cParentFolderPro, 
		locSpec, 
		diskLocDirEntryIndex, 
		directoryIndex);
	
	if (GetFileType() == Pro_FileType_ICN) {
		i_cDisk.pro->AddIconFile(this);
	}
	
	return err;
}

OSErr			CFilePro::FlushEntry(void)
{
	OSErr		err = noErr;
	
	if (i_myEntryCachedB) {
		if (!err) err = i_cDisk.pro->PushBlock();
						
		if (!err) {
			OSErr				err2;
			Pro_DirEntry		*entryP = NULL;
			
			entryP = ((CFolderPro *)GetParentFolder())->GetEntry(
				GetRboShort(i_diskLoc.pro), 
				i_diskLocDirEntryIndex);
			if (entryP == NULL) err = IC_Err_ENTRY_NOT_FOUND;
			
			if (!err) {
				*entryP = i_myEntry;
				err = i_cDisk.pro->SetBlock();
			}

			err2 = i_cDisk.pro->PopBlock();
			if (!err) err = err2;
		}
	}
	
	return err;
}

Pro_DirEntry	*CFilePro::GetMyEntry(void)
{
	Pro_DirEntry		*entryP = NULL;
	
	if (!i_myEntryCachedB) {
		OSErr		err = noErr;
		
		if (!err) err = i_cDisk.pro->PushBlock();
		if (!err) {
			OSErr				err2;
			CFolderPro			*folderP = (CFolderPro *)GetParentFolder();

			err = ASSERT(folderP);
			
			if (!err) {
				entryP = folderP->GetEntry(
					GetRboShort(i_diskLoc.pro), i_diskLocDirEntryIndex);
				if (entryP == NULL) err = IC_Err_ENTRY_NOT_FOUND;
			}
			
			if (!err) {
				i_myEntry			= *entryP;
				i_myEntryCachedB	= TRUE;
				entryP				= &i_myEntry;
			}

			err2 = i_cDisk.pro->PopBlock();
			if (!err) err = err2;
		}
	} else {
		entryP = &i_myEntry;
	}
	
	return entryP;
}

char		*CFilePro::GetName(char *buf)
{
	_inherited::GetName(buf);
	
	if (buf[0] == 0) {
		Pro_DirEntry		*dirEntry = GetMyEntry();
		
		buf[0] = 0;
		
		if (dirEntry) {
			Pro_GetFileName(dirEntry, buf);
		}
	}
	
	return buf;
}

void		CFilePro::SetName(char *buf)
{
	Pro_DirEntry		*dirEntry = GetMyEntry();
	
	if (dirEntry) {
		Pro_SetFileName(dirEntry, buf);
		Pro_GetFileName(dirEntry, buf);
		_inherited::SetName(buf);
		FlushEntry();
	}
}

ulong		CFilePro::GetPhysicalSize(void)
{
	if (i_physical_sizeL == 0) {
		Pro_DirEntry		*entryP	= GetMyEntry();
//		ulong				sizeL		= 0;
			
		if (entryP) {
			Pro_BlockNum	startBlockS;
			ushort			storageType;
			Pro_BlockNum	numFileBlocksS;
			Pro_BlockNum	numExtentBlocksS;

			startBlockS 		= GetRboShort(entryP->key);
			storageType 		= GetStorageType(entryP);
			numFileBlocksS		= 0;
			numExtentBlocksS	= 0;

			if (GetFileBlocks(
				startBlockS, storageType,
				NULL, &numFileBlocksS, 
				NULL, &numExtentBlocksS) != noErr)
			{
				numFileBlocksS		= GetRboShort(entryP->blocksUsed);
				numExtentBlocksS	= 0;
			}
			
			//	includes info for extended (forked) files
			i_physical_sizeL = (ulong)(numFileBlocksS + numExtentBlocksS) * (ulong)Pro_kBytesPerBlock;
		}
	}
	
	return i_physical_sizeL;
}

ulong		CFilePro::GetLogicalSize(void)
{
	if (i_logical_sizeL == 0) {
		Pro_DirEntry		*dirEntry	= GetMyEntry();
//		ulong				fileSizeL	= 0;
		
		if (dirEntry) {
			if (GetStorageType(dirEntry) == Pro_Storage_FORKED) {
				OSErr						err;
				Pro_ExtendedIndexBlock		*extIndBlockP;
				
				err = i_cDisk.pro->GetBlock(
					GetRboShort(dirEntry->key), 
					(Pro_Block **)&extIndBlockP);
				
				if (!err) {
					i_logical_sizeL = GetRbo3Bytes(extIndBlockP->data.eof) 
						+ GetRbo3Bytes(extIndBlockP->resource.eof);
				} else {
					i_logical_sizeL = 1;
				}
			} else {
				i_logical_sizeL = GetRbo3Bytes(dirEntry->eof);
			}
		}
	}

	return i_logical_sizeL;
}

char		*CFilePro::GetDescription(char *buf)
{
	if (IsGraphic()) {
		_inherited::GetDescription(buf);
	} else {
		Pro_FileTypeRec	typeRec;
		Pro_DirEntry	*dirEntry = GetMyEntry();
		
		if (dirEntry) {
			if (
				dirEntry->fileType == Pro_FileType_BINA
				&& GetStorageType(dirEntry) == Pro_Storage_FORKED
			) {
				strcpy(buf, "Forked");
			} else {
				typeRec.fileType	= dirEntry->fileType;
				typeRec.auxType		= GetRboShort(dirEntry->auxType);
				
				if (Pro_GetFileType(&typeRec)) {
					strcpy(buf, typeRec.descStr);
				} else {
					sprintf(buf, "$%02X", (short)dirEntry->fileType);
				}
			}
		}
	}

	return buf;
}

DateTimeRec		*CFilePro::GetCreatedTime(DateTimeRec *dt)
{
	Pro_DirEntry		*dirEntry = GetMyEntry();
	
	if (dirEntry) {
		Pro_DateTime		dateTime;

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

DateTimeRec		*CFilePro::GetModifiedTime(DateTimeRec *dt)
{
	Pro_DirEntry		*dirEntry = GetMyEntry();
	
	if (dirEntry) {
		Pro_DateTime		dateTime;

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

void			CFilePro::SetCreatedTime(DateTimeRec *dt)
{
	Pro_DateTime		dateTime;
	Pro_DirEntry		*dirEntry = GetMyEntry();

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

void			CFilePro::SetModifiedTime(DateTimeRec *dt)
{
	Pro_DateTime		dateTime;
	Pro_DirEntry		*dirEntry = GetMyEntry();

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

void			CFilePro::SetTimeStamps(DateTimeRec *creDate, DateTimeRec *modDate)
{
	SetCreatedTime(creDate);
	SetModifiedTime(modDate);
}

void			CFilePro::GetTimeStamps(DateTimeRec *creDate, DateTimeRec *modDate)
{
	GetCreatedTime(creDate);
	GetModifiedTime(modDate);
}

ushort		CFilePro::GetLoadAddress(void)
{
	ushort	laddr = 0;
	
	if (i_fileType == ADFS_File_BINARY) {
		laddr = GetAuxType();
	}
	
	return laddr;
}

void		CFilePro::SetLoadAddress(ushort address)
{
}

OSErr 		CFilePro::ReadFile(Byte *buffer, long length)
{
	OSErr	err = noErr;
	
	return err;
}

OSErr 		CFilePro::WriteFile(Byte *buffer, long length)
{
	OSErr	err = noErr;
	
	return err;
}

ADFS_IconType		CFilePro::GetIconType(void)
{
	ADFS_IconType	iconSuite = 0;

	if (HasCustomIcon()) {
		iconSuite = ADFS_Icon_CUSTOM;
	} else {
		Pro_DirEntry	*dirEntry = GetMyEntry();
		
		if (dirEntry) switch (dirEntry->fileType) {
			
			case Pro_FileType_SYS: {
				char	name[256];

				Pro_GetEntryName(&dirEntry->typeName, 0, name);
				
				if (strcmp(name, "PRODOS") == 0) {
					iconSuite = (ADFS_Icon_SYSTEM);
				} else {
					iconSuite = (ADFS_Icon_APPLICATION);
				}
				break;
			}
			
			case Pro_FileType_S16:
			case 0xB5: {
				//	0xB5       EXE   GS/OS Shell application                              *R*05/92
				iconSuite = (ADFS_Icon_APPLICATION);
				break;
			}

			case 0xF9: {
				//	0xF9       OS    GS/OS System file
				iconSuite = (ADFS_Icon_SYSTEM);
				break;
			};

			default: {
				iconSuite = _inherited::GetIconType();
				break;
			}
		}
	}

	return iconSuite;
}

//	gets native file type
ushort			CFilePro::GetFileType(void)
{
	ushort			fileType	= Pro_FileType_BINA;
	Pro_DirEntry	*dirEntry	= GetMyEntry();
	
	if (dirEntry) {
		fileType = dirEntry->fileType;
	}
	
	return fileType;
}

//	gets prodos equivalent file type
ushort			CFilePro::GetFileType_ProEquiv(void)
{
	return GetFileType();
}

ushort			CFilePro::GetAuxType(void)
{
	ushort			auxType		= 0;
	Pro_DirEntry	*dirEntry	= GetMyEntry();
	
	if (dirEntry) {
		auxType = GetRboShort(dirEntry->auxType);
	}
	
	return auxType;
}

void			CFilePro::SetAuxType(ushort auxType)
{
	Pro_DirEntry	*entryP = GetMyEntry();
	
	if (entryP) {
	
		//	can't set extended type
		if (GetStorageType(entryP) != Pro_Storage_FORKED) {
			entryP->auxType		= SetRboShort(auxType);
			FlushEntry();
		}
	}
}

void			CFilePro::SetFileType_ProEquiv(Byte fileType)
{
	SetFileType(fileType);
}
	
void			CFilePro::SetFileType(Byte fileType)
{
	Pro_DirEntry	*entryP = GetMyEntry();
	
	if (entryP) {
		//	can't set extended type
		if (GetStorageType(entryP) != Pro_Storage_FORKED) {
			entryP->fileType	= fileType;
			FlushEntry();
		}
	}
}

void			CFilePro::SetFileAndAuxType(Byte fileType, ushort auxType)
{
	Pro_DirEntry	*entryP = GetMyEntry();
	
	if (entryP) {
		//	can't set extended type
		if (GetStorageType(entryP) != Pro_Storage_FORKED) {
			SetFileType(fileType);
			SetAuxType(auxType);
			
			_inherited::SetFileAndAuxType(fileType, auxType);
		}
	}
}

ulong			CFilePro::ADFS_GetBufSize(void)
{
	ulong	size = sizeof(Pro_Block);
	
	if (
		i_fileType == ADFS_File_BASIC
		|| i_fileType == ADFS_File_INTBASIC
	) {
		size <<= 1;
	}

	return size;
}

OSErr		CFilePro::Pro_GetIndBlockNumR(
	Pro_StorageType		storageType, 
	Pro_BlockNum		keyBlockNum,
	Pro_BlockNum		blockIndex, 
	Pro_BlockNum		*blockNum)
{
	OSErr				err = noErr;
	Pro_IndexBlock		*indexBlockP;
	
	switch (storageType) {

		case Pro_Storage_SEEDLING: {
			*blockNum = keyBlockNum;
			break;
		}

		case Pro_Storage_SAPLING:
		case Pro_Storage_TREE: {
			err = i_cDisk.pro->GetBlock(keyBlockNum, (Pro_Block **)&indexBlockP);
			
			if (!err) {
				keyBlockNum = Pro_GetIndexBlockShort(
					indexBlockP, 
					storageType == Pro_Storage_SAPLING 
						? blockIndex & 0x00FF
						: blockIndex >> 8);
				
				err = Pro_GetIndBlockNumR(storageType - 1, keyBlockNum, blockIndex, blockNum);
			}
			break;
		}
		
		default: {
			ReportError(err = IC_Err_READ_ILLEGAL_PRO_STORAGE_TYPE);
			break;
		}
	}
	
	return err;
}

OSErr		CFilePro::Pro_GetIndBlockNum(
	Pro_BlockNum blockIndex, 
	Pro_BlockNum *blockNum)
{
	OSErr			err			= noErr;
	Pro_DirEntry	*dirEntry	= GetMyEntry();
	
	if (!dirEntry) {
		err = IC_Err_ENTRY_NOT_FOUND;
	} else {
		Pro_StorageType		storageType = GetStorageType(dirEntry);

		switch (storageType) {

			case Pro_Storage_SEEDLING:
				if (blockIndex > 0) {
					//	GS/OS allows sparse files greater than the storage type
					//	if all the zeroe'd blocks are after the first one
					*blockNum = 0;	//	indicate sparse block
					break;
				} else {
					goto GetBlockR;
				}
				
			case Pro_Storage_SAPLING:
				if (blockIndex > 0xFF) {
					//	GS/OS allows sparse files greater than the storage type
					//	if all the zeroe'd blocks are after the first one
					*blockNum = 0;	//	indicate sparse block
					break;
				} else {
					goto GetBlockR;
				}

			case Pro_Storage_TREE: {
				if (blockIndex > 0x7FFF) {	//	32767
					ReportError(err = IC_Err_READ_ILLEGAL_FILE_BLOCK);
					break;
				}

				GetBlockR:
				
				err = Pro_GetIndBlockNumR(
					storageType, 
					GetRboShort(dirEntry->key), 
					blockIndex,
					blockNum);

				break;
			}

			case Pro_Storage_FORKED: {
				Pro_ExtendedIndexBlock		*forkIndexBlockP;
				Pro_ForkEntry				*forkEntryP;
				
				err = i_cDisk.pro->GetBlock(
					GetRboShort(dirEntry->key), (Pro_Block **)&forkIndexBlockP);

				if (!err) {
					forkEntryP = i_resForkB ? &forkIndexBlockP->resource : &forkIndexBlockP->data;
					
					err = Pro_GetIndBlockNumR(
						forkEntryP->lowStorType.storageType, 
						GetRboShort(forkEntryP->key), 
						blockIndex,
						blockNum);
				}
				break;
			}

			case Pro_Storage_PASCAL: {
				ReportError(err = IC_Err_READ_ILLEGAL_PRO_STORAGE_PASCAL_TYPE);
				break;
			}

			default: {
				ReportError(err = IC_Err_READ_ILLEGAL_PRO_STORAGE_TYPE);
				break;
			}
		}
	}
	
	return err;
}

/*
		case Pro_Storage_SEEDLING: {
			if (blockIndex > 0) {
				err = IC_Err_READ_ILLEGAL_FILE_BLOCK;
				ReportError(err);
			} else {
				*blockNum = GetRboShort(dirEntry->key);
			}
			break;
		}

		case Pro_Storage_SAPLING: {
			if (blockIndex > 0xFF) {
				err = IC_Err_READ_ILLEGAL_FILE_BLOCK;
				ReportError(err);
			} else {
				Pro_IndexBlock		*indexBlockP;
				
				err = i_cDisk.pro->GetBlock(GetRboShort(dirEntry->key), (Pro_Block **)&indexBlockP);
				
				if (!err) {
					*blockNum = Pro_GetIndexBlockShort(indexBlockP, blockIndex);
				}
			}
			break;
		}

		case Pro_Storage_TREE: {
			if (blockIndex > 0x7FFF) {	//	32767
				err = IC_Err_READ_ILLEGAL_FILE_BLOCK;
				ReportError(err);
			} else {
				Pro_IndexBlock		*indexBlockP;
				
				err = i_cDisk.pro->GetBlock(GetRboShort(dirEntry->key), (Pro_Block **)&indexBlockP);
				
				if (!err) {
					*blockNum = Pro_GetIndexBlockShort(indexBlockP, blockIndex >> 8);
					err = i_cDisk.pro->GetBlock(*blockNum, (Pro_Block **)&indexBlockP);
				}

				if (!err) {
					*blockNum = Pro_GetIndexBlockShort(indexBlockP, blockIndex & 0x00FF);
				}
			}
			break;
		}
*/		

Boolean			CFilePro::IsForked(void)
{
	return GetStorageType() == Pro_Storage_FORKED;
}

OSErr		CFilePro::GetForkInfo(CCT_ForkInfo *forkInfoP)
{
	OSErr		err = _inherited::GetForkInfo(forkInfoP);
	
	if (!err) {
		Pro_DirEntry				*dirEntry	= GetMyEntry();
		Pro_ExtendedIndexBlock		*extIndBlockP;
		Pro_ForkFInfo				*srcP;
//		Pro_ForkFInfoEntryType		type = Pro_ForkFInfoEntry_NONE;
		
		err = i_cDisk.pro->GetBlock(
			GetRboShort(dirEntry->key), (Pro_Block **)&extIndBlockP);
		
		srcP = &extIndBlockP->data.fInfo1;
		switch (srcP->entryType) {

			case Pro_ForkFInfoEntry_FInfo: {
				forkInfoP->hasFInfo		= TRUE;
				memcpy(&forkInfoP->fileInfo, &srcP->fInfo, Pro_ForkFInfoSize);
				
				srcP = &extIndBlockP->data.fInfo2;
				ASSERT(srcP->entryType != Pro_ForkFInfoEntry_FInfo);
				
				if (srcP->entryType == Pro_ForkFInfoEntry_FXInfo) {
					forkInfoP->hasXFInfo		= TRUE;
					memcpy(&forkInfoP->xFileInfo, &srcP->fInfo, Pro_ForkFInfoSize);
				}
				break;
			}
			
			case Pro_ForkFInfoEntry_FXInfo: {
				forkInfoP->hasXFInfo	= TRUE;
				memcpy(&forkInfoP->xFileInfo, &srcP->fInfo, Pro_ForkFInfoSize);
				
				srcP = &extIndBlockP->data.fInfo2;
				ASSERT(srcP->entryType == Pro_ForkFInfoEntry_NONE);
				break;
			}
		}
	}

	return err;
}

OSErr		CFilePro::SetForkInfo(CCT_ForkInfo *forkInfoP)
{
	OSErr		err = _inherited::SetForkInfo(forkInfoP);
	
	if (!err) {
		Pro_DirEntry				*dirEntry	= GetMyEntry();
		Pro_ExtendedIndexBlock		*extIndBlockP;
		Pro_ForkFInfo				*dstP;
		char						*srcP;
		Pro_ForkFInfoEntryType		type = Pro_ForkFInfoEntry_NONE;
		
		err = i_cDisk.pro->GetBlock(
			GetRboShort(dirEntry->key), (Pro_Block **)&extIndBlockP);
		
		if (forkInfoP->hasFInfo) {
			srcP = (char *)&forkInfoP->fileInfo;
			type = Pro_ForkFInfoEntry_FInfo;
		} else if (forkInfoP->hasXFInfo) {
			srcP = (char *)&forkInfoP->xFileInfo;
			type = Pro_ForkFInfoEntry_FXInfo;
		}
		
		if (type != Pro_ForkFInfoEntry_NONE) {
			dstP			= &extIndBlockP->data.fInfo1;
			dstP->entrySize = Pro_ForkFInfoEntrySize;
			dstP->entryType = type;
			memcpy(&dstP->fInfo, srcP, Pro_ForkFInfoSize);
			
			if (type == Pro_ForkFInfoEntry_FInfo && forkInfoP->hasXFInfo) {
				dstP			= &extIndBlockP->data.fInfo2;
				dstP->entrySize = Pro_ForkFInfoEntrySize;
				dstP->entryType = Pro_ForkFInfoEntry_FXInfo;
				srcP			= (char *)&forkInfoP->xFileInfo;
				memcpy(&dstP->fInfo, srcP, Pro_ForkFInfoSize);
			}
		}

		err = i_cDisk.pro->SetBlock();
	}

	return err;
}

static	Boolean		Pro_IsEmptyBlock(Pro_Block *blockP)
{
	Boolean				emptyB = TRUE;
	Pro_BitMapBlock		*longBlockP = (Pro_BitMapBlock *)blockP;
	short				indexL;
	
	for (indexL = 0; emptyB && indexL < Pro_kLongsPerBlock; indexL++) {
		emptyB = (*longBlockP)[indexL] == 0;
	}
	
	return emptyB;
}

OSErr			CFilePro::ADFS_Read(ushort *bytesRead)
{
	OSErr			err = noErr;
	Pro_BlockNum	curBlockIndex	= (Pro_BlockNum)(i_filePos >> 9);		//	fp  is base 0, div by 512
	Pro_BlockNum	lastBlockIndex	= curBlockIndex;
	
	*bytesRead = 0;

	//	error if we're not reading block boundaries
	if (i_filePos != (ulong)curBlockIndex * (ulong)Pro_kBytesPerBlock) {
		err = IC_Err_BLOCK_BOUNDARIES;
		ReportError(err);
	}
	
	if (!err && i_eof > 0) {
		Pro_BlockNum	blockNum;
		Pro_Block		*blockP;

		lastBlockIndex	= (Pro_BlockNum)((i_eof - 1) >> 9);		//	eof is base 1, div by 512

		if (!err && curBlockIndex <= lastBlockIndex) {
			if (!err) err = Pro_GetIndBlockNum(curBlockIndex, &blockNum);
			
			if (blockNum > 0) {
				if (!err) err = i_cDisk.pro->GetBlock(blockNum, &blockP);
			}
			
			if (!err) {
				Boolean		translatedB = FALSE;
				
				if (IsCopyTranslated()) {
					
					if (
						i_fileType == ADFS_File_BASIC
						|| i_fileType == ADFS_File_INTBASIC
					) {
						translatedB = TRUE;
						
						err = ASSERT(blockNum > 0);
						
						if (curBlockIndex == 0) {
							//	read 2 blocks
							*((Pro_Block *)i_fileBufP) = *blockP;
							
							//	if this is the last block read, then we read <= 512 bytes!
							if (curBlockIndex == lastBlockIndex) {
								*bytesRead = ((i_eof - 1) & 0x000001FF) + 1;
							} else {
								*bytesRead = Pro_kBytesPerBlock;
								
								curBlockIndex++;

								if (!err) err = Pro_GetIndBlockNum(curBlockIndex, &blockNum);
								if (!err) err = i_cDisk.pro->GetBlock(blockNum, &blockP);
								*((Pro_Block *)&i_fileBufP[Pro_kBytesPerBlock]) = *blockP;
								
								//	if this is the last block read, then we read <= 512 bytes!
								if (curBlockIndex == lastBlockIndex) {
									*bytesRead += ((i_eof - 1) & 0x000001FF) + 1;
								} else {
									*bytesRead += Pro_kBytesPerBlock;
								}
							}
						} else if (curBlockIndex == lastBlockIndex + 1) {
							// just shift 2nd block down to 1st, continue
							*((Pro_Block *)i_fileBufP) = *((Pro_Block *)&i_fileBufP[Pro_kBytesPerBlock]);
							*bytesRead += ((i_eof - 1) & 0x000001FF) + 1;
						} else if (curBlockIndex > lastBlockIndex + 1) {
							err = IC_Err_READ_ILLEGAL_FILE_BLOCK;
							ReportError(err);
						} else {
							//	shift 2nd block to 1st, read 1 block into 2nd
							*((Pro_Block *)i_fileBufP) = *((Pro_Block *)&i_fileBufP[Pro_kBytesPerBlock]);
							*((Pro_Block *)&i_fileBufP[Pro_kBytesPerBlock]) = *blockP;

							//	if this is the last block read, then we read <= 512 bytes!
							if (curBlockIndex == lastBlockIndex) {
								*bytesRead = Pro_kBytesPerBlock + ((i_eof - 1) & 0x000001FF) + 1;
							} else {
								*bytesRead = Pro_kBytesPerBlock * 2;
							}
							
							i_filePos -= Pro_kBytesPerBlock;
						}
					}
				}
				
				if (!translatedB) {
					if (blockNum == 0) {
						memclr(i_fileBufP, sizeof(Pro_Block));
					} else {
						*((Pro_Block *)i_fileBufP) = *blockP;
					}
					
					//	if this is the last block read, then we read <= 512 bytes!
					if (curBlockIndex == lastBlockIndex) {
						*bytesRead = ((i_eof - 1) & 0x000001FF) + 1;
					} else {
						*bytesRead = Pro_kBytesPerBlock;
					}
				}
			}

			if (!err) err = _inherited::ADFS_Read(bytesRead);
		}
	}
		
	if (!err) {
		if (curBlockIndex >= lastBlockIndex) {
			err = eofErr;
		}
	}
	
	return err;
}

OSErr			CFilePro::ADFS_Write(ushort *bytesWritten)
{
	OSErr			err = noErr;
	Pro_BlockNum	curBlockIndex	= (Pro_BlockNum)(i_filePos >> 9);		//	div by 512
	
	//	error if we're not reading block boundaries
	if (i_filePos != ((ulong)curBlockIndex * (ulong)Pro_kBytesPerBlock)) {
		err = IC_Err_BLOCK_BOUNDARIES;
		ReportError(err);
	}
	
	if (!err) err = _inherited::ADFS_Write(bytesWritten);
	
	if (*bytesWritten > sizeof(Pro_Block)) {
		err = ASSERT(*bytesWritten <= sizeof(Pro_Block) * 2);

		if (!err) err = Pro_WriteBlock(sizeof(Pro_Block), (Pro_Block *)i_fileBufP);
		if (!err) err = Pro_WriteBlock(
			*bytesWritten - sizeof(Pro_Block), 
			(Pro_Block *)&i_fileBufP[sizeof(Pro_Block)]);
	} else {
		if (!err) err = Pro_WriteBlock(*bytesWritten, (Pro_Block *)i_fileBufP);
	}
	
	i_eof = i_filePos;

	return noErr;
}

OSErr		CFilePro::ADFS_Open(
	ADFS_IOType	ioType, 
	Boolean		resForkB, 
	char		**bufferP, 
	ulong		*bufSize)
{
	Pro_DirEntry	*dirEntry	= GetMyEntry();
	OSErr			err			= noErr;
	
	if (!dirEntry) err = IC_Err_ENTRY_NOT_FOUND;
	
	if (!err) {
	
		switch (ioType) {
		
			case ADFS_IO_READ: {
				if (IsForked()) {
					Pro_ExtendedIndexBlock		*forkBlockP;
					
					err = i_cDisk.pro->GetBlock(
						GetRboShort(dirEntry->key), (Pro_Block **)&forkBlockP);
					
					if (!err) {
						i_eof = GetRbo3Bytes(
							resForkB ? forkBlockP->resource.eof : forkBlockP->data.eof);
					}
				}
				break;
			}

			case ADFS_IO_WRITE: {
				if (resForkB) {
					memclr(&i_dataForkEntry, sizeof(i_dataForkEntry));

					i_dataForkEntry.lowStorType.storageType	= dirEntry->typeName.stnl.storageType;
					i_dataForkEntry.key						= dirEntry->key;
					i_dataForkEntry.blocksUsed				= dirEntry->blocksUsed;
					i_dataForkEntry.eof						= dirEntry->eof;
					
					dirEntry->typeName.stnl.storageType		= Pro_Storage_INACTIVE;
					dirEntry->key							= SetRboShort(0);
					dirEntry->blocksUsed					= SetRboShort(0);
					dirEntry->eof							= SetRbo3Bytes(0);
					i_eof									= 0;
				}
				break;
			}
		}
	}
	
	return _inherited::ADFS_Open(ioType, resForkB, bufferP, bufSize);
}

OSErr		CFilePro::ADFS_Close(void)
{
	OSErr		err = noErr;
	OSErr		err2;
	
	if (i_ioType == ADFS_IO_WRITE) {
		
		if (i_resForkB) {
			Pro_DirEntry 				*entryP = GetMyEntry();
			Pro_BlockNum				forkBlockS;
			Pro_ExtendedIndexBlock		*forkBlockP;
			Pro_BlockNum				totalBlocksS;
			ulong						totalEofL;

			err = i_cDisk.pro->ReserveNextFreeBlock(&forkBlockS);

			if (!err) err = i_cDisk.pro->PushBlock();
							
			if (!err) {
				err = i_cDisk.pro->GetBlock(forkBlockS, (Pro_Block **)&forkBlockP);
				
				if (!err) {
					//	fork block to point to data and res key blocks
					memclr(forkBlockP, Pro_kBytesPerBlock);
					
					forkBlockP->data = i_dataForkEntry;
					
					forkBlockP->resource.lowStorType.storageType	= entryP->typeName.stnl.storageType;
					forkBlockP->resource.key						= entryP->key;
					forkBlockP->resource.blocksUsed					= entryP->blocksUsed;
					forkBlockP->resource.eof						= entryP->eof;
					
					totalBlocksS	= GetRboShort(forkBlockP->data.blocksUsed)	+ GetRboShort(forkBlockP->resource.blocksUsed);
					totalEofL		= GetRbo3Bytes(forkBlockP->data.eof)		+ GetRbo3Bytes(forkBlockP->resource.eof);
					
					err = i_cDisk.pro->SetBlock();
				}
				
				err2 = i_cDisk.pro->PopBlock();
				if (!err) err = err2;
			}
			
			if (!err) {
				entryP->typeName.stnl.storageType = Pro_Storage_FORKED;
				
				//	set key block of file to point to index block
				entryP->key			= SetRboShort(forkBlockS);
				entryP->blocksUsed	= SetRboShort(totalBlocksS + 1);
				entryP->eof			= SetRbo3Bytes(totalEofL);
			}
		}
		
		if (!err) {
			err = FlushEntry();
		}
	}

	err2 = _inherited::ADFS_Close();
	if (!err) err = err2;
	
	return err;
}

OSErr	CFilePro::Pro_WriteBlock(
	ushort			numBytesS, 
	Pro_Block		*block)
{
	OSErr			err = 0;
	OSErr			err2;
	Pro_DirEntry 	*entryP = GetMyEntry();
	
	if (!entryP) {
		ReportError(err = IC_Err_ENTRY_NOT_FOUND);
	}

	//	must write at least one block, even if it's an empty block
	if (!err && (numBytesS || entryP->typeName.stnl.storageType == Pro_Storage_INACTIVE)) {
		Pro_BlockNum	dataBlockS = 0, indexBlockS, masterIndexBlockS;
		Pro_IndexBlock	*indexBlockP, *masterIndexBlockP;
		Pro_BlockNum	eofNumBlocks;
		Boolean			sparseB = Pro_IsEmptyBlock(block);
		
		if (numBytesS > sizeof(Pro_Block)) {
			ReportError(err = IC_Err_CANT_WRITE_MORE_THAN_1);
		}
		
		if (!err) switch (entryP->typeName.stnl.storageType) {

			case Pro_Storage_INACTIVE: {
				//	key block of file points to data block
				//	and is always allocated, even if it is sparse
				err = i_cDisk.pro->ReserveNextFreeBlock(&dataBlockS);
				
				if (!err) {
					entryP->typeName.stnl.storageType = Pro_Storage_SEEDLING;
					
					entryP->key			= SetRboShort(dataBlockS);
					entryP->blocksUsed	= SetRboShort(1);
					entryP->eof			= SetRbo3Bytes(numBytesS);
					
					err = FlushEntry();
				}
				break;
			}

			case Pro_Storage_SEEDLING: {
				if (!sparseB) {
					err = i_cDisk.pro->ReserveNextFreeBlock(&indexBlockS);
					if (!err) err = i_cDisk.pro->PushBlock();
									
					if (!err) {
						err = i_cDisk.pro->GetBlock(indexBlockS, (Pro_Block **)&indexBlockP);
						
						if (!err) {
							//	index block to point to data block
							memclr(indexBlockP, Pro_kBytesPerBlock);
							Pro_SetIndexBlockShort(indexBlockP, 0, GetRboShort(entryP->key));

							//	next block is data block
							err = i_cDisk.pro->ReserveNextFreeBlock(&dataBlockS);
						}

						if (!err) {
							Pro_SetIndexBlockShort(indexBlockP, 1, dataBlockS);
							err = i_cDisk.pro->SetBlock();	//	indexBlockP
						}

						err2 = i_cDisk.pro->PopBlock();
						if (!err) err = err2;
						
						if (!err) {
							entryP->typeName.stnl.storageType = Pro_Storage_SAPLING;
							
							//	set key block of file to point to index block
							entryP->key			= SetRboShort(indexBlockS);
							entryP->blocksUsed	= SetRboShort(3);
						}
					}
				}

				if (!err) {
					entryP->eof	= SetRbo3Bytes((ulong)Pro_kBytesPerBlock + (ulong)numBytesS);
					err			= FlushEntry();
				}
				break;
			}

			case Pro_Storage_SAPLING: {
				//	if the file already takes up 256 blocks (0x100), then adding
				//	one more means switching from sapling to tree
				if (Pro_GetBlocksUsed(entryP) == 0x100) {
						
					if (!sparseB) {
						err = i_cDisk.pro->ReserveNextFreeBlock(&masterIndexBlockS);
						if (!err) err = i_cDisk.pro->PushBlock();	//	masterIndexBlockP

						if (!err) {
							err = i_cDisk.pro->GetBlock(masterIndexBlockS, (Pro_Block **)&masterIndexBlockP);
							
							if (!err) {
								//	master index block to point to first index block
								memclr(masterIndexBlockP, Pro_kBytesPerBlock);
								Pro_SetIndexBlockShort(masterIndexBlockP, 0, GetRboShort(entryP->key));

								//	next block is second index block
								err = i_cDisk.pro->ReserveNextFreeBlock(&indexBlockS);
							}
							
							if (!err)  err = i_cDisk.pro->PushBlock();	//	indexBlockP
								
							if (!err) {
								err = i_cDisk.pro->GetBlock(indexBlockS, (Pro_Block **)&indexBlockP);
								
								if (!err) {
									//	master index must point to 2nd index block as well
									Pro_SetIndexBlockShort(masterIndexBlockP, 1, indexBlockS);
			
									//	next block is data block
									err = i_cDisk.pro->ReserveNextFreeBlock(&dataBlockS);
								}

								if (!err) {
									//	index block to point to data block
									memclr(indexBlockP, Pro_kBytesPerBlock);
									Pro_SetIndexBlockShort(indexBlockP, 0, dataBlockS);
								}

								if (!err) err = i_cDisk.pro->SetBlock();	//	indexBlockP
								err2 = i_cDisk.pro->PopBlock();				//	indexBlockP
								if (!err) err = err2;
							}

							if (!err) err = i_cDisk.pro->SetBlock();	//	masterIndexBlockP
							err2 = i_cDisk.pro->PopBlock();				//	masterIndexBlockP
							if (!err) err = err2;
						}
								
						if (!err) {
							entryP->typeName.stnl.storageType = Pro_Storage_TREE;
	
							//	set key block of file to point to master index block
							entryP->key			= SetRboShort(masterIndexBlockS);
							entryP->blocksUsed	= SetRboShort(GetRboShort(entryP->blocksUsed) + 3);
						}
					}
					
					if (!err) {
						entryP->eof	= SetRbo3Bytes(GetRbo3Bytes(entryP->eof) + numBytesS);
						err			= FlushEntry();
					}
				} else {
					eofNumBlocks = Pro_GetBlocksUsed(entryP);
					
					if (!sparseB) {
						err = i_cDisk.pro->ReserveNextFreeBlock(&dataBlockS);
					}
					
					if (!err) err = i_cDisk.pro->PushBlock();	//	indexBlockP

					if (!err) {
						err = i_cDisk.pro->GetBlock(GetRboShort(entryP->key), (Pro_Block **)&indexBlockP);

						if (!err) {					
							entryP->blocksUsed	= SetRboShort(GetRboShort(entryP->blocksUsed) + 1);
							entryP->eof			= SetRbo3Bytes(GetRbo3Bytes(entryP->eof) + numBytesS);

							Pro_SetIndexBlockShort(indexBlockP, eofNumBlocks, dataBlockS);
							
							err = i_cDisk.pro->SetBlock();	//	indexBlockP
						}

						err2 = i_cDisk.pro->PopBlock();	//	indexBlockP
						if (!err) err = err2;
					}
						
					if (!err) err = FlushEntry();
				}
					
				break;
			}

			case Pro_Storage_TREE: {
				eofNumBlocks = Pro_GetBlocksUsed(entryP);
			
				//	it's a multiple of 256 blocks
				if ((eofNumBlocks & 0xFF) == 0) {
					
					//	must grow again				
					//	can't grow past 128 * 256 blocks
					if (eofNumBlocks == 0x7FFF) {
						ReportError(err = IC_Err_FILE_TOO_LARGE);
					}
					
					if (!err && !sparseB) {
						//	next block is new index block
						if (!err) err = i_cDisk.pro->ReserveNextFreeBlock(&indexBlockS);
						if (!err) err = i_cDisk.pro->PushBlock();	//	indexBlockP

						if (!err) {
							if (!err) err = i_cDisk.pro->GetBlock(indexBlockS, (Pro_Block **)&indexBlockP);

							//	next block is data block
							if (!err) err = i_cDisk.pro->ReserveNextFreeBlock(&dataBlockS);
							if (!err) err = i_cDisk.pro->PushBlock();	//	masterIndexBlockP

							if (!err) {
								//	grab the master index block
								if (!err) err = i_cDisk.pro->GetBlock(GetRboShort(entryP->key), (Pro_Block **)&masterIndexBlockP);

								if (!err) {
									//	clear new index block
									memclr(indexBlockP, Pro_kBytesPerBlock);

									//	new index block points to new data block
									Pro_SetIndexBlockShort(indexBlockP, 0, dataBlockS);
									
									//	update file size
									entryP->blocksUsed	= SetRboShort((ushort)(GetRboShort(entryP->blocksUsed) + 2));
									
									//	master index block must to point to new index block
									Pro_SetIndexBlockShort(
										masterIndexBlockP, 
										HiByte(eofNumBlocks), 
										indexBlockS);
								}

								if (!err) err = i_cDisk.pro->SetBlock();	//	masterIndexBlockP
								err2 = i_cDisk.pro->PopBlock();				//	masterIndexBlockP
								if (!err) err = err2;
							}				

							if (!err) err = i_cDisk.pro->SetBlock();	//	indexBlockP
							err2 = i_cDisk.pro->PopBlock();				//	indexBlockP
							if (!err) err = err2;
						}
					}

					if (!err) {
						entryP->eof		= SetRbo3Bytes(GetRbo3Bytes(entryP->eof) + numBytesS);
						err				= FlushEntry();
					}
				} else {
					if (!sparseB) {
						if (!err) err = i_cDisk.pro->ReserveNextFreeBlock(&dataBlockS);
					}
					
					if (!err) err = i_cDisk.pro->PushBlock();	//	masterIndexBlockP

					if (!err) {
						if (!err) err = i_cDisk.pro->GetBlock(
							GetRboShort(entryP->key), 
							(Pro_Block **)&masterIndexBlockP);
							
						if (!err) err = i_cDisk.pro->PushBlock();	//	indexBlockP

						if (!err) {
							if (!err) err = i_cDisk.pro->GetBlock(
								Pro_GetIndexBlockShort(
									masterIndexBlockP, 
									HiByte(eofNumBlocks)), 
								(Pro_Block **)&indexBlockP);
								
							if (!err) {
								Pro_SetIndexBlockShort(
									indexBlockP, 
									LoByte(eofNumBlocks), 
									dataBlockS);

								entryP->blocksUsed	= SetRboShort((ushort)(GetRboShort(entryP->blocksUsed) + 1));
								entryP->eof			= SetRbo3Bytes(GetRbo3Bytes(entryP->eof) + numBytesS);
							}

							if (!err) err = i_cDisk.pro->SetBlock();	//	indexBlockP
							err2 = i_cDisk.pro->PopBlock();				//	indexBlockP
							if (!err) err = err2;
						}

						if (!err) err = i_cDisk.pro->SetBlock();	//	masterIndexBlockP
						err2 = i_cDisk.pro->PopBlock();				//	masterIndexBlockP
						if (!err) err = err2;
					}

					if (!err) err = FlushEntry();
				}
				break;
			}
		}
		
		if (!err && !sparseB) {
			Pro_Block	*dataBlockP;

			err = i_cDisk.pro->GetBlock(dataBlockS, (Pro_Block **)&dataBlockP);
			
			if (!err) {
				*dataBlockP = *block;
				
				if (numBytesS < sizeof(Pro_Block)) {
					memset(&dataBlockP->byte[numBytesS], 0, sizeof(Pro_Block) - numBytesS);
				}

				err = i_cDisk.pro->SetBlock();	//	dataBlockP
			}
		}
	}
		
	return err;
}

void			CFilePro::GetAccessBits(Gen_AccessBits *bits)
{
	if (i_openingB) {
		_inherited::GetAccessBits(bits);
	} else {
		Pro_DirEntry	*dirEntry = GetMyEntry();
		
		if (dirEntry) {
			*bits = *(Gen_AccessBits *)&dirEntry->access;
		}
	}
}

void			CFilePro::SetAccessBits(Gen_AccessBits *bits)
{
	Pro_DirEntry	*dirEntry = GetMyEntry();
	
	if (dirEntry) {
		dirEntry->access = *(Pro_AccessPriv *)bits;
		FlushEntry();
	}

	_inherited::SetAccessBits(bits);
}

static	char	*Pro_GetStorageStr(Byte storageType, char *buf)
{
	switch (storageType) {

		case Pro_Storage_INACTIVE: {
			strcpy(buf, "Inactive");
			break;
		}

		case Pro_Storage_SEEDLING: {
			strcpy(buf, "Seedling.");
			break;
		}

		case Pro_Storage_SAPLING: {
			strcpy(buf, "Sapling.");
			break;
		}

		case Pro_Storage_TREE: {
			strcpy(buf, "Tree.");
			break;
		}
		
		case Pro_Storage_PASCAL: {
			sprintf(buf, "Pascal Area.");
			break;
		}

		default: {
			strcpy(buf, "Unknown");
			break;
		}
	}
	
	return buf;
}

char		*CFilePro::GetStorageStr(char *buf256)
{
	Pro_DirEntry 	*entryP	= GetMyEntry();
	
	buf256[0] = 0;
	
	if (entryP) {
		char			str1[256];
		Pro_BlockNum	startBlockS;
		ushort			storageType;
		Pro_BlockNum	numFileBlocksS;
		Pro_BlockNum	numExtentBlocksS;
		ushort			blocks;

		startBlockS 		= GetRboShort(entryP->key);
		storageType 		= GetStorageType(entryP);
		numFileBlocksS		= 0;
		numExtentBlocksS	= 0;

		if (GetFileBlocks(
			startBlockS, storageType,
			NULL, &numFileBlocksS, 
			NULL, &numExtentBlocksS) != noErr)
		{
			blocks = GetRboShort(entryP->blocksUsed);
		} else {
			blocks = numFileBlocksS + numExtentBlocksS;
		}
		
		switch (storageType) {

			default: {
				Pro_GetStorageStr(storageType, str1);
				break;
			}

			case Pro_Storage_FORKED: {
				OSErr						err;
				Pro_ExtendedIndexBlock		*extIndBlockP;
				
				err = i_cDisk.pro->GetBlock(
					GetRboShort(entryP->key), (Pro_Block **)&extIndBlockP);
				
				if (!err) {
					char		dataStorTypeStr[256], resStorTypeStr[256];
					ushort		dataUsed	= GetRboShort(extIndBlockP->data.blocksUsed),
								resUsed		= GetRboShort(extIndBlockP->resource.blocksUsed);

					sprintf(
						str1, "Data: %s (%hu block%s), Res: %s (%hu block%s).", 
						Pro_GetStorageStr(extIndBlockP->data.lowStorType.storageType, dataStorTypeStr), 
						dataUsed, dataUsed == 1 ? ADFS_Str(ADFS_Str_NONE) : "s", 
						Pro_GetStorageStr(extIndBlockP->resource.lowStorType.storageType, resStorTypeStr), 
						resUsed, resUsed == 1 ? ADFS_Str(ADFS_Str_NONE) : "s");
				} else {
					Pro_GetStorageStr(storageType, str1);
				}
				break;
			}
		}

		sprintf(buf256, "%s (%hu block%s)", str1, blocks, blocks == 1 ? ADFS_Str(ADFS_Str_NONE) : "s");
	}

	return buf256;
}


OSErr		CFilePro::GetFileBlocks(
	Pro_BlockNum	startBlockS, 
	ushort			storageType, 
 	Pro_BlockNum	*fileBlockA0, 
	Pro_BlockNum	*numFileBlocksSP, 
	Pro_BlockNum	*extentBlockA0, 
	Pro_BlockNum	*numExtentBlocksSP)
{
	OSErr		err		= noErr;
	OSErr		err2	= noErr;

	if (!err) err = i_cDisk.pro->PushBlock();
	if (!err) {
		Pro_IndexBlock		*indexBlockP	= NULL;
		
		switch  (storageType) {
		
			case Pro_Storage_SEEDLING: {
				if (fileBlockA0) {
					fileBlockA0[*numFileBlocksSP] = startBlockS;
				}
		
				(*numFileBlocksSP)++;
				break;
			}

			case Pro_Storage_SAPLING:
			case Pro_Storage_TREE: {
				Pro_DirEntry		*entryP = GetMyEntry();
							
				if (extentBlockA0) {
					extentBlockA0[*numExtentBlocksSP] = startBlockS;
				}
		
				(*numExtentBlocksSP)++;
				
				err = i_cDisk.pro->GetBlock(
					startBlockS, (Pro_Block **)&indexBlockP);
				
				if (storageType == Pro_Storage_TREE) {
					if (!err) err = i_cDisk.pro->PushBlock();
				}
				
				if (!err) {
					Pro_BlockNum	curBlock;
					
					for (
						curBlock = 0; 
						!err && curBlock < Pro_kShortsPerBlock; 
						curBlock++
					) {
						startBlockS = Pro_GetIndexBlockShort(indexBlockP, curBlock);
						
						//	file is deleted?  then the index blocks are swapped!
						if (entryP->typeName.stnl.nameLength == 0) {
							startBlockS = GetRboShort(*(RboShort *)&startBlockS);
						}
						
						if (startBlockS != 0) {
							storageType--;
							
							err = GetFileBlocks(
								startBlockS, storageType, 
								fileBlockA0, numFileBlocksSP, 
								extentBlockA0, numExtentBlocksSP);
							
							storageType++;
						}
					}

					if (storageType == Pro_Storage_TREE) {
						err2 = i_cDisk.pro->PopBlock();
						if (!err) err = err2;
					}
				}
				break;
			}

			case Pro_Storage_PASCAL: {
				ReportError(err = IC_Err_READ_ILLEGAL_PRO_STORAGE_PASCAL_TYPE);
				break;
			}

			case Pro_Storage_FORKED: {
				Pro_ExtendedIndexBlock		*forkBlockP;
				
				if (extentBlockA0) {
					extentBlockA0[*numExtentBlocksSP] = startBlockS;
				}
		
				(*numExtentBlocksSP)++;
				
				err = i_cDisk.pro->GetBlock(
					startBlockS, (Pro_Block **)&forkBlockP);
				
				if (!err) err = i_cDisk.pro->PushBlock();
				if (!err) {
					if (!err) {
						startBlockS	= GetRboShort(forkBlockP->data.key);
						storageType	= forkBlockP->data.lowStorType.storageType;
						
						err = GetFileBlocks(
							startBlockS, storageType, 
							fileBlockA0, numFileBlocksSP, 
							extentBlockA0, numExtentBlocksSP);
					}

					if (!err) {
						startBlockS	= GetRboShort(forkBlockP->resource.key);
						storageType	= forkBlockP->resource.lowStorType.storageType;
						
						err = GetFileBlocks(
							startBlockS, storageType, 
							fileBlockA0, numFileBlocksSP, 
							extentBlockA0, numExtentBlocksSP);
					}

					err2 = i_cDisk.pro->PopBlock();
					if (!err) err = err2;
				}
				
				break;
			}
		}

		err2 = i_cDisk.pro->PopBlock();
		if (!err) err = err2;
	}
	
	return err;
}

OSErr		CFilePro::GetEntryAlloc(
	Boolean			getAsBlocksB, 
	Gen_EntryAlloc	**sectorListH)
{
	OSErr		err = noErr;
	
	*sectorListH	= (Gen_EntryAlloc *)TrackNewPtrClear(
		"entry sectors, for file", sizeof(Gen_EntryAlloc));
	
	if (*sectorListH == NULL) err = memFullErr;
	
	if (!err) {
		(**sectorListH).allocSize = getAsBlocksB ? Gen_AllocSize_SHORT : Gen_AllocSize_SECTORS;
	}
	
	if (!err && !IsDeleted()) {
		Pro_DirEntry	*entryP	= GetMyEntry();
		Pro_BlockNum	startBlockS;
		ushort			storageType;
		Pro_BlockNum	numFileBlocksS;
		Pro_BlockNum	numExtentBlocksS;
		Pro_BlockNum	*blockNumP = NULL;
				
		
		if (!entryP) {
			err = IC_Err_ENTRY_NOT_FOUND;
		} else {
			startBlockS			= GetRboShort(entryP->key);
			storageType			= GetStorageType(entryP);
			numFileBlocksS		= 0;
			numExtentBlocksS	= 0;
		}

		if (!err) err = GetFileBlocks(
			startBlockS, storageType,
			NULL, &numFileBlocksS, 
			NULL, &numExtentBlocksS);
		
		//	suppress illegal block access
		if (err == IC_Err_READ_ILLEGAL_FILE_BLOCK) err = noErr;
		
		if (!err && numFileBlocksS) {
			blockNumP = (Pro_BlockNum *)TrackNewPtrClear(
				"entry sectors, file blocks", 
				sizeof(Pro_BlockNum) * numFileBlocksS);
			
			if (blockNumP == NULL) err = memFullErr;
			
			if (!err) {
				(**sectorListH).type[Gen_Alloc_FILE].totalS				= numFileBlocksS;
				(**sectorListH).type[Gen_Alloc_FILE].u.short_blocksA	= blockNumP;
				blockNumP = NULL;
			}
		}

		if (!err && numExtentBlocksS) {
			blockNumP = (Pro_BlockNum *)TrackNewPtrClear(
				"entry sectors, file extent blocks", 
				sizeof(Pro_BlockNum) * numExtentBlocksS);
			
			if (blockNumP == NULL) err = memFullErr;
			
			if (!err) {
				(**sectorListH).type[Gen_Alloc_EXTENTS].totalS			= numExtentBlocksS;
				(**sectorListH).type[Gen_Alloc_EXTENTS].u.short_blocksA	= blockNumP;
				blockNumP = NULL;
			}
		}
		
		if (!err) {
			numFileBlocksS		= 0;
			numExtentBlocksS	= 0;

			err = GetFileBlocks(
				startBlockS, storageType, 
				(**sectorListH).type[Gen_Alloc_FILE].u.short_blocksA,		&numFileBlocksS, 
				(**sectorListH).type[Gen_Alloc_EXTENTS].u.short_blocksA,	&numExtentBlocksS);

			//	suppress illegal block access
			if (err == IC_Err_READ_ILLEGAL_FILE_BLOCK) err = noErr;
		}
	}
	
	if (!err && !getAsBlocksB) {
		err = i_cDisk.pro->EntryBlocksToSectors(*sectorListH);
	}
	
	if (err) {
		DisposeEntryAlloc(*sectorListH);
		*sectorListH = NULL;
	}
	
	return err;
}

Boolean		CFilePro::IsDeleted(void)
{
	Boolean		deletedB	= _inherited::IsDeleted();
	
	if (!deletedB) {
		Pro_DirEntry 	*entryP	= GetMyEntry();
		
		if (entryP) {
			CFolderPro	*parentP = (CFolderPro *)GetParentFolder();

			deletedB = i_cDisk.pro->IsDeletedEntry(
				entryP, 
				i_cDisk.pro->GetVolumeSize(), 
				parentP->GetDirKeyBlock());
		}
	}

	return deletedB;
}

OSErr		CFilePro::Delete(Boolean warnB, CDialogCopy *copyP0)
{
	OSErr		err				= noErr;
	CFolderPro	*folderP		= (CFolderPro *)GetParentFolder();
	
	if (!err) err = _inherited::Delete(warnB, copyP0);
	if (!err) err = folderP->Pro_DeleteEntry(i_directoryIndex);
		
	return err;
}

OSErr		CFilePro::UnDelete(Boolean recursiveB, CDialogCopy *copyP0)
{
	OSErr	err = noErr;
	
	if (!err) err = ASSERT(IsDeleted());
	
	if (!err) {
		CFolderPro		*parentFolderP = (CFolderPro *)GetParentFolder();
		
		if (parentFolderP && parentFolderP->IsDeleted()) {
			err = parentFolderP->UnDelete();
		}
	}
	
	if (!err) {
		char				errorAC[1024], whereAC[256];
		Pro_DirEntry 		*entryP		= GetMyEntry();
		
		if (entryP) {
			if (GetLogicalSize() == 0) {				
				sprintf(
					errorAC, 
					ADFS_Str(ADFS_Str_TOTALLY_OVERWRITTEN), 
					GetWhereString(whereAC));

				entryP = NULL;
				ReportErrorStr(-1, errorAC);
			}
		}
		
		if (entryP) {
			i_cDisk.pro->FlushMemDisk(FALSE);

			if (!err) err = i_cDisk.pro->CacheBitmapBlocks();
			
			if (err) entryP = NULL;
		}
			
		if (entryP) {
			OSErr				err2;
			Gen_EntryAlloc		*sectorListP;
			Boolean				successB = FALSE;

			entryP->typeName.stnl.storageType	= GetStorageType(entryP);
			FlushEntry();
			
			err = GetEntryAlloc(TRUE, &sectorListP);
			
			entryP->typeName.stnl.nameLength	= Pro_GetNameLength(&entryP->typeName);
			FlushEntry();

			if (!err) {
				Gen_AllocType		allocType;
				Pro_BlockNum		blockIndexS;
				Pro_BlockNum		curBlockS;
				Boolean				isFreeB;

				successB = TRUE;
				
				for (
					allocType = Gen_Alloc_FILE; 
					!err & allocType <= Gen_Alloc_EXTENTS; 
					allocType = (Gen_AllocType)(allocType + (Gen_Alloc_EXTENTS - Gen_Alloc_FILE))
				) {
					for (
						blockIndexS = 0;
						!err & blockIndexS < sectorListP->type[allocType].totalS;
						blockIndexS++
					) {
						curBlockS = sectorListP->type[allocType].u.short_blocksA[blockIndexS];
						
						err = i_cDisk.pro->IsBlockFree(curBlockS, &isFreeB);
						if (!isFreeB) {
							goto bad_bail;
						}
					}
				}

				if (!err) err = i_cDisk.pro->PushBlock();
				if (!err) {					
					for (
						allocType = Gen_Alloc_FILE; 
						!err & allocType <= Gen_Alloc_EXTENTS; 
						allocType = (Gen_AllocType)(allocType + (Gen_Alloc_EXTENTS - Gen_Alloc_FILE))
					) {
						for (
							blockIndexS = 0;
							!err & blockIndexS < sectorListP->type[allocType].totalS;
							blockIndexS++
						) {
							curBlockS = sectorListP->type[allocType].u.short_blocksA[blockIndexS];
							
							err = i_cDisk.pro->ReserveBlock(curBlockS);
							if (allocType == Gen_Alloc_EXTENTS) {
								//	and swap the index block so undelete works
								//	see Apple II Tech Notes : ProDOS : #23
								//	under 8.1.3, paragraph 2
								//	http://www.gno.org/pub/apple2/doc/apple/technotes/pdos/tn.pdos.023
								//	http://web.pdx.edu/~heiss/technotes/pdos/tn.pdos.23.html
								Pro_IndexBlock		*index_blockP;
								Pro_HalfBlock		temp_half;

								if (!err) err = i_cDisk.pro->GetBlock(
									curBlockS, (Pro_Block **)&index_blockP);
									
								if (!err) {
									temp_half			= index_blockP->low;
									index_blockP->low	= index_blockP->high;
									index_blockP->high	= temp_half;
									err = i_cDisk.pro->SetBlock();
								}
							}
						}
					}
					
					err2 = i_cDisk.pro->PopBlock();
					if (!err) err = err2;

					if (!err) err = ((CFolderPro *)GetParentFolder())->IncrementDirCount(1);
				}
							
				goto good_done;
				bad_bail:

				entryP->typeName.stnl.storageType	= 0;
				entryP->typeName.stnl.nameLength	= 0;
				FlushEntry();

				GetWhereString(whereAC);
				
				if (allocType == Gen_Alloc_EXTENTS) {
					sprintf(
						errorAC, 
						ADFS_Str(ADFS_Str_EXTENTS_OVERWRITTEN), 
						whereAC);
				} else {
					sprintf(
						errorAC, 
						ADFS_Str(ADFS_Str_PARTS_OVERWRITTEN), 
						whereAC);
				}
				
				successB = FALSE;
				ReportErrorStr(-1, errorAC);				
				
				good_done:
				DisposeEntryAlloc(sectorListP);
			}
			
			err2 = i_cDisk.pro->UnCacheBitmapBlocks();
			if (!err) err = err2;

			i_cDisk.pro->FlushMemDisk(TRUE);
		}
	}

	if (!err) err = _inherited::UnDelete(recursiveB, copyP0);

	return err;
}

Pro_StorageType		CFilePro::GetStorageType(Pro_DirEntry *entryP0)
{
	Pro_StorageType		storageS = Pro_Storage_INACTIVE;
	Pro_DirEntry		*entryP;
	
	if (entryP0) {
		entryP = entryP0;
	} else {
		entryP = GetMyEntry();
	}
	
	if (entryP) {
		if (IsDeleted()) {
			ulong	eofL	= GetRbo3Bytes(entryP->eof);
			OSErr	err		= noErr;
		
			if (!err) err = i_cDisk.pro->PushBlock();
			if (!err) {
				OSErr						err2;
				Pro_ExtendedIndexBlock		*forkP;
				ulong		 				blocksInVolumeL = i_cDisk.pro->GetTotalBlocks();

				err = i_cDisk.pro->GetBlock(
					GetRboShort(entryP->key), (Pro_Block **)&forkP);
				
				if (!err) {
					if (
						   forkP->data    .lowStorType.reserved == 0
						&& forkP->resource.lowStorType.reserved == 0
						&& forkP->data    .lowStorType.storageType <= Pro_Storage_TREE
						&& forkP->resource.lowStorType.storageType <= Pro_Storage_TREE
						&& GetRboShort(forkP->data    .key) < blocksInVolumeL
						&& GetRboShort(forkP->resource.key) < blocksInVolumeL
						
						&&     GetRboShort(forkP->data    .blocksUsed) 
							+  GetRboShort(forkP->resource.blocksUsed) 
							== GetRboShort(entryP->blocksUsed) + 1
							
						&&    GetRbo3Bytes(forkP->data    .eof) 
							+ GetRbo3Bytes(forkP->resource.eof) 
							== eofL
					) {
						storageS = Pro_Storage_FORKED;
					}
				}

				err2 = i_cDisk.pro->PopBlock();
				if (!err) err = err2;
			}
			
			if (storageS == Pro_Storage_INACTIVE) {
			
				if (eofL == 0) {
					storageS = Pro_Storage_INACTIVE;
				} else if (eofL <= sizeof(Pro_Block)) {
					storageS = Pro_Storage_SEEDLING;
				} else if (eofL <= sizeof(Pro_Block) * Pro_kShortsPerBlock) {
					storageS = Pro_Storage_SAPLING;
				} else {
					storageS = Pro_Storage_TREE;
				}
			}
		} else {
			storageS = entryP->typeName.stnl.storageType;
		}
	}
	
	return storageS;
}

Boolean		CFilePro::HasCustomIcon(void)
{
	char	buf[256];
	
	return i_cDisk.pro->HasCustomIcon(GetFileType(), GetAuxType(), GetName(buf));
}

void		CFilePro::DrawIcon(IconTransformType transform, Rect *theRect)
{
	if (HasCustomIcon()) {
		char			buf[256];
		extern Handle	g_iconSuiteH;

		g_iconSuiteH = i_cDisk.pro->GetCustomIcon(GetFileType(), GetAuxType(), GetName(buf));
	}
	
	_inherited::DrawIcon(transform, theRect);
}

