mirror of
https://github.com/badvision/jace.git
synced 2024-11-24 15:30:51 +00:00
Fixed bugs in the mass-storage implementation. Directories can be mounted via drag/drop, file information is now property reported including dates, and now large files >128k work correctly.
This commit is contained in:
parent
cf87f30e35
commit
4021af3ac6
@ -123,7 +123,7 @@ public class JaceUIController {
|
||||
return null;
|
||||
}
|
||||
for (File f : files) {
|
||||
if (f.isFile()) return f;
|
||||
if (f.exists()) return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -30,11 +30,14 @@ import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Prodos directory node
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
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);
|
||||
@ -44,7 +47,6 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
init(ownerFilesystem, physicalDir);
|
||||
}
|
||||
|
||||
|
||||
private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile) throws IOException {
|
||||
setPhysicalFile(physicalFile);
|
||||
setType(EntryType.SUBDIRECTORY);
|
||||
@ -78,7 +80,8 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Checks contents of subdirectory for changes as well as directory itself (super class)
|
||||
* Checks contents of subdirectory for changes as well as directory itself
|
||||
* (super class)
|
||||
*/
|
||||
public boolean checkFile() throws IOException {
|
||||
boolean success = true;
|
||||
@ -90,7 +93,7 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
for (File f : realFileList) {
|
||||
realFiles.add(f.getName());
|
||||
}
|
||||
for (Iterator<DiskNode> i = getChildren().iterator(); i.hasNext(); ) {
|
||||
for (Iterator<DiskNode> i = getChildren().iterator(); i.hasNext();) {
|
||||
DiskNode node = i.next();
|
||||
if (realFiles.contains(node.getPhysicalFile().getName())) {
|
||||
realFiles.remove(node.getPhysicalFile().getName());
|
||||
@ -119,14 +122,15 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
checkFile();
|
||||
if (block == 0) {
|
||||
generateHeader(buffer);
|
||||
for (int i=0; i < 12 && i < children.size(); i++)
|
||||
generateFileEntry(buffer, 4 + (i+1) * FILE_ENTRY_SIZE, i);
|
||||
for (int i = 0; i < 12 && i < children.size(); i++) {
|
||||
generateFileEntry(buffer, 4 + (i + 1) * FILE_ENTRY_SIZE, i);
|
||||
}
|
||||
} else {
|
||||
int start = (block * 13) - 1;
|
||||
int end = start + 13;
|
||||
int offset = 4;
|
||||
|
||||
for (int i=start; i < end && i < children.size(); i++) {
|
||||
for (int i = start; i < end && i < children.size(); i++) {
|
||||
// TODO: Add any parts that are not file entries.
|
||||
generateFileEntry(buffer, offset, i);
|
||||
offset += FILE_ENTRY_SIZE;
|
||||
@ -136,7 +140,9 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
if (file.getName().endsWith("~")) return false;
|
||||
if (file.getName().endsWith("~")) {
|
||||
return false;
|
||||
}
|
||||
char c = file.getName().charAt(0);
|
||||
if (c == '.' || c == '~') {
|
||||
return false;
|
||||
@ -146,24 +152,27 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
|
||||
/**
|
||||
* Generate the directory header found in the base block of a directory
|
||||
*
|
||||
* @param buffer where to write data
|
||||
*/
|
||||
@SuppressWarnings("static-access")
|
||||
private void generateHeader(byte[] buffer) {
|
||||
// System.out.println("Generating directory header");
|
||||
// Previous block = 0
|
||||
generateWord(buffer, 0,0);
|
||||
generateWord(buffer, 0, 0);
|
||||
// Next block
|
||||
int nextBlock = 0;
|
||||
if (!additionalNodes.isEmpty())
|
||||
if (!additionalNodes.isEmpty()) {
|
||||
nextBlock = additionalNodes.get(0).baseBlock;
|
||||
}
|
||||
generateWord(buffer, 0x02, nextBlock);
|
||||
// Directory header + name length
|
||||
// Volumme header = 0x0f0; Subdirectory header = 0x0e0
|
||||
buffer[4]= (byte) ((baseBlock == 0x02 ? 0x0f0 : 0x0E0) + getName().length());
|
||||
buffer[4] = (byte) ((baseBlock == 0x02 ? 0x0f0 : 0x0E0) + getName().length());
|
||||
generateName(buffer, 5, this);
|
||||
for (int i=0x014 ; i <= 0x01b; i++)
|
||||
for (int i = 0x014; i <= 0x01b; i++) {
|
||||
buffer[i] = 0;
|
||||
}
|
||||
generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified());
|
||||
// Prodos 1.9
|
||||
buffer[0x020] = 0x019;
|
||||
@ -185,6 +194,7 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
|
||||
/**
|
||||
* Generate the entry of a directory
|
||||
*
|
||||
* @param buffer where to write data
|
||||
* @param offset starting offset in buffer to write
|
||||
* @param fileNumber number of file (indexed in Children array) to write
|
||||
@ -192,17 +202,17 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException {
|
||||
// System.out.println("Generating entry for "+children.get(fileNumber).getName());
|
||||
DiskNode child = children.get(fileNumber);
|
||||
child.allocate();
|
||||
// Entry Type and length
|
||||
buffer[offset] = (byte) ((child.getType().code << 4) + child.getName().length());
|
||||
// Name
|
||||
generateName(buffer, offset+1, child);
|
||||
generateName(buffer, offset + 1, child);
|
||||
// File type
|
||||
buffer[offset + 0x010] = (byte) ((child instanceof DirectoryNode) ? 0x0f : ((FileNode) child).fileType);
|
||||
// Key pointer
|
||||
generateWord(buffer, offset + 0x011, child.getBaseBlock());
|
||||
// Blocks used -- will report only one unless file is actually allocated
|
||||
// child.allocate();
|
||||
generateWord(buffer, offset + 0x013, 1 + child.additionalNodes.size());
|
||||
generateWord(buffer, offset + 0x013, child.additionalNodes.size());
|
||||
// EOF
|
||||
// TODO: Verify this is the right thing to do -- is EOF total length or a modulo?
|
||||
int length = ((int) child.physicalFile.length()) & 0x0ffffff;
|
||||
@ -214,11 +224,12 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
buffer[offset + 0x01c] = 0x19;
|
||||
// Minimum version = 0
|
||||
buffer[offset + 0x01d] = 0;
|
||||
// Access = all granted
|
||||
buffer[offset + 0x01e] = (byte) 0x0ff;
|
||||
// Access = Read-only
|
||||
buffer[offset + 0x01e] = (byte) 0x001;
|
||||
// AUX type
|
||||
if (child instanceof FileNode)
|
||||
if (child instanceof FileNode) {
|
||||
generateWord(buffer, offset + 0x01f, ((FileNode) child).loadAddress);
|
||||
}
|
||||
// Modification date
|
||||
generateTimestamp(buffer, offset + 0x021, child.physicalFile.lastModified());
|
||||
// Key pointer for directory
|
||||
@ -231,26 +242,22 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
|
||||
// yyyyyyym mmmddddd - Byte 0,1
|
||||
// ---hhhhh --mmmmmm - Byte 2,3
|
||||
// buffer[offset+1] = (byte) (((c.get(Calendar.YEAR) - 1990) << 1) + ((c.get(Calendar.MONTH)>> 3) & 1));
|
||||
buffer[offset+0] = 0;
|
||||
buffer[offset+1] = 0;
|
||||
buffer[offset+2] = 0;
|
||||
buffer[offset+3] = 0;
|
||||
buffer[offset+1] = (byte) (((c.get(Calendar.YEAR) - 2000) << 1) | ((c.get(Calendar.MONTH)+1)>> 3));
|
||||
// buffer[offset+2] = (byte) ((c.get(Calendar.MONTH)>> 3) & 1);
|
||||
// buffer[offset+3] = (byte) (((c.get(Calendar.MONTH)&7) + c.get(Calendar.DAY_OF_MONTH)) & 0x0ff);
|
||||
// buffer[offset+0] = (byte) c.get(Calendar.HOUR_OF_DAY);
|
||||
// buffer[offset+1] = (byte) c.get(Calendar.MINUTE);
|
||||
buffer[offset+0] = (byte) (((((c.get(Calendar.MONTH)+1)&7)<<5) | c.get(Calendar.DAY_OF_MONTH)) & 0x0ff);
|
||||
buffer[offset+3] = (byte) c.get(Calendar.HOUR_OF_DAY);
|
||||
buffer[offset+2] = (byte) c.get(Calendar.MINUTE);
|
||||
}
|
||||
|
||||
private void generateWord(byte[] buffer, int i, int value) {
|
||||
// Little endian format
|
||||
buffer[i] = (byte) (value & 0x0ff);
|
||||
buffer[i+1] = (byte) ((value >> 8) & 0x0ff);
|
||||
buffer[i + 1] = (byte) ((value >> 8) & 0x0ff);
|
||||
}
|
||||
|
||||
private void generateName(byte[] buffer, int offset, DiskNode node) {
|
||||
for (int i=0; i < node.getName().length(); i++) {
|
||||
buffer[offset+i] = (byte) node.getName().charAt(i);
|
||||
for (int i = 0; i < node.getName().length(); i++) {
|
||||
buffer[offset + i] = (byte) node.getName().charAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ public abstract class DiskNode {
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
public void refresh() throws IOException {
|
||||
ownerFilesystem.deallocateEntry(this);
|
||||
doRefresh();
|
||||
allocationTime = System.currentTimeMillis();
|
||||
|
@ -21,6 +21,7 @@ package jace.hardware.massStorage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Representation of a prodos file with a known file type and having a known
|
||||
@ -81,16 +82,18 @@ public class FileNode extends DiskNode {
|
||||
} else if (fileSize <= SAPLING_MAX_SIZE) {
|
||||
setType(EntryType.SAPLING);
|
||||
return EntryType.SAPLING;
|
||||
} else {
|
||||
setType(EntryType.TREE);
|
||||
return EntryType.TREE;
|
||||
}
|
||||
setType(EntryType.TREE);
|
||||
return EntryType.TREE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
String[] parts = name.split("\\.");
|
||||
FileType t = null;
|
||||
String[] parts = name.replaceAll("[^A-Za-z0-9]", ".").split("\\.");
|
||||
FileType t = FileType.UNKNOWN;
|
||||
int offset = 0;
|
||||
String prodosName = name;
|
||||
if (parts.length > 1) {
|
||||
String extension = parts[parts.length - 1].toUpperCase();
|
||||
String[] extParts = extension.split("#");
|
||||
@ -103,17 +106,14 @@ public class FileNode extends DiskNode {
|
||||
} catch (IllegalArgumentException ex) {
|
||||
System.out.println("Not sure what extension " + extension + " is!");
|
||||
}
|
||||
name = "";
|
||||
prodosName = "";
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
name += (i > 0 ? "." + parts[i] : parts[i]);
|
||||
prodosName += (i > 0 ? "." + parts[i] : parts[i]);
|
||||
}
|
||||
if (extParts[extParts.length - 1].equals("SYSTEM")) {
|
||||
name += ".SYSTEM";
|
||||
prodosName += ".SYSTEM";
|
||||
}
|
||||
}
|
||||
if (t == null) {
|
||||
t = FileType.UNKNOWN;
|
||||
}
|
||||
if (offset == 0) {
|
||||
offset = t.defaultLoadAddress;
|
||||
}
|
||||
@ -121,12 +121,13 @@ public class FileNode extends DiskNode {
|
||||
loadAddress = offset;
|
||||
|
||||
// Pass usable name (stripped of file extension and other type info) as name
|
||||
super.setName(name);
|
||||
super.setName(prodosName);
|
||||
}
|
||||
|
||||
public FileNode(ProdosVirtualDisk ownerFilesystem, File file) throws IOException {
|
||||
setOwnerFilesystem(ownerFilesystem);
|
||||
setPhysicalFile(file);
|
||||
setName(file.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -135,16 +136,19 @@ public class FileNode extends DiskNode {
|
||||
|
||||
@Override
|
||||
public void doAllocate() throws IOException {
|
||||
int dataBlocks = (int) ((getPhysicalFile().length() / ProdosVirtualDisk.BLOCK_SIZE) + 1);
|
||||
int treeBlocks;
|
||||
if (dataBlocks > 1 && dataBlocks < 257) {
|
||||
treeBlocks = 1;
|
||||
} else {
|
||||
treeBlocks = 1 + (dataBlocks / 256);
|
||||
}
|
||||
for (int i = 1; i < dataBlocks + treeBlocks; i++) {
|
||||
SubNode subNode = new SubNode(i, this);
|
||||
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) treeBlocks++;
|
||||
// if (dataBlocks > 1 && (dataBlocks*2) < ProdosVirtualDisk.BLOCK_SIZE) {
|
||||
// treeBlocks = 1;
|
||||
// } else {
|
||||
// treeBlocks = 1 + (dataBlocks * 2 / ProdosVirtualDisk.BLOCK_SIZE);
|
||||
// }
|
||||
System.out.println("Allocating "+(dataBlocks + treeBlocks)+" blocks for file "+getName()+"; data "+dataBlocks+"; tree "+treeBlocks);
|
||||
for (int i = 0; i < dataBlocks + treeBlocks; i++) {
|
||||
new SubNode(i, this);
|
||||
}
|
||||
setBaseBlock(additionalNodes.get(0).getBaseBlock());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -154,6 +158,9 @@ public class FileNode extends DiskNode {
|
||||
@Override
|
||||
public void readBlock(int block, byte[] buffer) throws IOException {
|
||||
// System.out.println("Read block "+block+" of file "+getName());
|
||||
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) treeBlocks++;
|
||||
switch (this.getType()) {
|
||||
case SEEDLING:
|
||||
readFile(buffer, 0);
|
||||
@ -163,20 +170,20 @@ public class FileNode extends DiskNode {
|
||||
readFile(buffer, (block - 1));
|
||||
} else {
|
||||
// Generate seedling index block
|
||||
generateIndex(buffer, 0, 256);
|
||||
generateIndex(buffer, 0, dataBlocks);
|
||||
}
|
||||
break;
|
||||
case TREE:
|
||||
int dataBlocks = (int) ((getPhysicalFile().length() / ProdosVirtualDisk.BLOCK_SIZE) + 1);
|
||||
int treeBlocks = (dataBlocks / 256);
|
||||
if (block == 0) {
|
||||
generateIndex(buffer, 0, treeBlocks);
|
||||
} else if (block < treeBlocks) {
|
||||
int start = treeBlocks + (block - 1 * 256);
|
||||
int end = Math.min(start + 256, treeBlocks);
|
||||
generateIndex(buffer, treeBlocks, end);
|
||||
System.out.println("Reading index for "+getName());
|
||||
generateIndex(buffer, 1, treeBlocks);
|
||||
} else if (block <= treeBlocks) {
|
||||
System.out.println("Reading tree block "+block+" for "+getName());
|
||||
int start = treeBlocks + ((block - 1) * 256);
|
||||
int end = treeBlocks + dataBlocks;
|
||||
generateIndex(buffer, start, end);
|
||||
} else {
|
||||
readFile(buffer, (block - treeBlocks));
|
||||
readFile(buffer, (block - treeBlocks - 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -190,10 +197,22 @@ public class FileNode extends DiskNode {
|
||||
}
|
||||
|
||||
private void generateIndex(byte[] buffer, int indexStart, int indexLimit) {
|
||||
int pos = 0;
|
||||
for (int i = indexStart; pos < 256 && i < indexLimit && i < additionalNodes.size(); i++, pos++) {
|
||||
buffer[pos] = (byte) (additionalNodes.get(i).baseBlock & 0x0ff);
|
||||
buffer[pos + 256] = (byte) ((additionalNodes.get(i).baseBlock >> 8) & 0x0ff);
|
||||
System.out.println("Index block contents:");
|
||||
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;
|
||||
System.out.print(Integer.toHexString(base)+":");
|
||||
buffer[count] = (byte) (base & 0x0ff);
|
||||
buffer[count + 256] = (byte) (base >> 8);
|
||||
}
|
||||
System.out.println();
|
||||
for (int i=0; i < 256; i++) {
|
||||
System.out.printf("%02X ",buffer[i]&0x0ff);
|
||||
}
|
||||
System.out.println();
|
||||
for (int i=256; i < 512; i++) {
|
||||
System.out.printf("%02X ",buffer[i]&0x0ff);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ import java.util.logging.Logger;
|
||||
* is a folder and not a disk image. FreespaceBitmap and the various Node
|
||||
* classes are used to represent the filesystem structure.
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class ProdosVirtualDisk implements IDisk {
|
||||
|
||||
@ -61,7 +61,7 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
DiskNode node = physicalMap.get(block);
|
||||
Arrays.fill(ioBuffer, (byte) (block & 0x0ff));
|
||||
if (node == null) {
|
||||
System.out.println("Reading unknown block?!");
|
||||
System.out.println("Reading unknown block " + Integer.toHexString(block));
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
memory.write(bufferAddress + i, (byte) 0, false, false);
|
||||
}
|
||||
@ -132,11 +132,15 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
}
|
||||
|
||||
// Mark space occupied by node
|
||||
public void allocateEntry(DiskNode node) {
|
||||
public void allocateEntry(DiskNode node) throws IOException {
|
||||
physicalMap.put(node.baseBlock, node);
|
||||
node.additionalNodes.stream().forEach((sub) -> {
|
||||
physicalMap.put(sub.getBaseBlock(), sub);
|
||||
});
|
||||
|
||||
for (DiskNode subnode : node.additionalNodes) {
|
||||
int blockNum = getNextFreeBlock();
|
||||
System.out.println("Allocating block " + Integer.toHexString(blockNum) + " for " + subnode.getName());
|
||||
subnode.setBaseBlock(blockNum);
|
||||
physicalMap.put(blockNum, subnode);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark space occupied by nodes as free (remove allocation mapping)
|
||||
@ -145,11 +149,11 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
if (physicalMap.get(node.baseBlock) != null && physicalMap.get(node.baseBlock).equals(node)) {
|
||||
physicalMap.remove(node.baseBlock);
|
||||
}
|
||||
node.additionalNodes.stream().filter((sub) ->
|
||||
(physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.baseBlock).equals(sub))).
|
||||
node.additionalNodes.stream().filter((sub)
|
||||
-> (physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.baseBlock).equals(sub))).
|
||||
forEach((sub) -> {
|
||||
physicalMap.remove(sub.getBaseBlock());
|
||||
});
|
||||
physicalMap.remove(sub.getBaseBlock());
|
||||
});
|
||||
}
|
||||
|
||||
// Is the specified block in use?
|
||||
@ -200,7 +204,6 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
|
||||
allocateEntry(freespaceBitmap);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,7 +31,6 @@ import java.io.IOException;
|
||||
public class SubNode extends DiskNode {
|
||||
|
||||
int sequenceNumber;
|
||||
private int seq;
|
||||
|
||||
public SubNode(int seq, DiskNode parent) throws IOException {
|
||||
init(seq, parent);
|
||||
@ -49,6 +48,11 @@ public class SubNode extends DiskNode {
|
||||
parent.additionalNodes.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return parent.getName() + "; block "+sequenceNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDeallocate() {
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public enum DiskType {
|
||||
}
|
||||
|
||||
static public DiskType determineType(File file) {
|
||||
if (!file.exists()) return null;
|
||||
if (file == null || !file.exists()) return null;
|
||||
if (file.isDirectory()) return VIRTUAL;
|
||||
if (file.getName().toLowerCase().endsWith("hdv")) {
|
||||
return LARGE;
|
||||
|
Loading…
Reference in New Issue
Block a user