AppleCommander/src/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java
T. Joseph Carter 292afd24c9 Remove -Xlint:unchecked warnings
Fixed all instances of warnings OpenJDK finds with -Xlint:unchecked.
Merging this will at least result in a dependency on JDK 1.5 or later
that I'm pretty sure of.  But since the only JDKs that are really safe
to use these days are versions 1.8 and 9, that's probably okay.

No promises that this will make modern SWT work or anything, but it's
probably at least a beginnig toward that as well.
2017-11-09 13:03:03 -08:00

521 lines
17 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.os.rdos;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import com.webcodepro.applecommander.storage.DirectoryEntry;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.StorageBundle;
import com.webcodepro.applecommander.storage.physical.ImageOrder;
import com.webcodepro.applecommander.util.AppleUtil;
import com.webcodepro.applecommander.util.TextBundle;
/**
* Manages a disk that is in the RDOS format.
* <p>
* Note that the RDOS block interleave is different than the standard DOS 3.3 format.
* Thus, when the image is made, the sectors are skewed differently - use readRdosBlock
* to read the appropriate block number.
* <p>
* Also note that the operating system is itself the first file. Block #0 is really
* track 0, sector 0 - meaning that the first file should not (cannot) be deleted.
* <p>
* RDOS appears to have been placed on 13 sector disks. This limits the number of blocks
* to 455. It also may also cause incompatibilities with other formats and other cracks.
* <p>
* Date created: Oct 7, 2002 2:03:58 PM
* @author Rob Greene
*/
public class RdosFormatDisk extends FormattedDisk {
private TextBundle textBundle = StorageBundle.getInstance();
/**
* The RDOS disks are structured in a different order than DOS 3.3.
* This table interpolates between the RDOS ordering and the DOS
* ordering. It appears that RDOS may use the physical sector number
* instead of the logical sector.
*/
private static final int sectorSkew[] = {
0, 7, 0x0e, 6, 0x0d, 5, 0x0c, 4,
0x0b, 3, 0x0a, 2, 9, 1, 8, 0x0f
};
/**
* Specifies the length of a file entry.
*/
public static final int ENTRY_LENGTH = 0x20;
/**
* Specifies the number of blocks on the disk.
* RDOS apparantly only worked on 5.25" disks.
*/
public static final int BLOCKS_ON_DISK = 455;
/**
* The known filetypes for a RDOS disk.
*/
public static final String[] filetypes = { "B", "A", "T" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
/**
* Use this inner interface for managing the disk usage data.
* This offloads format-specific implementation to the implementing class.
* A BitSet is used to track all blocks, as RDOS disks do not have a
* bitmap stored on the disk. This is safe since we know the number of blocks
* that exist. (BitSet length is of last set bit - unset bits at the end are
* "lost".)
* <p>
* Note one really unique point about RDOS - the entire disk is mapped out
* by the file entries. There are no blocks marked off, by default, by the
* operating system. However, the first file (RDOS itself) starts on block
* 0 (track 0, sector 0) and runs for 26 blocks - which covers all of track 0
* (the operating system) and the 10 sectors used for file entries.
*/
private class RdosDiskUsage implements DiskUsage {
private int location = -1;
private BitSet bitmap = null;
public boolean hasNext() {
return location == -1 || location < BLOCKS_ON_DISK - 1;
}
public void next() {
if (bitmap == null) {
bitmap = new BitSet(BLOCKS_ON_DISK);
// mark all blocks as unused
for (int b=0; b<BLOCKS_ON_DISK; b++) {
bitmap.set(b);
}
// for each file, mark the blocks used
Iterator files = getFiles().iterator();
while (files.hasNext()) {
RdosFileEntry fileEntry = (RdosFileEntry) files.next();
if (!fileEntry.isDeleted()) {
for (int b=0; b<fileEntry.getSizeInBlocks(); b++) {
bitmap.clear(fileEntry.getStartingBlock()+b);
}
}
}
location = 0;
} else {
location++;
}
}
public boolean isFree() {
return bitmap.get(location); // true = free
}
public boolean isUsed() {
return !bitmap.get(location); // false = used
}
}
/**
* Constructor for RdosFormatDisk.
*/
public RdosFormatDisk(String filename, ImageOrder imageOrder) {
super(filename, imageOrder);
}
/**
* Create a RdosFormatDisk.
*/
public static RdosFormatDisk[] create(String filename, ImageOrder imageOrder) {
RdosFormatDisk disk = new RdosFormatDisk(filename, imageOrder);
disk.format();
return new RdosFormatDisk[] { disk };
}
/**
* Read an RDOS block. The sector skewing for RDOS seems to be different.
* This routine will convert the block number to a DOS track and sector,
* handling the sector change-over. The readSector method then should
* take care of various image formats.
* <p>
* Note that sectorSkew has the full 16 sectors, even though RDOS
* itself is a 13 sector format.
*/
public byte[] readRdosBlock(int block) {
int track = block / 13;
int sector = sectorSkew[block % 13];
return readSector(track, sector);
}
/**
* Write an RDOS block. The sector skewing for RDOS seems to be different.
* This routine will convert the block number to a DOS track and sector,
* handling the sector change-over. The writeSector method then should
* take care of various image formats.
* <p>
* Note that sectorSkew has the full 16 sectors, even though RDOS
* itself is a 13 sector format.
*/
public void writeRdosBlock(int block, byte[] data) {
int track = block / 13;
int sector = sectorSkew[block % 13];
writeSector(track, sector, data);
}
/**
* RDOS really does not have a disk name. Fake one.
*/
public String getDiskName() {
byte[] block = readRdosBlock(4);
return AppleUtil.getString(block, 0xe0, 0x20);
}
/**
* Retrieve a list of files.
*/
public List<FileEntry> getFiles() {
List<FileEntry> files = new ArrayList<>();
for (int b=13; b<23; b++) {
byte[] data = readRdosBlock(b);
for (int i=0; i<data.length; i+= ENTRY_LENGTH) {
byte[] entry = new byte[ENTRY_LENGTH];
System.arraycopy(data, i, entry, 0, entry.length);
if (AppleUtil.getUnsignedByte(entry[0]) != 0) {
RdosFileEntry fileEntry = new RdosFileEntry(entry, this);
files.add(fileEntry);
}
}
}
return files;
}
/**
* Create a new FileEntry.
*/
public FileEntry createFile() throws DiskFullException {
throw new DiskFullException(textBundle.get("FileCreationNotSupported")); //$NON-NLS-1$
}
/**
* Identify if additional directories can be created. This
* may indicate that directories are not available to this
* operating system or simply that the disk image is "locked"
* to writing.
*/
public boolean canCreateDirectories() {
return false;
}
/**
* Indicates if this disk image can create a file.
* If not, the reason may be as simple as it has not beem implemented
* to something specific about the disk.
*/
public boolean canCreateFile() {
return false; // FIXME - need to implement
}
/**
* Identify the operating system format of this disk.
*/
public String getFormat() {
return textBundle.get("RdosFormatDisk.Rdos21"); //$NON-NLS-1$
}
/**
* Return the number of free blocks.
*/
public int getFreeBlocks() {
return BLOCKS_ON_DISK - getUsedBlocks();
}
/**
* Return the number of used blocks.
*/
public int getUsedBlocks() {
int used = 0;
Iterator files = getFiles().iterator();
while (files.hasNext()) {
RdosFileEntry fileEntry = (RdosFileEntry) files.next();
if (!fileEntry.isDeleted()) used+= fileEntry.getSizeInBlocks();
}
return used;
}
/**
* Return the amount of free space in bytes.
*/
public int getFreeSpace() {
return getFreeBlocks() * SECTOR_SIZE;
}
/**
* Return the amount of used space in bytes.
*/
public int getUsedSpace() {
return getUsedBlocks() * SECTOR_SIZE;
}
/**
* Get suggested dimensions for display of bitmap.
* Since RDOS uses blocks, a null is returned indicating no suggsetions.
*/
public int[] getBitmapDimensions() {
return null;
}
/**
* Get the length of the bitmap.
*/
public int getBitmapLength() {
return BLOCKS_ON_DISK;
}
/**
* Get the disk usage iterator.
*/
public DiskUsage getDiskUsage() {
return new RdosDiskUsage();
}
/**
* Get the labels to use in the bitmap.
*/
public String[] getBitmapLabels() {
return new String[] { textBundle.get("Block") }; //$NON-NLS-1$
}
/**
* Get Pascal-specific disk information.
*/
public List<DiskInformation> getDiskInformation() {
List<DiskInformation> list = super.getDiskInformation();
list.add(new DiskInformation(textBundle.get("TotalBlocks"), BLOCKS_ON_DISK)); //$NON-NLS-1$
list.add(new DiskInformation(textBundle.get("FreeBlocks"), getFreeBlocks())); //$NON-NLS-1$
list.add(new DiskInformation(textBundle.get("UsedBlocks"), getUsedBlocks())); //$NON-NLS-1$
return list;
}
/**
* Get the standard file column header information.
* This default implementation is intended only for standard mode.
*/
public List<FileColumnHeader> getFileColumnHeaders(int displayMode) {
List<FileColumnHeader> list = new ArrayList<>();
switch (displayMode) {
case FILE_DISPLAY_NATIVE:
list.add(new FileColumnHeader(textBundle.get("Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("Name"), 24, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Size"), 6, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.StartingBlock"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
break;
case FILE_DISPLAY_DETAIL:
list.add(new FileColumnHeader(textBundle.get("Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("Name"), 24, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Size"), 6, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.StartingBlock"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Address"), 5, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
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
}
/**
* RDOS dos not support directories.
*/
public boolean canHaveDirectories() {
return false;
}
/**
* 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 RdosFileEntry)) {
throw new IllegalArgumentException(textBundle.get("RdosFormatDisk.IncorrectFileEntryError")); //$NON-NLS-1$
}
RdosFileEntry rdosEntry = (RdosFileEntry) fileEntry;
int startingBlock = rdosEntry.getStartingBlock();
byte[] fileData = new byte[rdosEntry.getSizeInBlocks() * SECTOR_SIZE];
int offset = 0;
for (int blockOffset = 0; blockOffset < rdosEntry.getSizeInBlocks(); blockOffset++) {
byte[] blockData = readRdosBlock(startingBlock + blockOffset);
System.arraycopy(blockData, 0, fileData, offset, blockData.length);
offset+= blockData.length;
}
return fileData;
}
/**
* Format the disk as an RDOS disk.
* FIXME - RDOS does not "like" an AppleCommander formatted disk.
* This appears to be because the &amp;CAT command
* reads from track 1 sector 9 (whatever RDOS block that
* would be) and executes that code for the directory.
* AppleCommander will need to either clone the code or write
* its own routine. This is RDOS block #25.
* @see com.webcodepro.applecommander.storage.FormattedDisk#format()
*/
public void format() {
getImageOrder().format();
writeBootCode();
// minor hack - ensure that AppleCommander itself recognizes the
// RDOS disk!
byte[] block = readSector(0, 0x0d);
AppleUtil.setString(block, 0xe0, textBundle.get("RdosFormatDisk.IdentifierText"), 0x20); //$NON-NLS-1$
writeSector(0, 0x0d, block);
// a hack - until real code goes here.
block = new byte[256];
block[0] = 0x60;
writeSector(1, 9, block);
// write the first directory entry
// FIXME - this should use FileEntry!
byte[] data = readRdosBlock(13);
AppleUtil.setString(data, 0x00, textBundle.get("RdosFormatDisk.InitialSystemFile"), 0x18); //$NON-NLS-1$
AppleUtil.setString(data, 0x18, "B", 0x01); //$NON-NLS-1$
data[0x19] = 26;
AppleUtil.setWordValue(data, 0x1a, 0x1000);
AppleUtil.setWordValue(data, 0x1c, 6656);
AppleUtil.setWordValue(data, 0x1e, 0);
writeRdosBlock(13, data);
}
/**
* Returns the logical disk number. Returns a 0 to indicate no numbering.
*/
public int getLogicalDiskNumber() {
return 0;
}
/**
* Returns a valid filename for the given filename. RDOS
* pretty much allows anything - so it is cut to 24 characters
* and trimmed (trailing whitespace may cause confusion).
*/
public String getSuggestedFilename(String filename) {
int len = Math.min(filename.length(), 24);
return filename.toUpperCase().substring(0, len).trim();
}
/**
* Returns a valid filetype for the given filename. The most simple
* format will just assume a filetype of binary. This method is
* available for the interface to make an intelligent first guess
* as to the filetype.
*/
public String getSuggestedFiletype(String filename) {
String filetype = "B"; //$NON-NLS-1$
int pos = filename.lastIndexOf("."); //$NON-NLS-1$
if (pos > 0) {
String what = filename.substring(pos+1);
if ("txt".equalsIgnoreCase(what)) { //$NON-NLS-1$
filetype = "T"; //$NON-NLS-1$
}
}
return filetype;
}
/**
* Returns a list of possible file types. Since the filetype is
* specific to each operating system, a simple String is used.
*/
public String[] getFiletypes() {
return filetypes;
}
/**
* Indicates if this filetype requires an address component.
* Presumably, the "B" filetype is binary and will need an
* address.
*/
public boolean needsAddress(String filetype) {
return "B".equals(filetype); //$NON-NLS-1$
}
/**
* Indicates if this FormattedDisk supports a disk map.
*/
public boolean supportsDiskMap() {
return true;
}
/**
* Change to a different ImageOrder. Remains in RDOS format but
* the underlying order can chage.
* @see ImageOrder
*/
public void changeImageOrder(ImageOrder imageOrder) {
AppleUtil.changeImageOrderByTrackAndSector(getImageOrder(), imageOrder);
setImageOrder(imageOrder);
}
/**
* Writes the raw bytes into the file. This bypasses any special formatting
* of the data (such as prepending the data with a length and/or an address).
* Typically, the FileEntry.setFileData method should be used.
*/
public void setFileData(FileEntry fileEntry, byte[] fileData) throws DiskFullException {
// TODO implement setFileData
}
/**
* Create a new DirectoryEntry.
* @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory()
*/
public DirectoryEntry createDirectory() throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
/**
* Create a new DirectoryEntry.
* @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory()
*/
public DirectoryEntry createDirectory(String name) throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
}