💾 Archived View for mirrors.apple2.org.za › archive › ground.icaen.uiowa.edu › MiscInfo › Programmin… captured on 2023-01-29 at 10:17:27.
-=-=-=-=-=-=-
"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