From 87eca0095c48d0ff12b0377991bbcfdaa3d68625 Mon Sep 17 00:00:00 2001 From: Date: Wed, 8 Aug 2012 03:10:57 +0000 Subject: [PATCH] Implement directory creation in ProDOS Side effect: opened up the previously private 'Name' class in ui; this simplifies directory handling elsewhere, as it automatically creates a directory structure to a pathed file. --- .../storage/DirectoryEntry.java | 2 +- .../storage/os/cpm/CpmFormatDisk.java | 8 ++ .../storage/os/dos33/DosFormatDisk.java | 8 ++ .../os/gutenberg/GutenbergFormatDisk.java | 8 ++ .../storage/os/nakedos/NakedosFormatDisk.java | 8 ++ .../storage/os/pascal/PascalFormatDisk.java | 8 ++ .../prodos/ProdosCommonDirectoryHeader.java | 8 +- .../storage/os/prodos/ProdosCommonEntry.java | 16 +++- .../os/prodos/ProdosDirectoryEntry.java | 4 +- .../storage/os/prodos/ProdosFormatDisk.java | 80 ++++++++++++++++++- .../os/prodos/ProdosSubdirectoryHeader.java | 24 +++++- .../storage/os/rdos/RdosFormatDisk.java | 8 ++ src/com/webcodepro/applecommander/ui/ac.java | 20 ++++- src/com/webcodepro/shrinkit/Utilities.java | 5 +- 14 files changed, 191 insertions(+), 16 deletions(-) diff --git a/src/com/webcodepro/applecommander/storage/DirectoryEntry.java b/src/com/webcodepro/applecommander/storage/DirectoryEntry.java index 17a2c81..72ff7e3 100644 --- a/src/com/webcodepro/applecommander/storage/DirectoryEntry.java +++ b/src/com/webcodepro/applecommander/storage/DirectoryEntry.java @@ -47,7 +47,7 @@ public interface DirectoryEntry { /** * Create a new DirectoryEntry. */ - public DirectoryEntry createDirectory() throws DiskFullException; + public DirectoryEntry createDirectory(String name) throws DiskFullException; /** * Identify if additional directories can be created. This diff --git a/src/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java index a759af8..332d9f2 100644 --- a/src/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java @@ -537,4 +537,12 @@ public class CpmFormatDisk extends FormattedDisk { public DirectoryEntry createDirectory() throws DiskFullException { throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ } + + /** + * Create a new DirectoryEntry. + * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() + */ + public DirectoryEntry createDirectory(String name) throws DiskFullException { + throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ + } } diff --git a/src/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java index e01b91c..e11a9f1 100644 --- a/src/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java @@ -745,4 +745,12 @@ public class DosFormatDisk extends FormattedDisk { public DirectoryEntry createDirectory() throws DiskFullException { throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ } + + /** + * Create a new DirectoryEntry. + * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() + */ + public DirectoryEntry createDirectory(String name) throws DiskFullException { + throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ + } } diff --git a/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java index 0a242c5..0c29b52 100644 --- a/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java @@ -715,4 +715,12 @@ public class GutenbergFormatDisk extends FormattedDisk { public DirectoryEntry createDirectory() throws DiskFullException { throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ } + + /** + * Create a new DirectoryEntry. + * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() + */ + public DirectoryEntry createDirectory(String name) throws DiskFullException { + throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ + } } diff --git a/src/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java index 63049bb..0bd774d 100644 --- a/src/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java @@ -532,4 +532,12 @@ public class NakedosFormatDisk extends FormattedDisk { public DirectoryEntry createDirectory() throws DiskFullException { throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ } + + /** + * Create a new DirectoryEntry. + * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() + */ + public DirectoryEntry createDirectory(String name) throws DiskFullException { + throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ + } } diff --git a/src/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java index f8842ab..7e2226d 100644 --- a/src/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java @@ -664,4 +664,12 @@ public class PascalFormatDisk extends FormattedDisk { public DirectoryEntry createDirectory() throws DiskFullException { throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ } + + /** + * Create a new DirectoryEntry. + * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() + */ + public DirectoryEntry createDirectory(String name) throws DiskFullException { + throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ + } } diff --git a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonDirectoryHeader.java b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonDirectoryHeader.java index 3a8336e..85de71d 100644 --- a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonDirectoryHeader.java +++ b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonDirectoryHeader.java @@ -69,14 +69,14 @@ public class ProdosCommonDirectoryHeader extends ProdosCommonEntry { } /** - * Get the number of active entries in the volume directory. + * Get the number of active entries in the directory. */ public int getFileCount() { return AppleUtil.getWordValue(readFileEntry(), 0x21); } /** - * Set the number of active entries in the volume directory. + * Set the number of active entries in the directory. */ public void setFileCount(int fileCount) { byte[] data = readFileEntry(); @@ -119,14 +119,14 @@ public class ProdosCommonDirectoryHeader extends ProdosCommonEntry { } /** - * Get the total number of blocks on this volume. + * Get the total number of blocks on this volume (only valid for volume directory block). */ public int getTotalBlocks() { return AppleUtil.getWordValue(readFileEntry(), 0x25); } /** - * Set the total number of blocks on this volume. + * Set the total number of blocks on this volume (only valid for volume directory block). */ public void setTotalBlocks(int totalBlocks) { byte[] data = readFileEntry(); diff --git a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonEntry.java b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonEntry.java index c9ab03d..05acc27 100644 --- a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonEntry.java +++ b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosCommonEntry.java @@ -81,7 +81,7 @@ public class ProdosCommonEntry { System.arraycopy(data, offset, entry, 0, ENTRY_LENGTH); return entry; } - + /** * Indicates if this entry is empty - filled with $00. */ @@ -162,6 +162,20 @@ public class ProdosCommonEntry { setStorageType(0x03); } + /** + * Indicates if this is a subdirectory entry. + */ + public boolean isSubdirectory() { + return getStorageType() == 0x0d; + } + + /** + * Sets the storage type to a subdirectory entry. + */ + public void setSubdirectory() { + setStorageType(0x0d); + } + /** * Indicates if this is a subdirectory header entry. */ diff --git a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosDirectoryEntry.java b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosDirectoryEntry.java index fc3206b..4db1885 100644 --- a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosDirectoryEntry.java +++ b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosDirectoryEntry.java @@ -95,7 +95,7 @@ public class ProdosDirectoryEntry extends ProdosFileEntry implements DirectoryEn * Create a new DirectoryEntry. * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() */ - public DirectoryEntry createDirectory() throws DiskFullException { - return getDisk().createDirectory(); + public DirectoryEntry createDirectory(String name) throws DiskFullException { + return getDisk().createDirectory(getSubdirectoryHeader(), name); } } diff --git a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java index 1916823..870ee8f 100644 --- a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java @@ -1315,7 +1315,7 @@ public class ProdosFormatDisk extends FormattedDisk { public void setFileData(FileEntry fileEntry, byte[] fileData) throws DiskFullException { setFileData((ProdosFileEntry)fileEntry, fileData); } - + protected ProdosVolumeDirectoryHeader getVolumeHeader() { return volumeHeader; } @@ -1324,7 +1324,81 @@ public class ProdosFormatDisk extends FormattedDisk { * Create a new DirectoryEntry. * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() */ - public DirectoryEntry createDirectory() throws DiskFullException { - throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ + public DirectoryEntry createDirectory(String name) throws DiskFullException { + return createDirectory(getVolumeHeader(), name); + } + + /** + * Create a new DirectoryEntry. + * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() + */ + public DirectoryEntry createDirectory(ProdosCommonDirectoryHeader directory, String name) throws DiskFullException { + int blockNumber = directory.getFileEntryBlock(); + while (blockNumber != 0) { + byte[] block = readBlock(blockNumber); + int offset = 4; + while (offset+ProdosCommonEntry.ENTRY_LENGTH < BLOCK_SIZE) { + int value = AppleUtil.getUnsignedByte(block[offset]); + if ((value & 0xf0) == 0) { + // First, create a new block to contain our subdirectory + byte[] volumeBitmap = readVolumeBitMap(); + int newDirBlockNumber = findFreeBlock(volumeBitmap); + setBlockUsed(volumeBitmap, newDirBlockNumber); + // Clean out the block - it may have been recycled, and control structures need to be gone + byte[] cleanBlock = new byte[512]; + for (int i = 0;i<512;i++) + cleanBlock[i] = 0; + writeBlock(newDirBlockNumber, cleanBlock); + writeVolumeBitMap(volumeBitmap); + ProdosSubdirectoryHeader newHeader = new ProdosSubdirectoryHeader(this, newDirBlockNumber); + ProdosFileEntry subdirEntry = (ProdosFileEntry)createFile(newHeader); + subdirEntry.setFilename(name); + newHeader.setHousekeeping(); + newHeader.setCreationDate(new Date()); + newHeader.setParentPointer(blockNumber); + // Now, add an entry for this subdirectory + ProdosDirectoryEntry fileEntry = + new ProdosDirectoryEntry(this, blockNumber, offset, newHeader); + fileEntry.setBlocksUsed(1); // Mark ourselves as the one block in use in this new subdirectory + fileEntry.setEofPosition(BLOCK_SIZE); + fileEntry.setKeyPointer(newDirBlockNumber); + fileEntry.setCreationDate(new Date()); + fileEntry.setLastModificationDate(new Date()); + fileEntry.setProdosVersion(0); + fileEntry.setMinimumProdosVersion(0); + fileEntry.setCanDestroy(true); + fileEntry.setCanRead(true); + fileEntry.setCanRename(true); + fileEntry.setCanWrite(true); + fileEntry.setSubdirectory(); + fileEntry.setHeaderPointer(blockNumber); + fileEntry.setFilename(name); + fileEntry.setFiletype(0x0f); // Filetype = subdirectory + directory.incrementFileCount(); + return fileEntry; + } + offset+= ProdosCommonEntry.ENTRY_LENGTH; + } + int nextBlockNumber = AppleUtil.getWordValue(block, NEXT_BLOCK_POINTER); + if (nextBlockNumber == 0 && directory instanceof ProdosSubdirectoryHeader) { + byte[] volumeBitmap = readVolumeBitMap(); + nextBlockNumber = findFreeBlock(volumeBitmap); + setBlockUsed(volumeBitmap, nextBlockNumber); + writeVolumeBitMap(volumeBitmap); + byte[] oldBlock = readBlock(blockNumber); + AppleUtil.setWordValue(oldBlock, NEXT_BLOCK_POINTER, nextBlockNumber); + writeBlock(blockNumber, oldBlock); + byte[] nextBlock = new byte[BLOCK_SIZE]; + AppleUtil.setWordValue(nextBlock, PREV_BLOCK_POINTER, blockNumber); + writeBlock(nextBlockNumber, nextBlock); + ProdosSubdirectoryHeader header = (ProdosSubdirectoryHeader) directory; + int blockCount = header.getProdosDirectoryEntry().getBlocksUsed(); + blockCount++; + header.getProdosDirectoryEntry().setBlocksUsed(blockCount); + header.getProdosDirectoryEntry().setEofPosition(blockCount * BLOCK_SIZE); + } + blockNumber = nextBlockNumber; + } + throw new DiskFullException(textBundle.get("ProdosFormatDisk.UnableToAllocateSpaceError")); //$NON-NLS-1$ } } diff --git a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosSubdirectoryHeader.java b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosSubdirectoryHeader.java index bbe20b3..bd631ab 100644 --- a/src/com/webcodepro/applecommander/storage/os/prodos/ProdosSubdirectoryHeader.java +++ b/src/com/webcodepro/applecommander/storage/os/prodos/ProdosSubdirectoryHeader.java @@ -22,7 +22,7 @@ package com.webcodepro.applecommander.storage.os.prodos; import com.webcodepro.applecommander.util.AppleUtil; /** - * Provides commone subdirectory attributes. + * Provides common subdirectory attributes. *

* Date created: Oct 5, 2002 11:17:57 PM * @author Rob Greene @@ -52,6 +52,15 @@ public class ProdosSubdirectoryHeader extends ProdosCommonDirectoryHeader { return AppleUtil.getWordValue(readFileEntry(), 0x23); } + /** + * Set the block number of the parent directory which contains the + * file entry for this subdirectory. + */ + public void setParentPointer(int block) { + byte[] data = readFileEntry(); + AppleUtil.setWordValue(data, 0x23, block); + } + /** * Return the number of the file entry within the parent block. */ @@ -79,4 +88,17 @@ public class ProdosSubdirectoryHeader extends ProdosCommonDirectoryHeader { public ProdosDirectoryEntry getProdosDirectoryEntry() { return directoryEntry; } + + /** + * Set up some housekeeping bits + */ + public void setHousekeeping() { + byte[] data = readFileEntry(); + data[0x00] = (byte) (0xe0 | (data[0x00] & 0x0f)); // Subdirectories have the high nibble set to 0x0e + data[0x10] = 0x75; // Reserved - must be $75 + data[0x1f] = (byte) ENTRY_LENGTH; + data[0x20] = 0x0d; + AppleUtil.setWordValue(data, 0x21, 0); // Set file count to zero + writeFileEntry(data); + } } diff --git a/src/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java index 73a6817..3b45b0d 100644 --- a/src/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java +++ b/src/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java @@ -509,4 +509,12 @@ public class RdosFormatDisk extends FormattedDisk { public DirectoryEntry createDirectory() throws DiskFullException { throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ } + + /** + * Create a new DirectoryEntry. + * @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory() + */ + public DirectoryEntry createDirectory(String name) throws DiskFullException { + throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$ + } } diff --git a/src/com/webcodepro/applecommander/ui/ac.java b/src/com/webcodepro/applecommander/ui/ac.java index 5e00900..47c999f 100644 --- a/src/com/webcodepro/applecommander/ui/ac.java +++ b/src/com/webcodepro/applecommander/ui/ac.java @@ -574,7 +574,7 @@ public class ac { "CommandLineHelp", AppleCommander.VERSION)); //$NON-NLS-1$ } - private static class Name { + public static class Name { private String fullName; private String name; private String[] path; @@ -616,17 +616,31 @@ public class ac { return formattedDisk.createFile(); } List files = formattedDisk.getFiles(); - DirectoryEntry dir = null; + DirectoryEntry dir = null, parentDir = null; for (int i = 0; i < path.length - 1; i++) { String dirName = path[i]; + dir = null; for (int j = 0; j < files.size(); j++) { FileEntry entry = (FileEntry) files.get(j); String entryName = entry.getFilename(); - if (entry.isDirectory() && dirName.equalsIgnoreCase(entryName)) { + if (!entry.isDeleted() && entry.isDirectory() && dirName.equalsIgnoreCase(entryName)) { dir = (DirectoryEntry) entry; + parentDir = dir; files = dir.getFiles(); } } + if (dir == null) { + if (parentDir != null) { + // If there's a parent directory in the mix, add + // the new child directory to that. + dir = parentDir.createDirectory(dirName); + parentDir = dir; + } else { + // Add the directory to the root of the filesystem + dir = formattedDisk.createDirectory(dirName); + parentDir = dir; + } + } } if (dir != null) { return dir.createFile(); diff --git a/src/com/webcodepro/shrinkit/Utilities.java b/src/com/webcodepro/shrinkit/Utilities.java index 3e07520..059f488 100644 --- a/src/com/webcodepro/shrinkit/Utilities.java +++ b/src/com/webcodepro/shrinkit/Utilities.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import com.webcodepro.applecommander.storage.Disk; +import com.webcodepro.applecommander.storage.FileEntry; import com.webcodepro.applecommander.storage.FormattedDisk; import com.webcodepro.applecommander.storage.StorageBundle; import com.webcodepro.applecommander.storage.os.prodos.ProdosFileEntry; @@ -32,6 +33,7 @@ import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk; import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout; import com.webcodepro.applecommander.storage.physical.ImageOrder; import com.webcodepro.applecommander.storage.physical.ProdosOrder; +import com.webcodepro.applecommander.ui.ac.Name; import com.webcodepro.applecommander.util.TextBundle; import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; @@ -118,7 +120,8 @@ public class Utilities { if (dataFork != null) { - newFile = (ProdosFileEntry) pdDisk.createFile(); + Name name = new Name(b.getFilename()); + newFile = (ProdosFileEntry)name.createEntry(pdDisk); if (newFile != null) { if (resourceFork != null)