/* * AppleCommander - An Apple ][ image utility. * Copyright (C) 2002 by Robert Greene * robgreene at users.sourceforge.net * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program 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 General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.webcodepro.applecommander.storage; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Manages a disk that is in the ProDOS format. *

* Date created: Oct 3, 2002 11:45:25 PM * @author: Rob Greene */ public class ProdosFormatDisk extends FormattedDisk { /** * Hold on to the volume directory header. */ private ProdosVolumeDirectoryHeader volumeHeader; /** * Use this inner interface for managing the disk usage data. * This offloads format-specific implementation to the implementing class. */ private class ProdosDiskUsage implements DiskUsage { private int location = -1; private transient byte[] data = null; public boolean hasNext() { return location == -1 || location < volumeHeader.getTotalBlocks() - 1; } public void next() { location++; } /** * Get the free setting for the bitmap at the current location. */ public boolean isFree() { if (location == -1) { throw new IllegalArgumentException("Invalid dimension for isFree! Did you call next first?"); } if (data == null) { data = readVolumeBitMap(); } return isBlockFree(data, location); } public boolean isUsed() { return !isFree(); } } /** * Constructor for ProdosFormatDisk. * @param filename * @param diskImage */ public ProdosFormatDisk(String filename, byte[] diskImage) { super(filename, diskImage); volumeHeader = new ProdosVolumeDirectoryHeader(this); } /** * Create a ProdosFormatDisk. */ public static ProdosFormatDisk[] create(String filename, String diskName, int imageSize) { ProdosFormatDisk disk = new ProdosFormatDisk(filename, new byte[imageSize]); disk.setDiskName(diskName); disk.format(); return new ProdosFormatDisk[] { disk }; } /** * Identify the operating system format of this disk. * @see com.webcodepro.applecommander.storage.Disk#getFormat() */ public String getFormat() { return "ProDOS"; } /** * Retrieve a list of files. * @see com.webcodepro.applecommander.storage.Disk#getFiles() */ public List getFiles() { return getFiles(2); } /** * Build a list of files, starting in the given block number. * This works for the master as well as the subdirectories. */ protected List getFiles(int blockNumber) { List files = new ArrayList(); while (blockNumber != 0) { byte[] block = readBlock(blockNumber); int offset = 4; while (offset+ProdosCommonEntry.ENTRY_LENGTH < BLOCK_SIZE) { ProdosCommonEntry tester = new ProdosCommonEntry(this, blockNumber, offset); if (tester.isVolumeHeader() || tester.isSubdirectoryHeader()) { // ignore it, we've already got it } else { ProdosFileEntry fileEntry = new ProdosFileEntry(this, blockNumber, offset); files.add(fileEntry); if (fileEntry.isDirectory()) { int keyPointer = fileEntry.getKeyPointer(); fileEntry.setSubdirectoryHeader( new ProdosSubdirectoryHeader(this, keyPointer)); fileEntry.setFiles(getFiles(keyPointer)); } } offset+= ProdosCommonEntry.ENTRY_LENGTH; } blockNumber = AppleUtil.getWordValue(block, 2); } return files; } /** * Return the amount of free space in bytes. * @see com.webcodepro.applecommander.storage.Disk#getFreeSpace() */ public int getFreeSpace() { return getFreeBlocks() * BLOCK_SIZE; } /** * Return the number of free blocks on the disk. */ public int getFreeBlocks() { int freeBlocks = 0; int blocksToProcess = (volumeHeader.getTotalBlocks() + 4095) / 4096; int blockNumber = volumeHeader.getBitMapPointer(); for (int ix=0; ix fileData.length) { // end of file int bytesToCopy = fileData.length - offset; if (blockNumber != 0) System.arraycopy(blockData, 0, fileData, offset, bytesToCopy); offset+= bytesToCopy; } else { if (blockNumber != 0) System.arraycopy(blockData, 0, fileData, offset, blockData.length); offset+= blockData.length; } } return offset; } /** * Read the Volume Bit Map. */ public byte[] readVolumeBitMap() { int volumeBitmapBlock = volumeHeader.getBitMapPointer(); int volumeBitmapBlocks = volumeHeader.getTotalBlocks(); int blocksToRead = (volumeBitmapBlocks / 4096) + 1; // Read in the entire volume bitmap: byte[] data = new byte[blocksToRead * BLOCK_SIZE]; for (int i=0; i 2) ? block-1 : 0; AppleUtil.setWordValue(data, 0, prevBlock); AppleUtil.setWordValue(data, 2, nextBlock); writeBlock(block, data); } // setup volume header information (each set will also save data) volumeHeader.setVolumeHeader(); volumeHeader.setVolumeName(volumeName); volumeHeader.setCreationDate(new Date()); volumeHeader.setProdosVersion(0); volumeHeader.setMinimumProdosVersion(0); volumeHeader.setChanged(true); volumeHeader.setDestroy(true); volumeHeader.setRead(true); volumeHeader.setRename(true); volumeHeader.setWrite(true); volumeHeader.setEntryLength(); volumeHeader.setEntriesPerBlock(); volumeHeader.setFileCount(0); volumeHeader.setBitMapPointer(6); volumeHeader.setTotalBlocks(totalBlocks); // setup bitmap usage byte[] bitmap = readVolumeBitMap(); for (int block=0; block