From david@uow.edu.au Tue Nov 28 10:05:23 CST 2000

Dennis Jenkins <dennis@usb.com> writes:
>Does anyone have any C/C++ code that can read (write too?!?) files from
>a disk image of a prodos disk under win32/linux/dos/anything?  I found
>some code to do it for dos 3.3, and I'm looking for the prodos
>equivalent.

I have some code that will (almost) copy all the files from a ProDOS image
and make a directory tree on the system on which it is run (eg UNIX). Feel free
to adapt this code for your own use. Note that it does not support Tree files.
This is just based on the code in Apple's ProDOS Technical Reference Manual
for recursively reading a directory. Run it as "protar < image_file".
Let me know if you find it useful.

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