mirror of
https://github.com/badvision/jace.git
synced 2024-11-28 10:52:33 +00:00
Fixed subdirectory bugs, added support for A2GameServer data files suffix (#TTAAAA where TT is the hex code and AAAA is the aux data/load address). This should be very servicable now.
This commit is contained in:
parent
5f9352abb3
commit
732f4768a6
@ -80,7 +80,7 @@ public abstract class ProdosDriver {
|
|||||||
MOS65C02 cpu = (MOS65C02) computer.getCpu();
|
MOS65C02 cpu = (MOS65C02) computer.getCpu();
|
||||||
cpu.A = returnCode;
|
cpu.A = returnCode;
|
||||||
// Clear carry flag if no error, otherwise set carry flag
|
// Clear carry flag if no error, otherwise set carry flag
|
||||||
cpu.C = (returnCode == 0x00) ? 00 : 01;
|
cpu.C = (returnCode == 0x00) ? 00 : 01;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MLI_RETURN prodosMLI() {
|
private MLI_RETURN prodosMLI() {
|
||||||
|
@ -18,14 +18,17 @@
|
|||||||
*/
|
*/
|
||||||
package jace.hardware.massStorage;
|
package jace.hardware.massStorage;
|
||||||
|
|
||||||
|
import static jace.hardware.massStorage.IDisk.BLOCK_SIZE;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -35,10 +38,14 @@ import java.util.logging.Logger;
|
|||||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||||
*/
|
*/
|
||||||
public class DirectoryNode extends DiskNode implements FileFilter {
|
public class DirectoryNode extends DiskNode implements FileFilter {
|
||||||
// public static int FILE_ENTRY_SIZE = 38;
|
|
||||||
|
|
||||||
public static int FILE_ENTRY_SIZE = 0x027;
|
public static final byte STANDARD_PERMISSIONS = (byte) 0x0c3;
|
||||||
|
public static final int PRODOS_VERSION = 0x023;
|
||||||
|
public static final int FILE_ENTRY_SIZE = 0x027;
|
||||||
|
public static final int ENTRIES_PER_BLOCK = (ProdosVirtualDisk.BLOCK_SIZE - 4) / FILE_ENTRY_SIZE;
|
||||||
private boolean isRoot;
|
private boolean isRoot;
|
||||||
|
|
||||||
|
private List<DiskNode> directoryEntries;
|
||||||
|
|
||||||
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock, boolean root) throws IOException {
|
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock, boolean root) throws IOException {
|
||||||
super(ownerFilesystem, baseBlock);
|
super(ownerFilesystem, baseBlock);
|
||||||
@ -52,9 +59,11 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
|
|
||||||
private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile, boolean root) throws IOException {
|
private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile, boolean root) throws IOException {
|
||||||
isRoot = root;
|
isRoot = root;
|
||||||
|
directoryEntries = new ArrayList<>();
|
||||||
setPhysicalFile(physicalFile);
|
setPhysicalFile(physicalFile);
|
||||||
setType(EntryType.SUBDIRECTORY);
|
setType(EntryType.SUBDIRECTORY);
|
||||||
setName(physicalFile.getName());
|
setName(physicalFile.getName());
|
||||||
|
allocate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -63,15 +72,15 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doAllocate() throws IOException {
|
public void doAllocate() throws IOException {
|
||||||
File[] files = physicalFile.listFiles(this);
|
for (int i = 1; i < getBlockCount(); i++) {
|
||||||
int numEntries = files.length;
|
if (isRoot) {
|
||||||
// First block has 12 entries, subsequent blocks have 13 entries
|
new SubNode(i, this, getOwnerFilesystem().getNextFreeBlock(3));
|
||||||
int numBlocks = 1 + numEntries / 13;
|
} else {
|
||||||
for (int i=1; i < numBlocks; i++) {
|
new SubNode(i, this);
|
||||||
new SubNode(i, this, getOwnerFilesystem().getNextFreeBlock());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (File f : files) {
|
for (File f : physicalFile.listFiles()) {
|
||||||
addFile(f);
|
addFile(f);
|
||||||
}
|
}
|
||||||
Collections.sort(children, (DiskNode o1, DiskNode o2) -> o1.getName().compareTo(o2.getName()));
|
Collections.sort(children, (DiskNode o1, DiskNode o2) -> o1.getName().compareTo(o2.getName()));
|
||||||
@ -88,58 +97,66 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
*/
|
*/
|
||||||
public boolean checkFile() throws IOException {
|
public boolean checkFile() throws IOException {
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
if (!super.checkFile()) {
|
if (!allocated) {
|
||||||
return false;
|
allocate();
|
||||||
}
|
} else {
|
||||||
HashSet<String> realFiles = new HashSet<>();
|
try {
|
||||||
File[] realFileList = physicalFile.listFiles(this);
|
if (!super.checkFile()) {
|
||||||
for (File f : realFileList) {
|
return false;
|
||||||
realFiles.add(f.getName());
|
|
||||||
}
|
|
||||||
for (Iterator<DiskNode> i = getChildren().iterator(); i.hasNext();) {
|
|
||||||
DiskNode node = i.next();
|
|
||||||
if (realFiles.contains(node.getPhysicalFile().getName())) {
|
|
||||||
realFiles.remove(node.getPhysicalFile().getName());
|
|
||||||
} else {
|
|
||||||
i.remove();
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
if (node.isAllocated()) {
|
|
||||||
if (!(node instanceof DirectoryNode) && !node.checkFile()) {
|
|
||||||
success = false;
|
|
||||||
}
|
}
|
||||||
|
HashSet<String> realFiles = new HashSet<>();
|
||||||
|
File[] realFileList = physicalFile.listFiles(this);
|
||||||
|
for (File f : realFileList) {
|
||||||
|
realFiles.add(f.getName());
|
||||||
|
}
|
||||||
|
for (Iterator<DiskNode> i = directoryEntries.iterator(); i.hasNext();) {
|
||||||
|
DiskNode node = i.next();
|
||||||
|
if (realFiles.contains(node.getPhysicalFile().getName())) {
|
||||||
|
realFiles.remove(node.getPhysicalFile().getName());
|
||||||
|
} else {
|
||||||
|
i.remove();
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
if (node.isAllocated()) {
|
||||||
|
if (!(node instanceof DirectoryNode) && !node.checkFile()) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!realFiles.isEmpty()) {
|
||||||
|
success = false;
|
||||||
|
// New files showed up -- deal with them!
|
||||||
|
realFiles.stream().forEach((fileName) -> {
|
||||||
|
addFile(new File(physicalFile, fileName));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!realFiles.isEmpty()) {
|
|
||||||
success = false;
|
|
||||||
// New files showed up -- deal with them!
|
|
||||||
realFiles.stream().forEach((fileName) -> {
|
|
||||||
addFile(new File(physicalFile, fileName));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readBlock(int block, byte[] buffer) throws IOException {
|
public void readBlock(int block, byte[] buffer) throws IOException {
|
||||||
Arrays.fill(buffer, (byte) 0);
|
|
||||||
checkFile();
|
checkFile();
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
int offset = 4;
|
||||||
|
generatePointers(buffer, block);
|
||||||
|
// System.out.println("Directory "+getName()+" sequence "+block+"; physical block "+getNodeSequence(block).getBaseBlock());
|
||||||
if (block == 0) {
|
if (block == 0) {
|
||||||
generateHeader(buffer);
|
generateHeader(buffer);
|
||||||
for (int i = 0; i < 12 && i < children.size(); i++) {
|
offset += FILE_ENTRY_SIZE;
|
||||||
generateFileEntry(buffer, 4 + (i + 1) * FILE_ENTRY_SIZE, i);
|
end = ENTRIES_PER_BLOCK - 1;
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
generatePointers(buffer, getNodeSequence(block-1).getBaseBlock(), getNodeSequence(block+1) != null ? getNodeSequence(block+1).getBaseBlock() : 0);
|
start = (block * ENTRIES_PER_BLOCK) - 1;
|
||||||
int start = (block * 13) - 1;
|
end = start + ENTRIES_PER_BLOCK;
|
||||||
int end = start + 13;
|
}
|
||||||
int offset = 4;
|
for (int i = start; i < end && i < directoryEntries.size(); i++, offset += FILE_ENTRY_SIZE) {
|
||||||
|
// TODO: Add any parts that are not file entries.
|
||||||
for (int i = start; i < end && i < children.size(); i++) {
|
// System.out.println("Entry "+i+": "+children.get(i).getName()+"; offset "+offset);
|
||||||
// TODO: Add any parts that are not file entries.
|
generateFileEntry(buffer, offset, i);
|
||||||
generateFileEntry(buffer, offset, i);
|
|
||||||
offset += FILE_ENTRY_SIZE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,13 +172,16 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
return !file.isHidden();
|
return !file.isHidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generatePointers(byte[] buffer, int prevBlock, int nextBlock) {
|
private void generatePointers(byte[] buffer, int sequence) {
|
||||||
// Previous block = 0
|
DiskNode prev = getNodeSequence(sequence - 1);
|
||||||
generateWord(buffer, 0, prevBlock);
|
DiskNode next = getNodeSequence(sequence + 1);
|
||||||
// Next block
|
// System.out.println("Sequence "+sequence+" prev="+(prev != null ? prev.getBaseBlock() : 0)+"; next="+(next != null ? next.getBaseBlock() : 0));
|
||||||
generateWord(buffer, 0x02, nextBlock);
|
// Previous block (or 0)
|
||||||
|
generateWord(buffer, 0, prev != null ? prev.getBaseBlock() : 0);
|
||||||
|
// Next block (or 0)
|
||||||
|
generateWord(buffer, 0x02, next != null ? next.getBaseBlock() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the directory header found in the base block of a directory
|
* Generate the directory header found in the base block of a directory
|
||||||
*
|
*
|
||||||
@ -169,40 +189,43 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("static-access")
|
@SuppressWarnings("static-access")
|
||||||
private void generateHeader(byte[] buffer) {
|
private void generateHeader(byte[] buffer) {
|
||||||
generatePointers(buffer, 0, getNodeSequence(1) == null ? 0 : getNodeSequence(1).getBaseBlock());
|
|
||||||
// Directory header + name length
|
// Directory header + name length
|
||||||
// Volumme header = 0x0f0; Subdirectory header = 0x0e0
|
// Volumme header = 0x0f0; Subdirectory header = 0x0e0
|
||||||
buffer[4] = (byte) ((isRoot ? 0x0F0 : 0x0E0) | getName().length());
|
buffer[4] = (byte) ((isRoot ? 0x0F0 : 0x0E0) | getName().length());
|
||||||
generateName(buffer, 5, this);
|
generateName(buffer, 5, this);
|
||||||
for (int i = 0x014; i <= 0x01b; i++) {
|
|
||||||
buffer[i] = 0;
|
|
||||||
}
|
|
||||||
if (!isRoot) {
|
if (!isRoot) {
|
||||||
buffer[0x014] = 0x075;
|
buffer[0x014] = 0x075;
|
||||||
|
buffer[0x015] = PRODOS_VERSION;
|
||||||
|
buffer[0x017] = STANDARD_PERMISSIONS;
|
||||||
|
buffer[0x018] = FILE_ENTRY_SIZE;
|
||||||
|
buffer[0x019] = ENTRIES_PER_BLOCK;
|
||||||
}
|
}
|
||||||
generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified());
|
generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified());
|
||||||
// Prodos 1.9
|
// Prodos 1.0 = 0
|
||||||
buffer[0x020] = 0x019;
|
buffer[0x020] = PRODOS_VERSION;
|
||||||
// Minimum version = 0 (no min)
|
// Minimum version = 0 (no min)
|
||||||
buffer[0x021] = 0x000;
|
buffer[0x021] = 0x000;
|
||||||
// Directory may be read/written to, may not be destroyed or renamed
|
// Directory may be read/written to, may not be destroyed or renamed
|
||||||
buffer[0x022] = 0x03;
|
buffer[0x022] = STANDARD_PERMISSIONS;
|
||||||
// Entry size
|
// Entry size
|
||||||
buffer[0x023] = (byte) FILE_ENTRY_SIZE;
|
buffer[0x023] = (byte) FILE_ENTRY_SIZE;
|
||||||
// Entries per block
|
// Entries per block
|
||||||
buffer[0x024] = (byte) 0x0d;
|
buffer[0x024] = (byte) ENTRIES_PER_BLOCK;
|
||||||
// Directory items count
|
// Directory items count
|
||||||
generateWord(buffer, 0x025, children.size());
|
generateWord(buffer, 0x025, directoryEntries.size()+1);
|
||||||
if (isRoot) {
|
if (isRoot) {
|
||||||
// Volume bitmap pointer
|
// Volume bitmap pointer
|
||||||
generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.getBaseBlock());
|
generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.getBaseBlock());
|
||||||
// Total number of blocks
|
// Total number of blocks
|
||||||
generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK);
|
generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK);
|
||||||
} else {
|
} else {
|
||||||
|
// According to the Beneath Apple Prodos supplement
|
||||||
|
int indexInParent = getParent().getChildren().indexOf(this) + 2;
|
||||||
|
int parentBlock = getParent().getNodeSequence(indexInParent / ENTRIES_PER_BLOCK).getBaseBlock();
|
||||||
// Parent pointer
|
// Parent pointer
|
||||||
generateWord(buffer, 0x027, getParent().getBaseBlock());
|
generateWord(buffer, 0x027, parentBlock);
|
||||||
buffer[0x029] = (byte) (getParent().getChildren().indexOf(this) + 1);
|
buffer[0x029] = (byte) (indexInParent % ENTRIES_PER_BLOCK);
|
||||||
buffer[0x02a] = 0x027;
|
buffer[0x02a] = (byte) FILE_ENTRY_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,9 +237,9 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
* @param fileNumber number of file (indexed in Children array) to write
|
* @param fileNumber number of file (indexed in Children array) to write
|
||||||
*/
|
*/
|
||||||
private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException {
|
private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException {
|
||||||
DiskNode child = children.get(fileNumber);
|
DiskNode child = directoryEntries.get(fileNumber);
|
||||||
// Entry Type and length
|
// Entry Type and length
|
||||||
buffer[offset] = (byte) ((child.getType().code << 4) + child.getName().length());
|
buffer[offset] = (byte) ((child.getType().code << 4) | child.getName().length());
|
||||||
// Name
|
// Name
|
||||||
generateName(buffer, offset + 1, child);
|
generateName(buffer, offset + 1, child);
|
||||||
// File type
|
// File type
|
||||||
@ -224,20 +247,20 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
// Key pointer
|
// Key pointer
|
||||||
generateWord(buffer, offset + 0x011, child.getBaseBlock());
|
generateWord(buffer, offset + 0x011, child.getBaseBlock());
|
||||||
// Blocks used -- will report only one unless file is actually allocated
|
// Blocks used -- will report only one unless file is actually allocated
|
||||||
generateWord(buffer, offset + 0x013, child.additionalNodes.size());
|
generateWord(buffer, offset + 0x013, child.additionalNodes.size() + 1);
|
||||||
// EOF
|
// EOF (file size or directory structure size
|
||||||
// TODO: Verify this is the right thing to do -- is EOF total length or a modulo?
|
int length = child.getLength();
|
||||||
int length = ((int) child.physicalFile.length()) & 0x0ffffff;
|
length &= 0x0ffffff;
|
||||||
generateWord(buffer, offset + 0x015, length & 0x0ffff);
|
generateWord(buffer, offset + 0x015, length & 0x0ffff);
|
||||||
buffer[offset + 0x017] = (byte) ((length >> 16) & 0x0ff);
|
buffer[offset + 0x017] = (byte) ((length >> 16) & 0x0ff);
|
||||||
// Creation date
|
// Creation date
|
||||||
generateTimestamp(buffer, offset + 0x018, child.physicalFile.lastModified());
|
generateTimestamp(buffer, offset + 0x018, child.physicalFile.lastModified());
|
||||||
// Version = 1.9
|
// Version = 1.0
|
||||||
buffer[offset + 0x01c] = 0x19;
|
buffer[offset + 0x01c] = PRODOS_VERSION;
|
||||||
// Minimum version = 0
|
// Minimum version = 0
|
||||||
buffer[offset + 0x01d] = 0;
|
buffer[offset + 0x01d] = 0;
|
||||||
// Access = Read-only
|
// Access = Read-only
|
||||||
buffer[offset + 0x01e] = (byte) 0x001;
|
buffer[offset + 0x01e] = STANDARD_PERMISSIONS;
|
||||||
// AUX type
|
// AUX type
|
||||||
if (child instanceof FileNode) {
|
if (child instanceof FileNode) {
|
||||||
generateWord(buffer, offset + 0x01f, ((FileNode) child).loadAddress);
|
generateWord(buffer, offset + 0x01f, ((FileNode) child).loadAddress);
|
||||||
@ -267,20 +290,40 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateName(byte[] buffer, int offset, DiskNode node) {
|
private void generateName(byte[] buffer, int offset, DiskNode node) {
|
||||||
for (int i = 0; i < node.getName().length(); i++) {
|
for (int i = 0; i < node.getName().length() && i < 15; i++) {
|
||||||
buffer[offset + i] = (byte) node.getName().charAt(i);
|
buffer[offset + i] = (byte) node.getName().charAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<DiskNode> findChildByFilename(String name) {
|
||||||
|
return directoryEntries.stream().filter((child) -> child.getPhysicalFile().getName().equals(name)).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
private void addFile(File file) {
|
private void addFile(File file) {
|
||||||
try {
|
if (!hasChildNamed(file.getName())) {
|
||||||
if (file.isDirectory()) {
|
try {
|
||||||
addChild(new DirectoryNode(getOwnerFilesystem(), file, false));
|
if (file.isDirectory()) {
|
||||||
} else {
|
addFileEntry(new DirectoryNode(getOwnerFilesystem(), file, false));
|
||||||
addChild(new FileNode(getOwnerFilesystem(), file));
|
} else {
|
||||||
|
addFileEntry(new FileNode(getOwnerFilesystem(), file));
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(DirectoryNode.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.getLogger(DirectoryNode.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addFileEntry(DiskNode entry) {
|
||||||
|
directoryEntries.add(entry);
|
||||||
|
entry.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return getBlockCount() * BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBlockCount() {
|
||||||
|
return isRoot ? 4 : 1 + (physicalFile.listFiles().length / ENTRIES_PER_BLOCK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prodos file/directory node abstraction. This provides a lot of the glue for
|
* Prodos file/directory node abstraction. This provides a lot of the glue for
|
||||||
@ -60,12 +61,12 @@ public abstract class DiskNode {
|
|||||||
|
|
||||||
public DiskNode(ProdosVirtualDisk fs) throws IOException {
|
public DiskNode(ProdosVirtualDisk fs) throws IOException {
|
||||||
init(fs);
|
init(fs);
|
||||||
setBaseBlock(fs.getNextFreeBlock());
|
fs.allocateEntry(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiskNode(ProdosVirtualDisk fs, int blockNumber) throws IOException {
|
public DiskNode(ProdosVirtualDisk fs, int blockNumber) throws IOException {
|
||||||
init(fs);
|
init(fs);
|
||||||
setBaseBlock(blockNumber);
|
fs.allocateEntryNear(this, blockNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(ProdosVirtualDisk fs) throws IOException {
|
private void init(ProdosVirtualDisk fs) throws IOException {
|
||||||
@ -89,7 +90,6 @@ public abstract class DiskNode {
|
|||||||
public void allocate() throws IOException {
|
public void allocate() throws IOException {
|
||||||
if (!allocated) {
|
if (!allocated) {
|
||||||
doAllocate();
|
doAllocate();
|
||||||
getOwnerFilesystem().physicalMap.put(baseBlock, this);
|
|
||||||
allocationTime = System.currentTimeMillis();
|
allocationTime = System.currentTimeMillis();
|
||||||
allocated = true;
|
allocated = true;
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ public abstract class DiskNode {
|
|||||||
public DiskNode getNodeSequence(int num) {
|
public DiskNode getNodeSequence(int num) {
|
||||||
if (num == 0) {
|
if (num == 0) {
|
||||||
return this;
|
return this;
|
||||||
} else if (num <= additionalNodes.size()) {
|
} else if (num > 0 && num <= additionalNodes.size()) {
|
||||||
return additionalNodes.get(num-1);
|
return additionalNodes.get(num-1);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -156,9 +156,8 @@ public abstract class DiskNode {
|
|||||||
/**
|
/**
|
||||||
* @param baseBlock the baseBlock to set
|
* @param baseBlock the baseBlock to set
|
||||||
*/
|
*/
|
||||||
private void setBaseBlock(int baseBlock) {
|
public void setBaseBlock(int baseBlock) {
|
||||||
this.baseBlock = baseBlock;
|
this.baseBlock = baseBlock;
|
||||||
ownerFilesystem.physicalMap.put(baseBlock, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -189,6 +188,7 @@ public abstract class DiskNode {
|
|||||||
public void setPhysicalFile(File physicalFile) {
|
public void setPhysicalFile(File physicalFile) {
|
||||||
this.physicalFile = physicalFile;
|
this.physicalFile = physicalFile;
|
||||||
setName(physicalFile.getName());
|
setName(physicalFile.getName());
|
||||||
|
lastCheckTime = physicalFile.lastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,6 +227,14 @@ public abstract class DiskNode {
|
|||||||
public void removeChild(DiskNode child) {
|
public void removeChild(DiskNode child) {
|
||||||
children.remove(child);
|
children.remove(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasChildNamed(String name) {
|
||||||
|
return findChildByFilename(name).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<DiskNode> findChildByFilename(String name) {
|
||||||
|
return getChildren().stream().filter((child) -> child.getPhysicalFile().getName().equals(name)).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the type
|
* @return the type
|
||||||
@ -253,10 +261,7 @@ public abstract class DiskNode {
|
|||||||
* @param name the name to set
|
* @param name the name to set
|
||||||
*/
|
*/
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
if (name.length() > 15) {
|
this.name = (name.length() > 15 ? name.substring(0, 15) : name).toUpperCase();
|
||||||
name = name.substring(0, 15);
|
|
||||||
}
|
|
||||||
this.name = name.toUpperCase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void doDeallocate();
|
public abstract void doDeallocate();
|
||||||
@ -266,6 +271,8 @@ public abstract class DiskNode {
|
|||||||
public abstract void doRefresh();
|
public abstract void doRefresh();
|
||||||
|
|
||||||
public abstract void readBlock(int sequence, byte[] buffer) throws IOException;
|
public abstract void readBlock(int sequence, byte[] buffer) throws IOException;
|
||||||
|
|
||||||
|
public abstract int getLength();
|
||||||
|
|
||||||
public void readBlock(byte[] buffer) throws IOException {
|
public void readBlock(byte[] buffer) throws IOException {
|
||||||
checkFile();
|
checkFile();
|
||||||
|
@ -21,7 +21,6 @@ package jace.hardware.massStorage;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a prodos file with a known file type and having a known
|
* Representation of a prodos file with a known file type and having a known
|
||||||
@ -31,6 +30,11 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public class FileNode extends DiskNode {
|
public class FileNode extends DiskNode {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return (int) getPhysicalFile().length();
|
||||||
|
}
|
||||||
|
|
||||||
public enum FileType {
|
public enum FileType {
|
||||||
|
|
||||||
UNKNOWN(0x00, 0x0000),
|
UNKNOWN(0x00, 0x0000),
|
||||||
@ -67,6 +71,15 @@ public class FileNode extends DiskNode {
|
|||||||
this.code = code;
|
this.code = code;
|
||||||
this.defaultLoadAddress = addr;
|
this.defaultLoadAddress = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FileType findByCode(int code) {
|
||||||
|
for (FileType t : FileType.values()) {
|
||||||
|
if (t.code == code) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public int fileType = 0x00;
|
public int fileType = 0x00;
|
||||||
public int loadAddress = 0x00;
|
public int loadAddress = 0x00;
|
||||||
@ -90,28 +103,35 @@ public class FileNode extends DiskNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
String[] parts = name.replaceAll("[^A-Za-z0-9#]", ".").split("\\.");
|
|
||||||
FileType t = FileType.UNKNOWN;
|
FileType t = FileType.UNKNOWN;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
String prodosName = name;
|
String prodosName = name;
|
||||||
if (parts.length > 1) {
|
if (name.matches("^.*?#[0-9A-Fa-f]{6}$")) {
|
||||||
String extension = parts[parts.length - 1].toUpperCase();
|
int type = Integer.parseInt(name.substring(name.length() - 6, name.length() - 4), 16);
|
||||||
String[] extParts = extension.split("\\#");
|
offset = Integer.parseInt(name.substring(name.length() - 4), 16);
|
||||||
if (extParts.length == 2) {
|
t = FileType.findByCode(type);
|
||||||
offset = Integer.parseInt(extParts[1], 16);
|
prodosName = name.substring(0, name.length()-7).replaceAll("[^A-Za-z0-9#]", ".").toUpperCase();
|
||||||
extension = extParts[0];
|
} else {
|
||||||
}
|
String[] parts = name.replaceAll("[^A-Za-z0-9#]", ".").split("\\.");
|
||||||
try {
|
if (parts.length > 1) {
|
||||||
t = FileType.valueOf(extension);
|
String extension = parts[parts.length - 1].toUpperCase();
|
||||||
} catch (IllegalArgumentException ex) {
|
String[] extParts = extension.split("\\#");
|
||||||
System.out.println("Not sure what extension " + extension + " is!");
|
if (extParts.length == 2) {
|
||||||
}
|
offset = Integer.parseInt(extParts[1], 16);
|
||||||
prodosName = "";
|
extension = extParts[0];
|
||||||
for (int i = 0; i < parts.length - 1; i++) {
|
}
|
||||||
prodosName += (i > 0 ? "." + parts[i] : parts[i]);
|
try {
|
||||||
}
|
t = FileType.valueOf(extension);
|
||||||
if (extParts[extParts.length - 1].equals("SYSTEM")) {
|
} catch (IllegalArgumentException ex) {
|
||||||
prodosName += ".SYSTEM";
|
System.out.println("Not sure what extension " + extension + " is!");
|
||||||
|
}
|
||||||
|
prodosName = "";
|
||||||
|
for (int i = 0; i < parts.length - 1; i++) {
|
||||||
|
prodosName += (i > 0 ? "." + parts[i] : parts[i]);
|
||||||
|
}
|
||||||
|
if (extParts[extParts.length - 1].equals("SYSTEM")) {
|
||||||
|
prodosName += ".SYSTEM";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
@ -128,6 +148,7 @@ public class FileNode extends DiskNode {
|
|||||||
super(ownerFilesystem);
|
super(ownerFilesystem);
|
||||||
setPhysicalFile(file);
|
setPhysicalFile(file);
|
||||||
setName(file.getName());
|
setName(file.getName());
|
||||||
|
allocate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -167,7 +188,7 @@ public class FileNode extends DiskNode {
|
|||||||
readFile(buffer, (block - 1));
|
readFile(buffer, (block - 1));
|
||||||
} else {
|
} else {
|
||||||
// Generate seedling index block
|
// Generate seedling index block
|
||||||
generateIndex(buffer, 1, dataBlocks+1);
|
generateIndex(buffer, 1, dataBlocks + 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TREE:
|
case TREE:
|
||||||
@ -192,7 +213,6 @@ public class FileNode extends DiskNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateIndex(byte[] buffer, int indexStart, int indexLimit) {
|
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++) {
|
for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i <= additionalNodes.size(); i++, count++) {
|
||||||
int base = getNodeSequence(i).getBaseBlock();
|
int base = getNodeSequence(i).getBaseBlock();
|
||||||
buffer[count] = (byte) (base & 0x0ff);
|
buffer[count] = (byte) (base & 0x0ff);
|
||||||
|
@ -22,25 +22,28 @@ import java.io.IOException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Maintain freespace and node allocation
|
* Maintain freespace and node allocation
|
||||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
*
|
||||||
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||||
*/
|
*/
|
||||||
public class FreespaceBitmap extends DiskNode {
|
public class FreespaceBitmap extends DiskNode {
|
||||||
|
|
||||||
int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE;
|
int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE;
|
||||||
|
|
||||||
public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException {
|
public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException {
|
||||||
super(fs, start);
|
super(fs, start);
|
||||||
|
allocate();
|
||||||
for (int i=1; i < size; i++) {
|
|
||||||
SubNode subNode = new SubNode(i, this, start+i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doDeallocate() {
|
public void doDeallocate() {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doAllocate() {
|
public void doAllocate() throws IOException {
|
||||||
///
|
for (int i = 1; i < size; i++) {
|
||||||
|
SubNode subNode = new SubNode(i, this, getBaseBlock());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,22 +54,18 @@ public class FreespaceBitmap extends DiskNode {
|
|||||||
@Override
|
@Override
|
||||||
public void readBlock(int sequence, byte[] buffer) throws IOException {
|
public void readBlock(int sequence, byte[] buffer) throws IOException {
|
||||||
int startBlock = sequence * ProdosVirtualDisk.BLOCK_SIZE * 8;
|
int startBlock = sequence * ProdosVirtualDisk.BLOCK_SIZE * 8;
|
||||||
int endBlock = (sequence+1)* ProdosVirtualDisk.BLOCK_SIZE * 8;
|
int endBlock = (sequence + 1) * ProdosVirtualDisk.BLOCK_SIZE * 8;
|
||||||
int bitCounter=0;
|
for (int i = startBlock; i < endBlock; i++) {
|
||||||
int pos=0;
|
if (!getOwnerFilesystem().isBlockAllocated(i)) {
|
||||||
int value=0;
|
int pos = (i - startBlock) / 8;
|
||||||
for (int i=startBlock; i < endBlock; i++) {
|
int bit = 1 << (i % 8);
|
||||||
if (!getOwnerFilesystem().isAllocated(i)) {
|
buffer[pos] |= bit;
|
||||||
value++;
|
|
||||||
}
|
|
||||||
bitCounter++;
|
|
||||||
if (bitCounter < 8) {
|
|
||||||
value *= 2;
|
|
||||||
} else {
|
|
||||||
bitCounter = 0;
|
|
||||||
buffer[pos++]=(byte) value;
|
|
||||||
value = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return (1 + getChildren().size()) * IDisk.BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public interface IDisk {
|
public interface IDisk {
|
||||||
public static int BLOCK_SIZE = 512;
|
public static int BLOCK_SIZE = 512;
|
||||||
public static int MAX_BLOCK = 65535;
|
public static int MAX_BLOCK = 0x07fff;
|
||||||
|
|
||||||
public void mliFormat() throws IOException;
|
public void mliFormat() throws IOException;
|
||||||
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException;
|
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException;
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package jace.hardware.massStorage;
|
package jace.hardware.massStorage;
|
||||||
|
|
||||||
|
import jace.Emulator;
|
||||||
import jace.EmulatorUILogic;
|
import jace.EmulatorUILogic;
|
||||||
import jace.apple2e.MOS65C02;
|
import jace.apple2e.MOS65C02;
|
||||||
import jace.core.Computer;
|
import jace.core.Computer;
|
||||||
@ -27,8 +28,6 @@ import jace.hardware.ProdosDriver.MLI_COMMAND_TYPE;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -42,16 +41,17 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class ProdosVirtualDisk implements IDisk {
|
public class ProdosVirtualDisk implements IDisk {
|
||||||
|
|
||||||
public static int VOLUME_START = 2;
|
public static final int VOLUME_START = 2;
|
||||||
public static int FREESPACE_BITMAP_START = 6;
|
public static final int FREESPACE_BITMAP_START = 6;
|
||||||
byte[] ioBuffer;
|
byte[] ioBuffer;
|
||||||
File physicalRoot;
|
File physicalRoot;
|
||||||
Map<Integer, DiskNode> physicalMap;
|
private final DiskNode[] physicalMap;
|
||||||
DirectoryNode rootDirectory;
|
DirectoryNode rootDirectory;
|
||||||
FreespaceBitmap freespaceBitmap;
|
FreespaceBitmap freespaceBitmap;
|
||||||
|
|
||||||
public ProdosVirtualDisk(File rootPath) throws IOException {
|
public ProdosVirtualDisk(File rootPath) throws IOException {
|
||||||
ioBuffer = new byte[BLOCK_SIZE];
|
ioBuffer = new byte[BLOCK_SIZE];
|
||||||
|
physicalMap = new DiskNode[MAX_BLOCK];
|
||||||
initDiskStructure();
|
initDiskStructure();
|
||||||
setPhysicalPath(rootPath);
|
setPhysicalPath(rootPath);
|
||||||
}
|
}
|
||||||
@ -59,29 +59,28 @@ public class ProdosVirtualDisk implements IDisk {
|
|||||||
@Override
|
@Override
|
||||||
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException {
|
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException {
|
||||||
// System.out.println("Read block " + block + " to " + Integer.toHexString(bufferAddress));
|
// System.out.println("Read block " + block + " to " + Integer.toHexString(bufferAddress));
|
||||||
DiskNode node = physicalMap.get(block);
|
DiskNode node = physicalMap[block];
|
||||||
Arrays.fill(ioBuffer, (byte) (block & 0x0ff));
|
Arrays.fill(ioBuffer, (byte) 0);
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
System.out.println("Reading unknown block " + Integer.toHexString(block));
|
System.out.println("Unknown block " + Integer.toHexString(block));
|
||||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
|
||||||
memory.write(bufferAddress + i, (byte) 0, false, false);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// if (node.getPhysicalFile() == null) {
|
|
||||||
// System.out.println("reading block "+block+ " from directory structure to "+Integer.toHexString(bufferAddress));
|
|
||||||
// } else {
|
|
||||||
// System.out.println("reading block "+block+ " from "+node.getPhysicalFile().getName()+" to "+Integer.toHexString(bufferAddress));
|
|
||||||
// }
|
|
||||||
node.readBlock(ioBuffer);
|
node.readBlock(ioBuffer);
|
||||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
|
||||||
memory.write(bufferAddress + i, ioBuffer[i], false, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// for (int i=0; i < 512; i++) {
|
for (int i = 0; i < ioBuffer.length; i++) {
|
||||||
// if (i % 32 == 0 && i > 0) System.out.println();
|
memory.write(bufferAddress + i, ioBuffer[i], false, false);
|
||||||
// System.out.print(((ioBuffer[i]&0x0ff)<16 ? "0" : "") + Integer.toHexString(ioBuffer[i] & 0x0ff) + " ");
|
}
|
||||||
|
// System.out.println("Block " + Integer.toHexString(block));
|
||||||
|
// for (int i = 0; i < 32; i++) {
|
||||||
|
// String hex = "";
|
||||||
|
// String text = "";
|
||||||
|
// for (int j = 0; j < 16; j++) {
|
||||||
|
// int val = 0x0ff & memory.readRaw(bufferAddress + i * 16 + j);
|
||||||
|
// char show = (char) (((val & 0x7f) < ' ') ? '.' : val & 0x7f);
|
||||||
|
// hex += (val < 16 ? "0" : "") + Integer.toString(val, 16) + " ";
|
||||||
|
// text += show;
|
||||||
|
// }
|
||||||
|
// System.out.println(hex + " " + text);
|
||||||
// }
|
// }
|
||||||
// System.out.println();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -121,33 +120,51 @@ public class ProdosVirtualDisk implements IDisk {
|
|||||||
return mostLikelyMatch;
|
return mostLikelyMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNextFreeBlock() throws IOException {
|
public int getNextFreeBlock(int start) throws IOException {
|
||||||
// Don't allocate Zero block for anything!
|
// Don't allocate Zero block for anything!
|
||||||
// for (int i = 0; i < MAX_BLOCK; i++) {
|
// for (int i = 0; i < MAX_BLOCK; i++) {
|
||||||
for (int i = 2; i < MAX_BLOCK; i++) {
|
for (int i = start; i < MAX_BLOCK; i++) {
|
||||||
if (!physicalMap.containsKey(i)) {
|
if (physicalMap[i] == null) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IOException("Virtual Disk Full!");
|
throw new IOException("Virtual Disk Full!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int allocateEntry(DiskNode node) throws IOException {
|
||||||
|
return allocateEntryNear(node, FREESPACE_BITMAP_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int allocateEntryNear(DiskNode node, int start) throws IOException {
|
||||||
|
if (isNodeAllocated(node)) {
|
||||||
|
return node.getBaseBlock();
|
||||||
|
}
|
||||||
|
int block = getNextFreeBlock(start);
|
||||||
|
node.setBaseBlock(block);
|
||||||
|
physicalMap[block] = node;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNodeAllocated(DiskNode node) {
|
||||||
|
return node.getBaseBlock() >= 0 && physicalMap[node.getBaseBlock()] == node;
|
||||||
|
}
|
||||||
|
|
||||||
// Mark space occupied by nodes as free (remove allocation mapping)
|
// Mark space occupied by nodes as free (remove allocation mapping)
|
||||||
public void deallocateEntry(DiskNode node) {
|
public void deallocateEntry(DiskNode node) {
|
||||||
// Only de-map nodes if the allocation table is actually pointing to the nodes!
|
// Only de-map nodes if the allocation table is actually pointing to the nodes!
|
||||||
if (physicalMap.get(node.getBaseBlock()) != null && physicalMap.get(node.getBaseBlock()).equals(node)) {
|
if (physicalMap[node.getBaseBlock()] != null && physicalMap[node.getBaseBlock()].equals(node)) {
|
||||||
physicalMap.remove(node.getBaseBlock());
|
physicalMap[node.getBaseBlock()] = null;
|
||||||
}
|
}
|
||||||
node.additionalNodes.stream().filter((sub)
|
node.additionalNodes.stream().filter((sub)
|
||||||
-> (physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.getBaseBlock()).equals(sub))).
|
-> (physicalMap[sub.getBaseBlock()] != null && physicalMap[sub.getBaseBlock()].equals(sub))).
|
||||||
forEach((sub) -> {
|
forEach((sub) -> {
|
||||||
physicalMap.remove(sub.getBaseBlock());
|
physicalMap[sub.getBaseBlock()] = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the specified block in use?
|
// Is the specified block in use?
|
||||||
public boolean isAllocated(int i) {
|
public boolean isBlockAllocated(int i) {
|
||||||
return (physicalMap.containsKey(i));
|
return (i >= physicalMap.length || physicalMap[i] != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -174,10 +191,9 @@ public class ProdosVirtualDisk implements IDisk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initDiskStructure() throws IOException {
|
private void initDiskStructure() throws IOException {
|
||||||
physicalMap = new HashMap<>();
|
|
||||||
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
|
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPhysicalPath(File f) throws IOException {
|
private void setPhysicalPath(File f) throws IOException {
|
||||||
if (physicalRoot != null && physicalRoot.equals(f)) {
|
if (physicalRoot != null && physicalRoot.equals(f)) {
|
||||||
return;
|
return;
|
||||||
@ -193,7 +209,6 @@ public class ProdosVirtualDisk implements IDisk {
|
|||||||
// Root directory ALWAYS starts on block 2!
|
// Root directory ALWAYS starts on block 2!
|
||||||
rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START, true);
|
rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START, true);
|
||||||
rootDirectory.setName("VIRTUAL");
|
rootDirectory.setName("VIRTUAL");
|
||||||
rootDirectory.allocate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -208,6 +223,6 @@ public class ProdosVirtualDisk implements IDisk {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return 0x0ffff;
|
return MAX_BLOCK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,4 +69,9 @@ public class SubNode extends DiskNode {
|
|||||||
public void readBlock(int sequence, byte[] buffer) throws IOException {
|
public void readBlock(int sequence, byte[] buffer) throws IOException {
|
||||||
parent.readBlock(sequenceNumber, buffer);
|
parent.readBlock(sequenceNumber, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return IDisk.BLOCK_SIZE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user