diff --git a/src/main/java/jace/hardware/massStorage/DirectoryNode.java b/src/main/java/jace/hardware/massStorage/DirectoryNode.java index 5de592d..6024af5 100644 --- a/src/main/java/jace/hardware/massStorage/DirectoryNode.java +++ b/src/main/java/jace/hardware/massStorage/DirectoryNode.java @@ -37,21 +37,23 @@ public class DirectoryNode extends DiskNode implements FileFilter { // public static int FILE_ENTRY_SIZE = 38; public static int FILE_ENTRY_SIZE = 0x027; - - public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock) throws IOException { - setBaseBlock(baseBlock); - init(ownerFilesystem, physicalDir); + private boolean isRoot; + + public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock, boolean root) throws IOException { + super(ownerFilesystem, baseBlock); + init(ownerFilesystem, physicalDir, root); } - public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir) throws IOException { - init(ownerFilesystem, physicalDir); + public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, boolean root) throws IOException { + super(ownerFilesystem); + init(ownerFilesystem, physicalDir, root); } - private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile) throws IOException { + private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile, boolean root) throws IOException { + isRoot = root; setPhysicalFile(physicalFile); setType(EntryType.SUBDIRECTORY); setName(physicalFile.getName()); - setOwnerFilesystem(ownerFilesystem); } @Override @@ -59,15 +61,15 @@ public class DirectoryNode extends DiskNode implements FileFilter { } @Override - public void doAllocate() { + public void doAllocate() throws IOException { File[] files = physicalFile.listFiles(this); int numEntries = files.length; - int numBlocks = 1; // First block has 12 entries, subsequent blocks have 13 entries - if (numEntries > 12) { - numBlocks += (numEntries - 12) / 13; + int numBlocks = 1 + numEntries / 13; + for (int i=1; i < numBlocks; i++) { + new SubNode(i, this, getOwnerFilesystem().getNextFreeBlock()); } - + for (File f : files) { addFile(f); } @@ -126,6 +128,7 @@ public class DirectoryNode extends DiskNode implements FileFilter { generateFileEntry(buffer, 4 + (i + 1) * FILE_ENTRY_SIZE, i); } } else { + generatePointers(buffer, additionalNodes.get(block-1).getBaseBlock(), block < (additionalNodes.size()-1) ? additionalNodes.get(block+1).getBaseBlock() : 0); int start = (block * 13) - 1; int end = start + 13; int offset = 4; @@ -150,6 +153,13 @@ public class DirectoryNode extends DiskNode implements FileFilter { return !file.isHidden(); } + private void generatePointers(byte[] buffer, int prevBlock, int nextBlock) { + // Previous block = 0 + generateWord(buffer, 0, prevBlock); + // Next block + generateWord(buffer, 0x02, nextBlock); + } + /** * Generate the directory header found in the base block of a directory * @@ -157,21 +167,17 @@ public class DirectoryNode extends DiskNode implements FileFilter { */ @SuppressWarnings("static-access") private void generateHeader(byte[] buffer) { - // Previous block = 0 - generateWord(buffer, 0, 0); - // Next block - int nextBlock = 0; - if (!additionalNodes.isEmpty()) { - nextBlock = additionalNodes.get(0).baseBlock; - } - generateWord(buffer, 0x02, nextBlock); + generatePointers(buffer, 0, additionalNodes.size()>1 ? additionalNodes.get(1).getBaseBlock() : 0); // Directory header + name length // Volumme header = 0x0f0; Subdirectory header = 0x0e0 - buffer[4] = (byte) ((baseBlock == 0x02 ? 0x0f0 : 0x0E0) + getName().length()); + buffer[4] = (byte) ((isRoot ? 0x0F0 : 0x0E0) | getName().length()); generateName(buffer, 5, this); for (int i = 0x014; i <= 0x01b; i++) { buffer[i] = 0; } + if (!isRoot) { + buffer[0x014] = 0x075; + } generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified()); // Prodos 1.9 buffer[0x020] = 0x019; @@ -185,10 +191,17 @@ public class DirectoryNode extends DiskNode implements FileFilter { buffer[0x024] = (byte) 0x0d; // Directory items count generateWord(buffer, 0x025, children.size()); - // Volume bitmap pointer - generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.baseBlock); - // Total number of blocks - generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK); + if (isRoot) { + // Volume bitmap pointer + generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.getBaseBlock()); + // Total number of blocks + generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK); + } else { + // Parent pointer + generateWord(buffer, 0x027, getParent().getBaseBlock()); + buffer[0x029] = (byte) (getParent().getChildren().indexOf(this) + 1); + buffer[0x02a] = 0x027; + } } /** @@ -200,7 +213,6 @@ public class DirectoryNode extends DiskNode implements FileFilter { */ private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException { DiskNode child = children.get(fileNumber); - child.allocate(); // Entry Type and length buffer[offset] = (byte) ((child.getType().code << 4) + child.getName().length()); // Name @@ -261,7 +273,7 @@ public class DirectoryNode extends DiskNode implements FileFilter { private void addFile(File file) { try { if (file.isDirectory()) { - addChild(new DirectoryNode(getOwnerFilesystem(), file)); + addChild(new DirectoryNode(getOwnerFilesystem(), file, false)); } else { addChild(new FileNode(getOwnerFilesystem(), file)); } diff --git a/src/main/java/jace/hardware/massStorage/DiskNode.java b/src/main/java/jace/hardware/massStorage/DiskNode.java index e005482..e44b839 100644 --- a/src/main/java/jace/hardware/massStorage/DiskNode.java +++ b/src/main/java/jace/hardware/massStorage/DiskNode.java @@ -33,7 +33,6 @@ import java.util.List; public abstract class DiskNode { public enum EntryType { - DELETED(0), SEEDLING(1), SAPLING(2), @@ -50,7 +49,7 @@ public abstract class DiskNode { boolean allocated = false; long allocationTime = -1L; long lastCheckTime = -1L; - int baseBlock = -1; + private int baseBlock = -1; List additionalNodes; ProdosVirtualDisk ownerFilesystem; File physicalFile; @@ -59,13 +58,23 @@ public abstract class DiskNode { private EntryType type; private String name; - public DiskNode() { + public DiskNode(ProdosVirtualDisk fs) throws IOException { + init(fs); + setBaseBlock(fs.getNextFreeBlock()); + } + + public DiskNode(ProdosVirtualDisk fs, int blockNumber) throws IOException { + init(fs); + setBaseBlock(blockNumber); + } + + private void init(ProdosVirtualDisk fs) throws IOException { additionalNodes = new ArrayList<>(); children = new ArrayList<>(); + setOwnerFilesystem(fs); } public boolean checkFile() throws IOException { - allocate(); if (physicalFile == null) { return false; } @@ -80,9 +89,9 @@ public abstract class DiskNode { public void allocate() throws IOException { if (!allocated) { doAllocate(); + getOwnerFilesystem().physicalMap.put(baseBlock, this); allocationTime = System.currentTimeMillis(); allocated = true; - ownerFilesystem.allocateEntry(this); } } @@ -101,11 +110,9 @@ public abstract class DiskNode { } public void refresh() throws IOException { - ownerFilesystem.deallocateEntry(this); + deallocate(); doRefresh(); - allocationTime = System.currentTimeMillis(); - allocated = true; - ownerFilesystem.allocateEntry(this); + allocate(); } /** @@ -139,8 +146,9 @@ public abstract class DiskNode { /** * @param baseBlock the baseBlock to set */ - public void setBaseBlock(int baseBlock) { + private void setBaseBlock(int baseBlock) { this.baseBlock = baseBlock; + ownerFilesystem.physicalMap.put(baseBlock, this); } /** @@ -154,12 +162,8 @@ public abstract class DiskNode { * @param ownerFilesystem the ownerFilesystem to set * @throws IOException */ - public void setOwnerFilesystem(ProdosVirtualDisk ownerFilesystem) throws IOException { + private void setOwnerFilesystem(ProdosVirtualDisk ownerFilesystem) throws IOException { this.ownerFilesystem = ownerFilesystem; - if (baseBlock == -1) { - setBaseBlock(ownerFilesystem.getNextFreeBlock()); - } - ownerFilesystem.allocateEntry(this); } /** @@ -206,6 +210,7 @@ public abstract class DiskNode { } public void addChild(DiskNode child) { + child.setParent(this); children.add(child); } diff --git a/src/main/java/jace/hardware/massStorage/FileNode.java b/src/main/java/jace/hardware/massStorage/FileNode.java index 8c9158f..d6893f2 100644 --- a/src/main/java/jace/hardware/massStorage/FileNode.java +++ b/src/main/java/jace/hardware/massStorage/FileNode.java @@ -125,7 +125,7 @@ public class FileNode extends DiskNode { } public FileNode(ProdosVirtualDisk ownerFilesystem, File file) throws IOException { - setOwnerFilesystem(ownerFilesystem); + super(ownerFilesystem); setPhysicalFile(file); setName(file.getName()); } @@ -141,10 +141,9 @@ public class FileNode extends DiskNode { if (treeBlocks > 1) { treeBlocks++; } - for (int i = 0; i < dataBlocks + treeBlocks; i++) { + for (int i = 1; i < dataBlocks + treeBlocks; i++) { new SubNode(i, this); } - setBaseBlock(additionalNodes.get(0).getBaseBlock()); } @Override @@ -153,6 +152,7 @@ public class FileNode extends DiskNode { @Override public void readBlock(int block, byte[] buffer) throws IOException { + allocate(); int dataBlocks = (int) ((getPhysicalFile().length() + ProdosVirtualDisk.BLOCK_SIZE - 1) / ProdosVirtualDisk.BLOCK_SIZE); int treeBlocks = (((dataBlocks * 2) + (ProdosVirtualDisk.BLOCK_SIZE - 2)) / ProdosVirtualDisk.BLOCK_SIZE); if (treeBlocks > 1) { @@ -167,7 +167,7 @@ public class FileNode extends DiskNode { readFile(buffer, (block - 1)); } else { // Generate seedling index block - generateIndex(buffer, 1, treeBlocks + dataBlocks); + generateIndex(buffer, 1, dataBlocks+1); } break; case TREE: @@ -193,8 +193,8 @@ public class FileNode extends DiskNode { private void generateIndex(byte[] buffer, int indexStart, int indexLimit) { Arrays.fill(buffer, (byte) 0); - for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i < additionalNodes.size(); i++, count++) { - int base = additionalNodes.get(i).baseBlock; + for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i <= additionalNodes.size(); i++, count++) { + int base = additionalNodes.get(i-1).getBaseBlock(); buffer[count] = (byte) (base & 0x0ff); buffer[count + 256] = (byte) (base >> 8); } diff --git a/src/main/java/jace/hardware/massStorage/FreespaceBitmap.java b/src/main/java/jace/hardware/massStorage/FreespaceBitmap.java index 0b3dee2..e09cf4c 100644 --- a/src/main/java/jace/hardware/massStorage/FreespaceBitmap.java +++ b/src/main/java/jace/hardware/massStorage/FreespaceBitmap.java @@ -27,8 +27,7 @@ import java.io.IOException; public class FreespaceBitmap extends DiskNode { int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE; public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException { - setBaseBlock(start); - setOwnerFilesystem(fs); + super(fs, start); for (int i=1; i < size; i++) { SubNode subNode = new SubNode(i, this, start+i); diff --git a/src/main/java/jace/hardware/massStorage/ProdosVirtualDisk.java b/src/main/java/jace/hardware/massStorage/ProdosVirtualDisk.java index 7f4d566..34cddb9 100644 --- a/src/main/java/jace/hardware/massStorage/ProdosVirtualDisk.java +++ b/src/main/java/jace/hardware/massStorage/ProdosVirtualDisk.java @@ -52,6 +52,7 @@ public class ProdosVirtualDisk implements IDisk { public ProdosVirtualDisk(File rootPath) throws IOException { ioBuffer = new byte[BLOCK_SIZE]; + initDiskStructure(); setPhysicalPath(rootPath); } @@ -131,25 +132,14 @@ public class ProdosVirtualDisk implements IDisk { throw new IOException("Virtual Disk Full!"); } - // Mark space occupied by node - public void allocateEntry(DiskNode node) throws IOException { - physicalMap.put(node.baseBlock, node); - - for (DiskNode subnode : node.additionalNodes) { - int blockNum = getNextFreeBlock(); - subnode.setBaseBlock(blockNum); - physicalMap.put(blockNum, subnode); - } - } - // Mark space occupied by nodes as free (remove allocation mapping) public void deallocateEntry(DiskNode node) { // Only de-map nodes if the allocation table is actually pointing to the nodes! - if (physicalMap.get(node.baseBlock) != null && physicalMap.get(node.baseBlock).equals(node)) { - physicalMap.remove(node.baseBlock); + if (physicalMap.get(node.getBaseBlock()) != null && physicalMap.get(node.getBaseBlock()).equals(node)) { + physicalMap.remove(node.getBaseBlock()); } node.additionalNodes.stream().filter((sub) - -> (physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.baseBlock).equals(sub))). + -> (physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.getBaseBlock()).equals(sub))). forEach((sub) -> { physicalMap.remove(sub.getBaseBlock()); }); @@ -183,12 +173,16 @@ public class ProdosVirtualDisk implements IDisk { return physicalRoot; } - public void setPhysicalPath(File f) throws IOException { + private void initDiskStructure() throws IOException { + physicalMap = new HashMap<>(); + freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START); + } + + private void setPhysicalPath(File f) throws IOException { if (physicalRoot != null && physicalRoot.equals(f)) { return; } physicalRoot = f; - physicalMap = new HashMap<>(); if (!physicalRoot.exists() || !physicalRoot.isDirectory()) { try { throw new IOException("Root path must be a directory that exists!"); @@ -197,12 +191,9 @@ public class ProdosVirtualDisk implements IDisk { } } // Root directory ALWAYS starts on block 2! - rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START); + rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START, true); rootDirectory.setName("VIRTUAL"); - allocateEntry(rootDirectory); - freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START); - allocateEntry(freespaceBitmap); - + rootDirectory.allocate(); } @Override diff --git a/src/main/java/jace/hardware/massStorage/SubNode.java b/src/main/java/jace/hardware/massStorage/SubNode.java index 200bff0..d832d8b 100644 --- a/src/main/java/jace/hardware/massStorage/SubNode.java +++ b/src/main/java/jace/hardware/massStorage/SubNode.java @@ -33,18 +33,18 @@ public class SubNode extends DiskNode { int sequenceNumber; public SubNode(int seq, DiskNode parent) throws IOException { + super(parent.getOwnerFilesystem()); init(seq, parent); } public SubNode(int seq, DiskNode parent, int baseBlock) throws IOException { - setBaseBlock(baseBlock); + super(parent.getOwnerFilesystem(), baseBlock); init(seq, parent); } private void init(int seq, DiskNode parent) throws IOException { sequenceNumber = seq; setParent(parent); - setOwnerFilesystem(parent.getOwnerFilesystem()); parent.additionalNodes.add(this); }