//	CpmStructs.h

#ifndef _H_CpmStructs
#define _H_CpmStructs

#include "GenStructs.h"
#include "CpmDiskLocSpec.h"
#include "FileSystemTypes.h"

#ifndef ushort
	typedef unsigned short ushort;
#endif

#pragma options align = packed

typedef Cpm_BlockSpec	Cpm_BlockNum;
typedef	ushort			Cpm_EntryIndex;

#define	Cpm_GetBlock(imageRec, blockNum)		\
	&(imageRec->image.cpm->userType.block[blockNum])
	
/*
	for access bits, they are stored in the hi-bit of
	the corresponding character position.
	
	eg: to see if a file is locked, look in the 
	Cpm_kAccessChar_READ_ONLY char of the file name, 
	if it's hi bit is set, it's locked.
	
	the bits are propagated thru all the dir entries for a 
	single file, not just set on the first entry
*/
enum	{
	Cpm_kAccessChar_ILLEGAL_0, 
	Cpm_kAccessChar_PUBLIC, 
	Cpm_kAccessChar_DATESTAMP, 
	Cpm_kAccessChar_ILLEGAL_3,
	Cpm_kAccessChar_ILLEGAL_4,
	Cpm_kAccessChar_ILLEGAL_5,
	Cpm_kAccessChar_ILLEGAL_6,
	Cpm_kAccessChar_WHEEL_PROTECT, 	Cpm_kNameLength, 
	Cpm_kAccessChar_READ_ONLY =		Cpm_kNameLength, 
	Cpm_kAccessChar_SYSTEM, 
	Cpm_kAccessChar_ARCHIVE,		Cpm_kNameAndTypeLength, 
	
	Cpm_kTypeLength = Cpm_kNameAndTypeLength - Cpm_kNameLength
};
typedef short Cpm_AccessChar;

#define	FOR_EACH_ACCESS_CHAR(_IDX)				\
for (	charIndex = Cpm_kAccessChar_ILLEGAL_0;	\
		charIndex <= Cpm_kAccessChar_ARCHIVE;	\
		charIndex++)

enum	{
	Cpm_kAccessBit_ILLEGAL_0		= 1 << Cpm_kAccessChar_ILLEGAL_0, 
	Cpm_kAccessBit_PUBLIC			= 1 << Cpm_kAccessChar_PUBLIC, 
	Cpm_kAccessBit_DATESTAMP		= 1 << Cpm_kAccessChar_DATESTAMP, 
	Cpm_kAccessBit_ILLEGAL_3		= 1 << Cpm_kAccessChar_ILLEGAL_3, 
	Cpm_kAccessBit_ILLEGAL_4		= 1 << Cpm_kAccessChar_ILLEGAL_4, 
	Cpm_kAccessBit_ILLEGAL_5		= 1 << Cpm_kAccessChar_ILLEGAL_5, 
	Cpm_kAccessBit_ILLEGAL_6		= 1 << Cpm_kAccessChar_ILLEGAL_6, 
	Cpm_kAccessBit_WHEEL_PROTECT	= 1 << Cpm_kAccessChar_WHEEL_PROTECT, 
	Cpm_kAccessBit_READ_ONLY		= 1 << Cpm_kAccessChar_READ_ONLY, 
	Cpm_kAccessBit_SYSTEM			= 1 << Cpm_kAccessChar_SYSTEM, 
	Cpm_kAccessBit_ARCHIVE			= 1 << Cpm_kAccessChar_ARCHIVE
};
typedef short Cpm_AccessBits;

typedef char		Cpm_FileName[Cpm_kNameLength];
typedef char		Cpm_FileNameStr[Cpm_kNameLength + 1];

typedef char		Cpm_FileType[Cpm_kTypeLength];
typedef char		Cpm_FileTypeStr[Cpm_kTypeLength + 1];

//	room for "dot" and the "\0"
typedef char		Cpm_FileNameAndTypeStr[Cpm_kNameAndTypeLength + 2];

#define		Cpm_IsAccessCharSet(_foo)	\
	(((_foo) & 0x80) != 0)

#define		Cpm_SetAccessChar(_char, _setB)		\
	if (_setB) {								\
		(_char) |= 0x80;						\
	} else {									\
		(_char) &= 0x7F;						\
	}

#define		Cpm_SetAccessBit(_bits, _bit, _setB)	\
	if (_setB) {									\
		(_bits) |= _bit;							\
	} else {										\
		(_bits) &= ~_bit;							\
	}

#define		Cpm_GetAccessBit(_bits, _bit)	\
	(((_bits) & _bit) != 0)

#define	Cpm_kMaxTotalDirEntries		64
#define	Cpm_kMaxUseableDirEntries	48
#define	Cpm_kUserNumber				0

#define	Cpm_kIllegalBlock			0xFF
#define	Cpm_kErasedSentinel			0xE5
#define	Cpm_kTextFileEnd			0x1A	//	ctrl-Z

#define	Cpm_kBytesPerRecord			128L
#define	Cpm_kRecordsPerBlock		8
#define	Cpm_kBytesPerBlock 			/* 1024 */	(Cpm_kBytesPerRecord * Cpm_kRecordsPerBlock)

#define	Cpm_kBlocksPerExtent		16
#define	Cpm_kBlocksPerTrack			4
#define	Cpm_kSectorsPerBlock		4
#define	Cpm_kRecordsPerExtent 		/* 128 */	(Cpm_kBlocksPerExtent * Cpm_kRecordsPerBlock)

#define	Cpm_kHeaderBlocksPerDisk	((Cpm_BlockNum)12)
#define	Cpm_kUserBlocksPerDisk		((Cpm_BlockNum)128)
#define	Cpm_kBlocksPerDisk			(Cpm_kHeaderBlocksPerDisk + Cpm_kUserBlocksPerDisk)

typedef struct {
	Byte			userNumber;
	Cpm_FileName	name;
	Cpm_FileType	fileType;
	Byte			extentNum;
	RboShort		system;
	Byte			numRecords;
	Cpm_BlockSpec	extent[Cpm_kBlocksPerExtent];
} Cpm_DirEntry;
typedef Cpm_DirEntry	Cpm_Directory[Cpm_kMaxTotalDirEntries];

typedef struct {
	Byte		byte[Cpm_kBytesPerRecord];
} Cpm_Record;

typedef union {
	Cpm_Record	record[Cpm_kRecordsPerBlock];
	Byte		byte[Cpm_kBytesPerBlock];
	Gen_Sector	sector[Cpm_kSectorsPerBlock];
} Cpm_Block;

typedef	Cpm_Block		Cpm_HeaderArea[Cpm_kHeaderBlocksPerDisk];
typedef	Cpm_Block		Cpm_UserArea[Cpm_kUserBlocksPerDisk];

typedef struct Cpm_Disk {
	Cpm_HeaderArea	header;
	
	union {
		Cpm_UserArea	block;
		Cpm_Directory	directory;
	} userType;
} Cpm_Disk;

#define	Cpm_kNumBootBlocks			((Cpm_BlockNum)0)
#define	Cpm_kDirStartBlock			((Cpm_BlockNum)0)
#define	Cpm_kNumDirBlocks			/* 2 */ ((Cpm_BlockNum)(sizeof(Cpm_Directory) / Cpm_kBytesPerBlock))
#define	Cpm_kDirEndBlock			/* 1 */ ((Cpm_BlockNum)Cpm_kDirStartBlock + Cpm_kNumDirBlocks - 1)

#define	Cpm_kFreeUserArea			/* ????? */	(sizeof(Cpm_Disk) - sizeof(Cpm_HeaderArea) - sizeof(Cpm_Directory));
#define	Cpm_kMaxFileSize			/* ????? */	(Cpm_kMaxUseableDirEntries * Cpm_kBlocksPerExtent * Cpm_kBytesPerBlock)
#define	Cpm_kMaxExtentNum			(Cpm_kUserBlocksPerDisk / Cpm_kBlocksPerExtent)

#pragma options align = reset

Boolean		Cpm_IsCpm(DiskImageRec *imageRec);
void		Cpm_Catalog(DiskImageRec *imageRec);

#endif