/* * Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package jace.hardware.massStorage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; /** * Representation of a prodos file with a known file type and having a known * size (either seedling, sapling or tree) * * @author Brendan Robert (BLuRry) brendan.robert@gmail.com */ public class FileNode extends DiskNode { @Override public int getLength() { return (int) getPhysicalFile().length(); } public enum FileType { UNKNOWN(0x00, 0x0000), ADB(0x019, 0x0000), AWP(0x01a, 0x0000), ASP(0x01b, 0x0000), BAD(0x01, 0x0000), BIN(0x06, 0x0300), CLASS(0xED, 0x0000), BAS(0xfc, 0x0801), CMD(0x0f0, 0x0000), INT(0xfa, 0x0801), IVR(0xfb, 0x0000), PAS(0xef, 0x0000), REL(0x0Fe, 0x0000), SHK(0x0e0, 0x08002), SDK(0x0e0, 0x08002), SYS(0x0ff, 0x02000), SYSTEM(0x0ff, 0x02000), TXT(0x04, 0x0000), U01(0x0f1, 0x0000), U02(0x0f2, 0x0000), U03(0x0f3, 0x0000), U04(0x0f4, 0x0000), U05(0x0f5, 0x0000), U06(0x0f6, 0x0000), U07(0x0f7, 0x0000), U08(0x0f8, 0x0000), VAR(0x0FD, 0x0000); public int code = 0; public int defaultLoadAddress = 0; FileType(int code, int addr) { this.code = code; 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 loadAddress = 0x00; public static int SEEDLING_MAX_SIZE = ProdosVirtualDisk.BLOCK_SIZE; public static int SAPLING_MAX_SIZE = ProdosVirtualDisk.BLOCK_SIZE * 128; @Override public EntryType getType() { long fileSize = getPhysicalFile().length(); if (fileSize <= SEEDLING_MAX_SIZE) { setType(EntryType.SEEDLING); return EntryType.SEEDLING; } else if (fileSize <= SAPLING_MAX_SIZE) { setType(EntryType.SAPLING); return EntryType.SAPLING; } else { setType(EntryType.TREE); return EntryType.TREE; } } @Override public void setName(String name) { FileType t = FileType.UNKNOWN; int offset = 0; String prodosName = name; if (name.matches("^.*?#[0-9A-Fa-f]{6}$")) { int type = Integer.parseInt(name.substring(name.length() - 6, name.length() - 4), 16); offset = Integer.parseInt(name.substring(name.length() - 4), 16); t = FileType.findByCode(type); prodosName = name.substring(0, name.length()-7).replaceAll("[^A-Za-z0-9#]", ".").toUpperCase(); } else { String[] parts = name.replaceAll("[^A-Za-z0-9#]", ".").split("\\."); if (parts.length > 1) { String extension = parts[parts.length - 1].toUpperCase(); String[] extParts = extension.split("\\#"); if (extParts.length == 2) { offset = Integer.parseInt(extParts[1], 16); extension = extParts[0]; } try { t = FileType.valueOf(extension); } catch (IllegalArgumentException ex) { 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) { offset = t.defaultLoadAddress; } fileType = t.code; loadAddress = offset; // Pass usable name (stripped of file extension and other type info) as name super.setName(prodosName); } public FileNode(ProdosVirtualDisk ownerFilesystem, File file) throws IOException { super(ownerFilesystem); setPhysicalFile(file); setName(file.getName()); allocate(); } @Override public void doDeallocate() { } @Override public void doAllocate() throws IOException { 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++; } for (int i = 1; i < dataBlocks + treeBlocks; i++) { new SubNode(i, this); } } @Override public void doRefresh() { } @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) { treeBlocks++; } switch (this.getType()) { case SEEDLING: readFile(buffer, 0); break; case SAPLING: if (block > 0) { readFile(buffer, (block - 1)); } else { // Generate seedling index block generateIndex(buffer, 1, dataBlocks + 1); } break; case TREE: if (block == 0) { generateIndex(buffer, 1, treeBlocks); } else if (block <= treeBlocks) { int start = treeBlocks + ((block - 1) * 256); int end = treeBlocks + dataBlocks; generateIndex(buffer, start, end); } else { readFile(buffer, (block - treeBlocks - 1)); } break; } } private void readFile(byte[] buffer, int start) throws IOException { try (FileInputStream f = new FileInputStream(physicalFile)) { f.skip(start * ProdosVirtualDisk.BLOCK_SIZE); f.read(buffer, 0, ProdosVirtualDisk.BLOCK_SIZE); } } private void generateIndex(byte[] buffer, int indexStart, int indexLimit) { for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i <= additionalNodes.size(); i++, count++) { int base = getNodeSequence(i).getBaseBlock(); buffer[count] = (byte) (base & 0x0ff); buffer[count + 256] = (byte) (base >> 8); } } }