diff --git a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFileEntry.java b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFileEntry.java index 96772ad..4c9c238 100644 --- a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFileEntry.java +++ b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFileEntry.java @@ -207,6 +207,14 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry { return (filetype >= 0x80 && filetype <= 0x8f); } + /** + * Indicate if this is a GEOS file. + */ + public boolean isForkedFile() { + int storageType = AppleUtil.getUnsignedByte(readFileEntry()[0x00]); + return ((storageType & 0x50) == 0x50); + } + /** * Get the key pointer. This is either the data block (seedling), * index block (sapling), or master index block (tree). @@ -499,6 +507,15 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry { getDisk().setFileData(this, data); } + /** + * Set the file data, with the expectation that both data and resource forks + * are present (storage type $05). See: + * http://www.1000bit.it/support/manuali/apple/technotes/pdos/tn.pdos.25.html + */ + public void setFileData(byte[] dataFork, byte[] resourceFork) throws DiskFullException { + getDisk().setFileData(this, dataFork, resourceFork); + } + /** * Get the suggested FileFilter. This appears to be operating system * specific, so each operating system needs to implement some manner diff --git a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java index 04bc760..5e5bad5 100644 --- a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java @@ -719,6 +719,178 @@ public class ProdosFormatDisk extends FormattedDisk { } } + /** + * Set the data associated with the specified ProdosFileEntry into sectors + * on the disk. Automatically grows the filesystem structures from seedling + * to sapling to tree. + */ + // TODO: the writing of a single fork can be factored out... it is very common to routines nearby. + protected void setFileData(ProdosFileEntry fileEntry, byte[] dataFork, byte[] resourceFork) + throws DiskFullException { + + // compute free space and see if the data will fit! + int numberOfDataBlocks = (dataFork.length + BLOCK_SIZE - 1) / BLOCK_SIZE + + (resourceFork.length + BLOCK_SIZE - 1) / BLOCK_SIZE; + int numberOfBlocks = numberOfDataBlocks; + if (numberOfBlocks > 1) { + numberOfBlocks+= ((numberOfDataBlocks-1) / 256) + 1; // that's 128K + if (numberOfDataBlocks > 256) { + numberOfBlocks++; + } + } + if (numberOfBlocks > getFreeBlocks() + fileEntry.getBlocksUsed()) { + throw new DiskFullException(textBundle. + format("ProdosFormatDisk.NotEnoughSpaceOnDiskError", //$NON-NLS-1$ + numberOfBlocks, getFreeBlocks())); + } + // free "old" data and just rewrite stuff... + freeBlocks(fileEntry); + byte[] bitmap = readVolumeBitMap(); + int blockNumber = fileEntry.getKeyPointer(); + if (blockNumber == 0) { + blockNumber = findFreeBlock(bitmap); + } + int blockCount = 0; + int extendedKeyBlockNumber = findFreeBlock(bitmap); + setBlockUsed(bitmap, extendedKeyBlockNumber); + byte[] extendedKeyBlockData = new byte[BLOCK_SIZE]; + int indexBlockNumber = 0; + byte[] indexBlockData = null; + int masterIndexBlockNumber = 0; + byte[] masterIndexBlockData = new byte[BLOCK_SIZE]; + + int offset = 0; + numberOfDataBlocks = (dataFork.length + BLOCK_SIZE - 1) / BLOCK_SIZE ; + while (offset < dataFork.length) { + blockNumber = findFreeBlock(bitmap); + setBlockUsed(bitmap, blockNumber); + blockCount++; + byte[] blockData = new byte[BLOCK_SIZE]; + int length = Math.min(BLOCK_SIZE, dataFork.length - offset); + System.arraycopy(dataFork,offset,blockData,0,length); + writeBlock(blockNumber, blockData); + if (numberOfDataBlocks > 1) { + // growing to a tree file + if (offset > 0 && (offset / BLOCK_SIZE) % 256 == 0) { + if (masterIndexBlockNumber == 0) { + masterIndexBlockNumber = findFreeBlock(bitmap); + setBlockUsed(bitmap, masterIndexBlockNumber); + blockCount++; + } + writeBlock(indexBlockNumber, indexBlockData); + indexBlockData = null; + indexBlockNumber = 0; + } + // new index block + if (indexBlockData == null) { // sapling files + indexBlockNumber = findFreeBlock(bitmap); + indexBlockData = new byte[BLOCK_SIZE]; + setBlockUsed(bitmap, indexBlockNumber); + blockCount++; + // This is only used for Tree files (but we always record it): + int position = (offset / (BLOCK_SIZE * 256)); + byte low = (byte)(indexBlockNumber % 256); + byte high = (byte)(indexBlockNumber / 256); + masterIndexBlockData[position] = low; + masterIndexBlockData[position + 0x100] = high; + } + // record last block position in index block + int position = (offset / BLOCK_SIZE) % 256; + byte low = (byte)(blockNumber % 256); + byte high = (byte)(blockNumber / 256); + indexBlockData[position] = low; + indexBlockData[position + 0x100] = high; + } + offset+= BLOCK_SIZE; + } + AppleUtil.setWordValue(extendedKeyBlockData,0x03,numberOfDataBlocks); // Set the number of blocks used + AppleUtil.set3ByteValue(extendedKeyBlockData,0x05, dataFork.length); // Set the number of bytes used + if (numberOfDataBlocks == 1) { + extendedKeyBlockData[0] = 1; // Set the seedling ProDOS storage type + AppleUtil.setWordValue(extendedKeyBlockData,1,blockNumber); // Set the master block number + } else if (numberOfDataBlocks <= 256) { + writeBlock(indexBlockNumber, indexBlockData); + AppleUtil.setWordValue(extendedKeyBlockData,1,indexBlockNumber); // Set the master block number + extendedKeyBlockData[0] = 2; // Set the sapling ProDOS storage type + } else { + writeBlock(indexBlockNumber, indexBlockData); + writeBlock(masterIndexBlockNumber, masterIndexBlockData); + AppleUtil.setWordValue(extendedKeyBlockData,1,masterIndexBlockNumber); // Set the master block number + extendedKeyBlockData[0] = 3; // Set the tree ProDOS storage type + } + blockCount++; // To account for extendedKeyBlock + + offset = 0; + indexBlockNumber = 0; + indexBlockData = null; + masterIndexBlockNumber = 0; + numberOfDataBlocks = (resourceFork.length + BLOCK_SIZE - 1) / BLOCK_SIZE; + while (offset < resourceFork.length) { + if (blockCount > 0) blockNumber = findFreeBlock(bitmap); + setBlockUsed(bitmap, blockNumber); + blockCount++; + byte[] blockData = new byte[BLOCK_SIZE]; + int length = Math.min(BLOCK_SIZE, resourceFork.length - offset); + System.arraycopy(resourceFork,offset,blockData,0,length); + writeBlock(blockNumber, blockData); + if (numberOfDataBlocks > 1) { + // growing to a tree file + if (offset > 0 && (offset / BLOCK_SIZE) % 256 == 0) { + if (masterIndexBlockNumber == 0) { + masterIndexBlockNumber = findFreeBlock(bitmap); + setBlockUsed(bitmap, masterIndexBlockNumber); + blockCount++; + } + writeBlock(indexBlockNumber, indexBlockData); + indexBlockData = null; + indexBlockNumber = 0; + } + // new index block + if (indexBlockData == null) { // sapling files + indexBlockNumber = findFreeBlock(bitmap); + indexBlockData = new byte[BLOCK_SIZE]; + setBlockUsed(bitmap, indexBlockNumber); + blockCount++; + // This is only used for Tree files (but we always record it): + int position = (offset / (BLOCK_SIZE * 256)); + byte low = (byte)(indexBlockNumber % 256); + byte high = (byte)(indexBlockNumber / 256); + masterIndexBlockData[position] = low; + masterIndexBlockData[position + 0x100] = high; + } + // record last block position in index block + int position = (offset / BLOCK_SIZE) % 256; + byte low = (byte)(blockNumber % 256); + byte high = (byte)(blockNumber / 256); + indexBlockData[position] = low; + indexBlockData[position + 0x100] = high; + } + offset+= BLOCK_SIZE; + } + AppleUtil.setWordValue(extendedKeyBlockData,0x103,numberOfDataBlocks); // Set the number of blocks used + AppleUtil.set3ByteValue(extendedKeyBlockData,0x105, resourceFork.length); // Set the number of bytes used + if (numberOfDataBlocks == 1) { + extendedKeyBlockData[0x100] = 1; // Set the seedling ProDOS storage type + AppleUtil.setWordValue(extendedKeyBlockData,0x101,blockNumber); // Set the master block number + } else if (numberOfDataBlocks <= 256) { + writeBlock(indexBlockNumber, indexBlockData); + AppleUtil.setWordValue(extendedKeyBlockData,0x101,indexBlockNumber); // Set the master block number + extendedKeyBlockData[0x100] = 2; // Set the sapling ProDOS storage type + } else { + writeBlock(indexBlockNumber, indexBlockData); + writeBlock(masterIndexBlockNumber, masterIndexBlockData); + AppleUtil.setWordValue(extendedKeyBlockData,0x101,masterIndexBlockNumber); // Set the master block number + extendedKeyBlockData[0x100] = 3; // Set the tree ProDOS storage type + } + writeBlock(extendedKeyBlockNumber, extendedKeyBlockData); + + fileEntry.setKeyPointer(extendedKeyBlockNumber); + fileEntry.setBlocksUsed(blockCount); + fileEntry.setEofPosition(dataFork.length+resourceFork.length); + fileEntry.setLastModificationDate(new Date()); + writeVolumeBitMap(bitmap); + } + /** * Set the data associated with the specified ProdosFileEntry into sectors * on the disk. Take GEOS file structures into account. @@ -1153,6 +1325,7 @@ public class ProdosFormatDisk extends FormattedDisk { * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() */ public DirectoryEntry createDirectory() throws DiskFullException { + System.out.println("What?!?"); throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ } } diff --git a/src/com/webcodepro/shrinkit/Utilities.java b/src/com/webcodepro/shrinkit/Utilities.java index fd342d8..d5318be 100644 --- a/src/com/webcodepro/shrinkit/Utilities.java +++ b/src/com/webcodepro/shrinkit/Utilities.java @@ -67,8 +67,11 @@ public class Utilities ImageOrder imageOrder = new ProdosOrder(layout); FormattedDisk[] disks = ProdosFormatDisk.create(fileName, "APPLECOMMANDER", imageOrder); ProdosFormatDisk pdDisk = (ProdosFormatDisk)disks[0]; + ThreadRecord dataFork, resourceFork; for (HeaderBlock b : a.getHeaderBlocks()) { ProdosFileEntry newFile = null; + dataFork = null; + resourceFork = null; for (ThreadRecord r : b.getThreadRecords()) { try { @@ -83,19 +86,19 @@ public class Utilities break; case DATA_FORK: // This is a normal-ish file - newFile = (ProdosFileEntry) pdDisk.createFile(); - if (newFile != null) { - newFile.setFileData(readThread(r)); - } + dataFork = r; break; case DISK_IMAGE: dmgBuffer = readThread(r); break; case RESOURCE_FORK: + // This is a resource fork - we're talking GSOS FST here + resourceFork = r; break; case FILENAME: break; default: + System.out.println("ERRR, What?"); // Hmmm, this should not occur - but let us not fret about it. break; } @@ -105,13 +108,29 @@ public class Utilities System.out.println(ex); } } - if (newFile != null) { - newFile.setFilename(b.getFilename()); - newFile.setFiletype(b.getFileType()); - newFile.setAuxiliaryType((int)b.getExtraType()); - newFile.setCreationDate(b.getCreateWhen()); - newFile.setLastModificationDate(b.getModWhen()); - newFile = null; + try + { + if (dataFork != null) { + newFile = (ProdosFileEntry) pdDisk.createFile(); + if (newFile != null) { + if (resourceFork != null) { + newFile.setFileData(readThread(dataFork),readThread(resourceFork)); + newFile.setStorageType(0x05); + } else { + newFile.setFileData(readThread(dataFork)); + } + newFile.setFilename(b.getFilename()); + newFile.setFiletype(b.getFileType()); + newFile.setAuxiliaryType((int)b.getExtraType()); + newFile.setCreationDate(b.getCreateWhen()); + newFile.setLastModificationDate(b.getModWhen()); + newFile = null; + } + } + } + catch (Exception ex) + { + System.out.println(ex); } } if (dmgBuffer != null)