AppleCommander/src/com/webcodepro/applecommander/storage/DosFormatDisk.java
Robert Greene 6a59567b16 Added UniDosFormatDisk - which is essentially a DOS disk except that
the physical disk supports two logical disks. Added a logical disk number
which returns 0 when there are no logical disks or the specific logical
disk number if they exist. Currently, only UniDosFormatDisk does this.
In the future, Pascal disk may support this and possibly ProDOS disks
that have sections set aside (ie, a Pascal partition) - if they exist in the
real world.
2002-12-14 05:45:08 +00:00

485 lines
14 KiB
Java

/*
* 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.List;
/**
* Manages a disk that is in Apple DOS 3.3 format.
* <p>
* Date created: Oct 4, 2002 12:29:23 AM
* @author: Rob Greene
*/
public class DosFormatDisk extends FormattedDisk {
/**
* Indicates the length in bytes of the DOS file entry field.
*/
public static final int FILE_DESCRIPTIVE_ENTRY_LENGTH = 35;
/**
* Indicates the index of the track in the location array.
*/
public static final int TRACK_LOCATION_INDEX = 0;
/**
* Indicates the index of the sector in the location array.
*/
public static final int SECTOR_LOCATION_INDEX = 1;
/**
* The standard DOS 3.3 catalog track.
*/
public static final int CATALOG_TRACK = 17;
/**
* The standard VTOC sector.
*/
public static final int VTOC_SECTOR = 0;
/**
* Use this inner interface for managing the disk usage data.
* This offloads format-specific implementation to the implementing class.
*/
private class DosDiskUsage implements DiskUsage {
private int[] location = null;
public boolean hasNext() {
return location == null
|| (location[TRACK_LOCATION_INDEX] < getTracks()
&& location[SECTOR_LOCATION_INDEX] < getSectors());
}
public void next() {
if (location == null) {
location = new int[2];
} else {
location[SECTOR_LOCATION_INDEX]++;
if (location[SECTOR_LOCATION_INDEX] >= getSectors()) {
location[SECTOR_LOCATION_INDEX] = 0;
location[TRACK_LOCATION_INDEX]++;
}
}
}
/**
* Get the free setting for the bitmap at the current location.
*/
public boolean isFree() {
if (location == null || location.length != 2) {
throw new IllegalArgumentException("Invalid dimension for isFree! Did you call next first?");
}
return isSectorFree(location[TRACK_LOCATION_INDEX],
location[SECTOR_LOCATION_INDEX], readVtoc());
}
public boolean isUsed() {
return !isFree();
}
}
/**
* Constructor for DosFormatDisk.
* @param filename
* @param diskImage
* @param order
*/
public DosFormatDisk(String filename, byte[] diskImage) {
super(filename, diskImage);
}
/**
* Constructor for DosFormatDisk. All DOS disk images are expected to
* be 140K in size.
* @param filename
* @param diskImage
* @param order
*/
public DosFormatDisk(String filename) {
super(filename, new byte[APPLE_140KB_DISK]);
}
/**
* Identify the operating system format of this disk as DOS 3.3.
* @see com.webcodepro.applecommander.storage.Disk#getFormat()
*/
public String getFormat() {
return "DOS 3.3";
}
/**
* Retrieve a list of files.
* @see com.webcodepro.applecommander.storage.Disk#getFiles()
*/
public List getFiles() {
List list = new ArrayList();
byte[] vtoc = readVtoc();
int track = AppleUtil.getUnsignedByte(vtoc[1]);
int sector = AppleUtil.getUnsignedByte(vtoc[2]);
while (track != 0) { // iterate through all catalog sectors
byte[] catalogSector = readSector(track, sector);
int offset = 0x0b;
while (offset < 0xff) { // iterate through all entries
byte[] entry = new byte[FILE_DESCRIPTIVE_ENTRY_LENGTH];
System.arraycopy(catalogSector, offset, entry, 0, entry.length);
if (entry[0] != 0) {
list.add(new DosFileEntry(entry, this));
}
offset+= entry.length;
}
track = catalogSector[1];
sector = catalogSector[2];
}
return list;
}
/**
* Identify if this disk format as not capable of having directories.
* @see com.webcodepro.applecommander.storage.Disk#hasDirectories()
*/
public boolean canHaveDirectories() {
return false;
}
/**
* Compute the amount of freespace available on the disk.
* This algorithm completely ignores tracks and sectors by
* running through the entire bitmap stored on the VTOC.
* @see com.webcodepro.applecommander.storage.Disk#getFreeSpace()
*/
public int getFreeSpace() {
return getFreeSectors() * SECTOR_SIZE;
}
/**
* Comput the number of free sectors available on the disk.
*/
public int getFreeSectors() {
byte[] vtoc = readVtoc();
int freeSectors = 0;
for (int offset=0x38; offset<0xff; offset++) {
byte bitmap = vtoc[offset];
freeSectors+= AppleUtil.getBitCount(bitmap);
}
return freeSectors;
}
/**
* Return the amount of used space in bytes.
* @see com.webcodepro.applecommander.storage.Disk#getUsedSpace()
*/
public int getUsedSpace() {
return getUsedSectors() * SECTOR_SIZE;
}
/**
* Compute the number of used sectors on the disk.
*/
public int getUsedSectors() {
return getTotalSectors() - getFreeSectors();
}
/**
* Compute the total number of sectors available on the disk.
*/
public int getTotalSectors() {
int tracks = getTracks();
int sectors = getSectors();
return tracks * sectors;
}
/**
* Return the DOS disk name. Basically, the DISK VOLUME #xxx
* that a CATALOG command would show. Note that Java bytes are
* signed, so a little mojo is in order.
* @see com.webcodepro.applecommander.storage.Disk#getDiskName()
*/
public String getDiskName() {
int volumeNumber = AppleUtil.getUnsignedByte(readVtoc()[0x06]);
return "DISK VOLUME #" + volumeNumber;
}
/**
* Return the VTOC (Volume Table Of Contents).
*/
protected byte[] readVtoc() {
return readSector(CATALOG_TRACK, VTOC_SECTOR);
}
/**
* Get the disk usage iterator.
*/
public DiskUsage getDiskUsage() {
return new DosDiskUsage();
}
/**
* Get the number of tracks on this disk.
*/
public int getTracks() {
byte[] vtoc = readVtoc();
return AppleUtil.getUnsignedByte(vtoc[0x34]);
}
/**
* Get the number of sectors on this disk.
*/
public int getSectors() {
byte[] vtoc = readVtoc();
return AppleUtil.getUnsignedByte(vtoc[0x35]);
}
/**
* Get suggested dimensions for display of bitmap. For DOS 3.3, that information
* is stored in the VTOC, and that information is fairly important.
* @see com.webcodepro.applecommander.storage.FormattedDisk#getBitmapDimensions()
*/
public int[] getBitmapDimensions() {
int tracks = getTracks();
int sectors = getSectors();
return new int[] { tracks, sectors };
}
/**
* Get the length of the bitmap.
*/
public int getBitmapLength() {
return getTotalSectors();
}
/**
* Get the labels to use in the bitmap.
*/
public String[] getBitmapLabels() {
return new String[] { "Track", "Sector" };
}
/**
* Get DOS-specific disk information.
*/
public List getDiskInformation() {
List list = super.getDiskInformation();
list.add(new DiskInformation("Total Sectors", getTotalSectors()));
list.add(new DiskInformation("Free Sectors", getFreeSectors()));
list.add(new DiskInformation("Used Sectors", getUsedSectors()));
list.add(new DiskInformation("Tracks On Disk", getTracks()));
list.add(new DiskInformation("Sectors On Disk", getSectors()));
return list;
}
/**
* Get the standard file column header information.
* This default implementation is intended only for standard mode.
*/
public List getFileColumnHeaders(int displayMode) {
List list = new ArrayList();
switch (displayMode) {
case FILE_DISPLAY_NATIVE:
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Size (sectors)", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
break;
case FILE_DISPLAY_DETAIL:
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Size (bytes)", 6, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Size (sectors)", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Deleted?", 7, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Track/Sector List", 7, FileColumnHeader.ALIGN_CENTER));
break;
default: // FILE_DISPLAY_STANDARD
list.addAll(super.getFileColumnHeaders(displayMode));
break;
}
return list;
}
/**
* Indicates if this disk format supports "deleted" files.
*/
public boolean supportsDeletedFiles() {
return true;
}
/**
* Indicates if this disk image can read data from a file.
*/
public boolean canReadFileData() {
return true;
}
/**
* Indicates if this disk image can write data to a file.
*/
public boolean canWriteFileData() {
return false; // FIXME - not implemented
}
/**
* Indicates if this disk image can create a file.
*/
public boolean canCreateFile() {
return false; // FIXME - not implemented
}
/**
* Indicates if this disk image can delete a file.
*/
public boolean canDeleteFile() {
return false; // FIXME - not implemented
}
/**
* Get the data associated with the specified FileEntry.
*/
public byte[] getFileData(FileEntry fileEntry) {
if ( !(fileEntry instanceof DosFileEntry)) {
throw new IllegalArgumentException("Most have a DOS 3.3 file entry!");
}
DosFileEntry dosEntry = (DosFileEntry) fileEntry;
// Size is calculated by sectors used - not actual size - as size varies
// on filetype, etc.
byte[] fileData = new byte[(dosEntry.getSectorsUsed()-1) * SECTOR_SIZE];
int track = dosEntry.getTrack();
int sector = dosEntry.getSector();
int offset = 0;
while (track != 0) {
byte[] trackSectorList = readSector(track, sector);
track = AppleUtil.getUnsignedByte(trackSectorList[0x01]);
sector = AppleUtil.getUnsignedByte(trackSectorList[0x02]);
for (int i=0x0c; i<0x100; i+=2) {
int t = AppleUtil.getUnsignedByte(trackSectorList[i]);
if (t == 0) break;
int s = AppleUtil.getUnsignedByte(trackSectorList[i+1]);
byte[] sectorData = readSector(t,s);
System.arraycopy(sectorData, 0, fileData, offset, sectorData.length);
offset+= sectorData.length;
}
}
return fileData;
}
/**
* Format the disk as DOS 3.3.
* @see com.webcodepro.applecommander.storage.FormattedDisk#format()
*/
public void format() {
writeBootCode();
// create catalog sectors
byte[] data = new byte[SECTOR_SIZE];
for (int sector=15; sector > 0; sector--) {
if (sector == 0) {
data[0x01] = CATALOG_TRACK;
data[0x02] = (byte)(sector-1);
} else {
data[0x01] = 0;
data[0x02] = 0;
}
writeSector(CATALOG_TRACK, sector, data);
}
// create VTOC
data[0x01] = CATALOG_TRACK; // track# of first catalog sector
data[0x02] = 15; // sector# of first catalog sector
data[0x03] = 3; // DOS 3.3 formatted
data[0x06] = (byte)254; // DISK VOLUME#
data[0x27] = 122; // maximum # of T/S pairs in a sector
data[0x30] = CATALOG_TRACK+1; // last track where sectors allocated
data[0x31] = 1; // direction of allocation
data[0x34] = 35; // tracks per disk
data[0x35] = 16; // sectors per disk
data[0x37] = 1; // 36/37 are # of bytes per sector
for (int track=0; track<35; track++) {
for (int sector=0; sector<16; sector++) {
if (track == 0 || track == CATALOG_TRACK) {
setSectorUsed(track, sector, data);
} else {
setSectorFree(track, sector, data);
}
}
}
writeSector(CATALOG_TRACK, VTOC_SECTOR, data);
}
/**
* Indicates if a specific track/sector is free.
*/
public boolean isSectorFree(int track, int sector, byte[] vtoc) {
checkRange(track, sector);
byte byt = vtoc[getFreeMapByte(track, sector)];
return AppleUtil.isBitSet(byt, getFreeMapBit(sector));
}
/**
* Indicates if a specific track/sector is used.
*/
public boolean isSectorUsed(int track, int sector, byte[] vtoc) {
return !isSectorFree(track, sector, vtoc);
}
/**
* Sets the track/sector indicator to free.
*/
public void setSectorFree(int track, int sector, byte[] vtoc) {
checkRange(track, sector);
int offset = getFreeMapByte(track, sector);
byte byt = vtoc[offset];
byt = AppleUtil.setBit(byt, getFreeMapBit(sector));
vtoc[offset] = byt;
}
/**
* Sets the track/sector indicator to used.
*/
public void setSectorUsed(int track, int sector, byte[] vtoc) {
checkRange(track, sector);
int offset = getFreeMapByte(track, sector);
byte byt = vtoc[offset];
byt = AppleUtil.clearBit(byt, getFreeMapBit(sector));
vtoc[offset] = byt;
}
/**
* Compute the VTOC byte for the T/S map.
*/
protected int getFreeMapByte(int track, int sector) {
return 0x38 + (track * 4) + (sector / 8);
}
/**
* Compute the VTOC bit for the T/S map.
*/
protected int getFreeMapBit(int sector) {
return 7 - (sector % 8);
}
/**
* Validate track/sector range. This just validates the
* maximum values allowable for track and sector.
*/
protected void checkRange(int track, int sector) {
if (track > 50 || sector > 32) {
throw new IllegalArgumentException(
"Invalid track (" + track + "), sector (" + sector
+ ") combination.");
}
}
/**
* Returns the logical disk number. Returns a 0 to indicate no numbering.
*/
public int getLogicalDiskNumber() {
return 0;
}
}