In comp.sys.apple2 Michael Pender wrote: > Omigosh!! ProBlock version 1.0 could do that back in 1990!! > > Seriously though, I thought the format would be a bit more complex. Nope. Common Apple II disk formats: - Universal Disk Images (.2mg) Spec available on www.a2central.com as FTN $e0/0130. - DiskCopy 4.2 (.dsk) Spec available as FTN $e0/0005. - Copy II Plus (.img) Unadorned sectors, but in "raw" order. - Sim //e HDV images (.hdv) Unadorned sectors with a header. - Dalton's Disk Disintegrator (DDD v2.1+, DDD Pro v1.1+) (.ddd) Not described anywhere, but a source code disassembly is available. - Unadorned sector-format files (.po, .do, .raw, .hdv) The "DSK" format, which is usually in DOS or ProDOS sector order. DiskCopy 6 (.dc6) is usually just this. - Unadorned nibble-format files (.nib, .nb2) Raw nibbles, with 6384 or 6656 bytes per track. - ShrinkIt (NuFX) compressed disk images (.shk, .sdk) Spec available as $e0/8002. There are a few others (e.g. Davex archived volumes, $e0/8004), and I think Apple Oasis has its own. The above are often stored with gzip compression or (ick) in a ZIP archive on FTP sites. The real trick is figuring out whether an unadorned 5.25" image is in DOS, ProDOS, or "physical" order. Also, don't make the mistake I made in CiderPress 1.0 and assume that all volumes are going to be multiples of something nice, like 4K. It's possible to have odd-sized ProDOS volumes, mostly from Windows/Mac/UNIX utilities that create a full-sized HD volume but don't actually write the blocks until necessary. -- Send mail to fadden@fadden.com (Andy McFadden) - http://www.fadden.com/ CD-Recordable FAQ - http://www.cdrfaq.org/ CiderPress Apple II archive utility for Windows - http://www.faddensoft.com/ Fight Internet Spam - http://spam.abuse.net/spam/ & http://spamcop.net/ In comp.sys.apple2 Bill Garber wrote: > You all are making DSK too complicated. All they are is this. > Track 1, sector 1, byte 1 is byte 1 of the file, and so on each byte > from the disk is sequential in the file. Nothing more. Track 0, sector 0, byte 0 starts at byte 0 of the file. This is because, for all possible sector skews, sector 0 is always in the same place. Track 0, sector 1, byte 0 could be in one of three different places. That's the difference between .PO, .DO, and Copy ][+ .IMG. If there were a CP/M disk-image utility you'd have four possibilities. A DOS utility will read sector 0 through sector 15 and write them out in that order. A ProDOS utility will read block 0 through block 7 and write them out as 512-byte blocks. Unfortunately, block 0 is track 0 sector 0 combined with track 0 sector 2, *not* track 0 sector 1. This means that disk images created with DOS RWTS-based utilities are different from images created with ProDOS utilities like ShrinkIt. CiderPress looks for DOS, ProDOS, Pascal, CP/M, and RDOS filesystems in "DOS", "ProDOS", and "physical" sector ordering every time you open a disk image. If the file has a helpful extension (like ".do"), it knows to try that first. However, until it successfully identifies a filesystem, it can't know the sector ordering, which is why for disks with no recognizable filesystem (like games with custom loaders) it will ask you to choose one. Because sector 0 is always in the right place, finding a DOS 3.3 VTOC isn't sufficient... we know it's DOS 3.3, but we don't know what order the sectors are, so it's necessary to examine the rest of the catalog track. Which gets even more fun when you deal with 32-sector images on 800K disks. Some formats, such as 2MG, are kind enough to tell you the order. Others, like ShrinkIt, are always ProDOS-order. In general, though, you have to keep poking at it until something looks familiar. -- Send mail to fadden@fadden.com (Andy McFadden) - http://www.fadden.com/ CD-Recordable FAQ - http://www.cdrfaq.org/ CiderPress Apple II archive utility for Windows - http://www.faddensoft.com/ Fight Internet Spam - http://spam.abuse.net/spam/ & http://spamcop.net/ In comp.sys.apple2 Michael Pender wrote: > I remember that back-in-the-day (BITD?) there were disk utilities that > changed the interleave between DOS disk sectors to speed up file transfer. > That is, physical sector 0 and logical sector 0 are always the same, but > physical sector 1 and logical sector 1 could be different. Is this a > similar problem? It's similar. Sectors have a physical order (by default written straight from 0 to 15) and a logical order (skewed around). The mapping between them is fixed for each filesystem, and is different for DOS, Pascal/ProDOS, and CP/M. (It happens that logical sector 0 is mapped to physical sector 0 in all established formats, which means that you'll see track 0 sector 0 at the start of every disk image regardless of ordering.) The "fast skew" programs couldn't change the mapping between physical and logical sectors, but they could write the physical sectors in a different order by reformatting the tracks. Quality Software's Bag of Tricks was one such utility. This sort of re-skewing would only be noticeable by software that handles nibble images, because an RWTS read of sector 5 will always map to the same physical sector. The trouble with ".DSK" images is that they were either written as a series of 256-byte sectors (sector 0, 1, 2, 3, ...) or a series of 512-byte ProDOS/Pascal blocks (sector 0, 2, 4, 6, ...). If you assume that track 0 sector 1 starts 256 bytes into the disk image file, you may be disappointed. Naming 5.25" disk images with ".DSK" is a mistake, IMHO. They should be ".PO" or ".DO" to identify the contents unambiguously. If I recall correctly, early versions of KEGS assumed that 5.25" disks were in DOS order and 3.5" disks were in ProDOS order, so failing to label a disk image could get you into trouble. -- Send mail to fadden@fadden.com (Andy McFadden) - http://www.fadden.com/ CD-Recordable FAQ - http://www.cdrfaq.org/ CiderPress Apple II archive utility for Windows - http://www.faddensoft.com/ Fight Internet Spam - http://spam.abuse.net/spam/ & http://spamcop.net/ CPM: i've got all the C++ routines to set/read all these items too, if you want them, lemme know // 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