💾 Archived View for mirrors.apple2.org.za › archive › ground.icaen.uiowa.edu › MiscInfo › Programmin… captured on 2023-01-29 at 10:17:27.

View Raw

More Information

-=-=-=-=-=-=-


"Sheldon Simms" <sheldon@interprice.com> writes:
>Speaking of which, I'd love to get a doc of the ProDOS filesystem. I own
>the ProDOS reference manual but it's inaccessible to me at the moment
>(and I don't imagine I'll be able to get it back until the end of the
>year). I managed to reverse engineer pretty much everything by poking
>around a disk image with a hex editor and relying on memory, but I'd like
>to see a doc before December just be to sure I got it right.

I have the ProDOS Technical Reference manual (1st & 2nd editions) as well
as Beneath Apple ProDOS which all document the filesystem format. Here is
some code that can read a ProDOS filesystem (except for Tree files) to
let you cross check your code.

prodos.h:
/*
 *	Directory pointers
 */
#define	Prev(x)		(x[0] + (x[1] << 8))
#define	Next(x)		(x[2] + (x[3] << 8))
/*
 *	Fields common to vol header, sub dir headers & file entries
 */
#define	Storage_type(x)	(x[0] >> 4)
#define	Name_length(x)	(x[0] & 15)
#define	Active(x)	(x[0] != 0)
#define	File_name(x)	(x + 1)
#define	Creation(x)	(x[0x1a] + (x[0x1b] << 8) + (x[0x18] << 16) + (x[0x19] << 24))
#define	Version(x)	(x[0x1c])
#define	Min_version(x)	(x[0x1d])
#define	Access(x)	(x[0x1e])
/*
 *	File entry only
 */
#define	File_type(x)	(x[0x10])
#define	Key_pointer(x)	(x[0x11] + (x[0x12] << 8))
#define	Blocks_used(x)	(x[0x13] + (x[0x14] << 8))
#define	Eof(x)	(x[0x15] + (x[0x16] << 8) + (x[0x17] << 16))
#define	Aux_type(x)	(x[0x1f] + (x[0x20] << 8))
#define	Last_mod(x)	(x[0x23] + (x[0x24] << 8) + (x[0x21] << 16) + (x[0x22] << 24))
#define	Header_pointer(x)	(x[0x25] + (x[0x26] << 8))
/*
 *	Vol & sub dir headers
 */
#define	Entry_length(x)	(x[0x1f])
#define	Entries_per_block(x)	(x[0x20])
#define	File_count(x)	(x[0x21] + (x[0x22] << 8))
/*
 *	Vol header only
 */
#define	Bit_map_pointer(x)	(x[0x23] + (x[0x24] << 8))
#define	Total_blocks(x)	(x[0x25] + (x[0x26] << 8))
/*
 *	Sub dir header only
 */
#define	Parent_entry_number(x)	(x[0x25])
#define	Parent_entry_length(x)	(x[0x26])
#define	Parent_pointer(x)	(x[0x23] + (x[0x24] << 8))
/*
 *	Date format
 */
#define	year(x)		((x >> 25) & 127)
#define	month(x)	((x >> 21) & 15)
#define	day(x)		((x >> 16) & 31)
#define	hour(x)		((x >> 8) & 31)
#define	minute(x)	(x & 63)
/*
 *	Indirect block function
 */
#define	indirect(block,index)	(block[index] + (block[index + 256] << 8))
/*
 *	ProDOS constants
 */
#define	PRODOS_BS	512
#define	PRODOS_ROOT	2

protar.c:
#include	<stdio.h>
#include	<fcntl.h>
#include	"prodos.h"

extern	long	lseek();
extern	char	*sprintf();

char	err[80], filename[64];

main(argc, argv)
int	argc;
char	*argv[];
{
	ReadDir(PRODOS_ROOT, 0);
}

ReadDir(blocknum, filelen)
unsigned	blocknum, filelen;
{
	unsigned char	block[PRODOS_BS];
	int	elen, epb, fc, be, ae;
	unsigned char	*ep;

	if (lseek(0, (long) (PRODOS_BS * blocknum), 0) == -1L ||
		read(0, (char *) block, PRODOS_BS) == -1)
	{
		sprintf(err, "Error reading block %d", blocknum);
		perror(err);
		return;
	}
	ep = block + 4;
	if (filelen == 0)
	{
		sprintf(filename, "%.*s", Name_length(ep), File_name(ep));
		filelen = Name_length(ep);
	}
	elen = Entry_length(ep);
	epb = Entries_per_block(ep);
	fc = File_count(ep);
	ep += elen;
	be = 2;
	ae = 0;
	while (ae < fc)
	{
		if (Active(ep))
		{
			ProcessEntry(ep, filelen);
			++ae;
		}
		if (ae < fc)
			if (be == epb)
			{
				if (Next(block) == 0)
				{
					fprintf(stderr, "Short Directory. ae = %d fc = %d blocknum = %d\n", ae, fc, blocknum);
					return;
				}
				blocknum = Next(block);
				if (lseek(0, (long) (PRODOS_BS * blocknum), 0) == -1L ||
					read(0, (char *) block, PRODOS_BS) == -1)
				{
					sprintf(err, "Error reading block %d", blocknum);
					perror(err);
					return;
				}
				be = 1;
				ep = block + 4;
			}
			else
			{
				ep += elen;
				++be;
			}
	}
}

ProcessEntry(ep, filelen)
unsigned char	*ep;
unsigned	filelen;
{
	sprintf(filename + filelen, "/%.*s", Name_length(ep), File_name(ep));
	filelen += Name_length(ep) + 1;
	ProcessFile(ep);
	if (File_type(ep) == 0x0f)
		ReadDir(Key_pointer(ep), filelen);
	filelen -= Name_length(ep) + 1;
}

unsigned	char	masterblock[PRODOS_BS], ptrblock[PRODOS_BS], datablock[PRODOS_BS];

ProcessFile(ep)
unsigned char	*ep;
{
	int	fd, len, i;

#ifdef	DEBUG
	puts(filename);
#else
	switch (Storage_type(ep))
	{
	case	0:	/* Inactive */
		break;
	case	1:	/* Seedling */
		if ((fd = open(filename, O_WRONLY|O_CREAT, 0777)) < 0)
			perror(filename);
		else
		{
			len = Eof(ep);
			if (lseek(0, (long) (PRODOS_BS * Key_pointer(ep)), 0) == -1L ||
			    read(0, datablock, len) != len ||
			    write(fd, datablock, len) != len ||
			    close(fd) == -1)
				perror(filename);
		}
		break;
	case	2:	/* Sapling */
		if ((fd = open(filename, O_WRONLY|O_CREAT, 0777)) < 0)
			perror(filename);
		else
		{
			len = Eof(ep);
			if (lseek(0, (long) (PRODOS_BS * Key_pointer(ep)), 0) == -1L ||
			    read(0, ptrblock, PRODOS_BS) != PRODOS_BS)
				perror(filename);
			else
			{
				for (i = 0; len >= PRODOS_BS; ++i, len -= PRODOS_BS)
					if (lseek(0, (long) PRODOS_BS * indirect(ptrblock, i), 0) == -1L ||
					    read(0, datablock, PRODOS_BS) != PRODOS_BS ||
					    write(fd, datablock, PRODOS_BS) != PRODOS_BS)
						perror(filename);
				if (len)
					if (lseek(0, (long) PRODOS_BS * indirect(ptrblock, i), 0) == -1L ||
					    read(0, datablock, len) != len ||
					    write(fd, datablock, len) != len)
						perror(filename);
				if (close(fd) == -1)
					perror(filename);
			}
		}
		break;
	case	3:	/* Tree */
		break;
	case	0xd:	/* SubDir file entry */
		if (mkdir(filename, 0777) == -1)
			perror(filename);
		break;
	case	0xe:	/* SubDir header */
		break;
	case	0xf:	/* Vol header */
		break;
	default:	/* ??? */
		fprintf(stderr, "%s: unknown storage_type %x\n", filename, Storage_type(ep));
		break;
	}
#endif
}
--
David Wilson  School of IT & CS, Uni of Wollongong, Australia