/**********************************************************************/
/* This file contains the datastructures used by PFS                  */
/*                                                                    */
/* If you have specific programming plans I would like to know about  */
/* it. I can help you with any problems and can supply extra          */
/* information, if so needed. I can supply you with PFS specific      */
/* code too, like allocation or anode routines.                       */
/* Of course you're free to do whatever you want with the tools you   */
/* make, but if you'll allow me, I would like to add them to the PFS  */
/* package, or at least refer to it in the documentation.             */
/*                                                                    */
/**********************************************************************/

// max length of filename, diskname and comment
#define FNSIZE 108
#define DNSIZE 32
#define CMSIZE 80

#define ID_PFS_DISK		(0x50465300L)	//'PFS\0'


/**********************************************************************/
/*                            Blockformat                             */
/**********************************************************************/

#define DBLKID	0x4442	//'DB'
#define ABLKID	0x4142	//'AB'


/* Directory blocks
**
** dirblock structure:
**
** This is what a dirblock looks like on disk. Reserved fields are for
** future extension.
**
** DirEntry structure:
**
** The 'type', 'creationtime' and 'protection' are, accept for their
** size, in DOS format.
** The filename and the comment are dynamic: size followed by that
** number of characters (like BSTRs). The comment directly follows the
** filename. The 'next' field contains the size of the direntry. This
** should always be even. The end of the directoryblock is marked by
** next = 0.
**
** The size of a direntry can be calculated by:
** size = (sizeof(struct direntry) + strlen(name) + strlen(comment))&0xfe
*/
#define DB_HEADSPACE (6 + 10)
#define DB_ENTRYSPACE (TD_SECTOR - 16)

struct dirblock			// voor disk only (WORD blokno), timestamp later..
{
	UWORD id;					// 'DB'
	ULONG reserved_1[2];
	UWORD reserved_2;
	UWORD anodenr;				// anodenr belonging to this directory (points to FIRST block of dir)
	UWORD parent;				// parent; ANODE_ROOTDIR = root
	UBYTE entries[DB_ENTRYSPACE];	// entries, fini by NULL
};

struct direntry
{
	UBYTE next;			// sizeof direntry
	BYTE  type;			// dir, file, link etc
	UWORD anode;		// anode nummer
	ULONG size;			// sizeof file
	UWORD creationday;	// days since Jan. 1, 1978 (ala ADOS; WORD ipv LONG)
	UWORD creationminute; // minutes past midnight
	UWORD creationtick;	// ticks past minute
	UBYTE protection;	// protection bits (ala ADOS)
	UBYTE nlength;		// length of filename
	UBYTE startofname;	// filename, followed by commentlen & comment
	UBYTE pad;			// make size even
};

// comment: de is struct direntry *
#define COMMENT(de) ((UBYTE*)(&((de)->startofname) + (de)->nlength))
#define OBJECTNAME(de) ((UBYTE*)(&(de->startofname)))

// get a pointer to the next direntry; de = struct direntry *
#define NEXTENTRY(de) ((struct direntry*)((UBYTE*)(de) + (de)->next))

// get a pointer to the firste direntry; blok is struct dirblock *
#define FIRSTENTRY(blok) ((struct direntry*)((blok)->blk.entries))

#define ENTRYLEN(de, comment) ((sizeof(struct direntry) + (de)->nlength + strlen(comment))&0xfffe)


/* Anode blocks and Anodes
**
** All space on disk is allocated by anodes. Anodes are runlength
** lists: a blocknr and a number indicating how many blocks are
** allocated by this anode.
**
** The anodes are organized in an array spread over anodeblocks. The
** anodenr is the index in this array. The anodeblock themselves are
** allocated in the rootblock.
**
** The first 6 anodes have a special function, they allocate
** freespace, the rootdirectory and perform system functions
**
** Every anodeblock contains ANODESINBLOCK anodes.
**
** struct anode:
**
** The anode itself. The 'clustersize' is the number of blocks being
** allocated. The allocated blocks are 'blocknr' up to (but not 
** including)  'blocknr' + 'clustersize'. The 'next' field is a link
** to another anode where the allocation continues. At the end of the
** anode chain next = 0.
**
** If blocknr = 0 the anode is free, and can be reused. If the anode
** contains { 0, -1, next} the anode is reserved by a file or
** directory but is not yet in use. PS: empty anodes, with clustersize
** 0, are valid.
** Vrijgeven van een anode:  clustersize = 0; blocknr =  0; next = 0;
*/

#define ANODESINBLOCK	82

/* anode 0 is reserved; EOF; must be 0, -1, 0 (use for endoflist detection)
** ANODE_FREESPACE MUST have ANODE_RESERVED linked. All reserved blocks
** must be at the end of the chain. There may not be 'mixed' anodes
** anywhere
**
** Anodes < 6 may be {0,0,0} but should NEVER be allocated for other
** purposes.
** ANODE_FREESPACE, ANODE_BADBLOCKS and ANODE_EOF should always be
** reserved {0, -1, 0}.
** ANODE_USERFIRST is the first anode usable for files etc.
*/
#define ANODE_EOF			0
#define ANODE_FREESPACE		1
#define ANODE_RESERVED		2
#define ANODE_TOBEFREED		3
#define ANODE_BADBLOCKS		4
#define ANODE_ROOTDIR		5
#define ANODE_USERFIRST		6

struct anode
{
	UWORD clustersize;
	UWORD blocknr;
	UWORD next;
};


struct anodeblock
{
	UWORD id;
	ULONG reserved_1[2];
	UWORD seqnr;		// sequence number of anodeblock (starts with 0)
	UWORD next;			// chain, 0 = end
	UWORD pad;
	struct anode nodes[ANODESINBLOCK];
	ULONG reserved_2;
};

/* Data blocks
*/
#define DATAINBLOCK	512

struct datablock
{
	UBYTE data[DATAINBLOCK];	// only data
};


/* Rootblock and bootblocks
*/
#define BOOTBLOCK1 0
#define BOOTBLOCK2 1
#define ROOTBLOCK 2

struct rootblock
{
	LONG  disktype;			// 'FDOS' same place as AmigaDos puts id
	LONG  reserved_1[2];
	UWORD creationday;		// days since Jan. 1, 1978 (ala ADOS; WORD ipv LONG)
	UWORD creationminute; 	// minutes past midnight
	UWORD creationtick;		// ticks past minute, not really needed
	UWORD protection;		// protection bits (ala ADOS)
	UBYTE diskname[32];		// DSTR of diskname;
	UWORD firstreserved;	// first reserved block (must be 0)
	UWORD lastreserved;		// end of reserved area
	LONG  reserved_2[10];
	WORD anodeblocks[42];	// 0->notallocated
	UBYTE reserved_3[DATAINBLOCK - 96 - 2*42];
};

struct bootblock
{
	LONG disktype;
	UBYTE reserved[508];
};


/* Reserved areas and other notes
**
** Anode and directory blocks are allocated in a special reserved area
** at the start of the partition. The reserved area bounderies are defined
** in the rootblock and vary with disksize. The reserved area can
** overflow in the normal area and vice versa.
**
** Directories are allocated by anodes, but they don't support
** clustersizes other than 1.
**
** Allocation strategy
**
** PFS always always writes directory and anodeblocks in a different
** place than they came from. The same for files: overwritten files are
** actually written somewhere else. A kind of 'freed first reused last'
** (FFRL) algorithm is used, so old versions stay on the disk for a long
** time. Sometimes PFS will abandon FFRL if this can prevent fragmentation.
** Recovery tools could make use of this: old versions can be restored
** if the current one is corrupt, and deleted files can be recovered.
**
** Update strategy
**
** Dirty anode and dirblocks are written to disk before the rootblock.
** Because the rootblock allocates the anodeblock which allocate the
** directories, and every anode is written on a new position, all
** changes take effect at the moment the rootblock is written. Before
** that seemingly nothing has changed.
*/


