dmolony-DiskBrowser/src/com/bytezone/diskbrowser/prodos/FileEntry.java

826 lines
29 KiB
Java
Executable File

package com.bytezone.diskbrowser.prodos;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.applefile.ApplesoftBasicProgram;
import com.bytezone.diskbrowser.applefile.AssemblerProgram;
import com.bytezone.diskbrowser.applefile.BasicProgramGS;
import com.bytezone.diskbrowser.applefile.BasicTextFile;
import com.bytezone.diskbrowser.applefile.CharacterRom;
import com.bytezone.diskbrowser.applefile.DefaultAppleFile;
import com.bytezone.diskbrowser.applefile.DeviceDriver;
import com.bytezone.diskbrowser.applefile.DosMasterFile;
import com.bytezone.diskbrowser.applefile.DoubleHiResImage;
import com.bytezone.diskbrowser.applefile.ErrorMessageFile;
import com.bytezone.diskbrowser.applefile.ExoBuffer;
import com.bytezone.diskbrowser.applefile.FaddenHiResImage;
import com.bytezone.diskbrowser.applefile.FileSystemTranslator;
import com.bytezone.diskbrowser.applefile.FileTypeDescriptorTable;
import com.bytezone.diskbrowser.applefile.FinderData;
import com.bytezone.diskbrowser.applefile.FontFile;
import com.bytezone.diskbrowser.applefile.HiResImage;
import com.bytezone.diskbrowser.applefile.IconFile;
import com.bytezone.diskbrowser.applefile.IntegerBasicProgram;
import com.bytezone.diskbrowser.applefile.LodeRunner;
import com.bytezone.diskbrowser.applefile.MerlinSource;
import com.bytezone.diskbrowser.applefile.ObjectModule;
import com.bytezone.diskbrowser.applefile.OriginalHiResImage;
import com.bytezone.diskbrowser.applefile.PascalArea;
import com.bytezone.diskbrowser.applefile.PascalCode;
import com.bytezone.diskbrowser.applefile.ProdosDirectory;
import com.bytezone.diskbrowser.applefile.QuickDrawFont;
import com.bytezone.diskbrowser.applefile.SHRPictureFile1;
import com.bytezone.diskbrowser.applefile.SHRPictureFile2;
import com.bytezone.diskbrowser.applefile.Selector;
import com.bytezone.diskbrowser.applefile.ShapeTable;
import com.bytezone.diskbrowser.applefile.SimpleText;
import com.bytezone.diskbrowser.applefile.StoredVariables;
import com.bytezone.diskbrowser.applefile.TextBuffer;
import com.bytezone.diskbrowser.appleworks.AppleworksADBFile;
import com.bytezone.diskbrowser.appleworks.AppleworksSSFile;
import com.bytezone.diskbrowser.appleworks.AppleworksWPFile;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.utilities.Utility;
// - Set sector types for each used sector
// - Populate dataBlocks, indexBlocks, catalogBlock and masterIndexBlock
// - Provide getDataSource ()
// -----------------------------------------------------------------------------------//
class FileEntry extends CatalogEntry implements ProdosConstants
// -----------------------------------------------------------------------------------//
{
private final int fileType;
final int keyPtr;
private final int blocksUsed;
private final int endOfFile;
private final int auxType;
private final LocalDateTime modified;
private final int headerPointer;
private DataSource file;
private final DiskAddress catalogBlock;
private ResourceFork resourceFork;
private DiskAddress masterIndexBlock;
private final List<DiskAddress> indexBlocks = new ArrayList<> ();
private boolean invalid;
private FileEntry link;
// ---------------------------------------------------------------------------------//
FileEntry (ProdosDisk fDisk, byte[] entryBuffer, DirectoryHeader parent,
int parentBlock, int entryNo)
// ---------------------------------------------------------------------------------//
{
super (fDisk, entryBuffer, parentBlock, entryNo);
assert parent != null;
this.parentDirectory = parent;
this.catalogBlock = this.disk.getDiskAddress (parentBlock);
fileType = entryBuffer[0x10] & 0xFF;
keyPtr = Utility.getShort (entryBuffer, 0x11);
blocksUsed = Utility.getShort (entryBuffer, 0x13);
endOfFile = Utility.intValue (entryBuffer[21], entryBuffer[22], entryBuffer[23]);
auxType = Utility.getShort (entryBuffer, 0x1F);
modified = Utility.getAppleDate (entryBuffer, 0x21);
headerPointer = Utility.getShort (entryBuffer, 0x25);
switch (storageType)
{
case SEEDLING:
case SAPLING:
case TREE:
addDataBlocks (storageType, keyPtr, dataBlocks);
break;
case GSOS_EXTENDED_FILE:
readForks ();
break;
case SUBDIRECTORY:
int block = keyPtr;
do
{
DiskAddress diskAddress = disk.getDiskAddress (block);
if (diskAddress == null)
break;
dataBlocks.add (diskAddress);
byte[] buffer = disk.readBlock (block);
block = Utility.getShort (buffer, 2);
} while (block > 0);
break;
case PASCAL_ON_PROFILE:
for (int i = keyPtr; i < disk.getTotalBlocks (); i++)
{
dataBlocks.add (disk.getDiskAddress (i));
parentDisk.setSectorType (i, parentDisk.dataSector);
}
break;
default:
System.out.println ("Unknown storage type: " + storageType);
}
}
// ---------------------------------------------------------------------------------//
private void readForks ()
// ---------------------------------------------------------------------------------//
{
parentDisk.setSectorType (keyPtr, parentDisk.extendedKeySector);
indexBlocks.add (disk.getDiskAddress (keyPtr));
byte[] buffer2 = disk.readBlock (keyPtr); // data fork and resource fork
// read 2 mini entries (data fork & resource fork)
for (int i = 0; i < 512; i += 256)
{
int storageType = buffer2[i] & 0x0F;
int keyBlock = Utility.getShort (buffer2, i + 1);
int size = Utility.getShort (buffer2, i + 3);
int eof = Utility.readTriple (buffer2, i + 5);
if (i < 256)
addDataBlocks (storageType, keyBlock, dataBlocks);
else
addDataBlocks (storageType, keyBlock, resourceBlocks);
}
resourceFork = new ResourceFork (disk.readBlocks (resourceBlocks));
if (!resourceFork.isValid ())
System.out.printf ("Invalid Resource Fork: %s%n", getUniqueName ());
}
// ---------------------------------------------------------------------------------//
private void addDataBlocks (int storageType, int keyPtr, List<DiskAddress> dataBlocks)
// ---------------------------------------------------------------------------------//
{
DiskAddress emptyDiskAddress = disk.getDiskAddress (0);
List<Integer> blockNos = new ArrayList<> ();
switch (storageType)
{
case SEEDLING:
if (isValid (keyPtr))
blockNos.add (keyPtr);
break;
case SAPLING:
if (isValid (keyPtr))
blockNos.addAll (readIndex (keyPtr));
break;
case TREE:
if (isValid (keyPtr))
for (Integer indexBlock : readMasterIndex (keyPtr))
if (isValid (indexBlock))
blockNos.addAll (readIndex (indexBlock));
break;
}
// remove trailing empty blocks
while (blockNos.size () > 0 && blockNos.get (blockNos.size () - 1) == 0)
blockNos.remove (blockNos.size () - 1);
for (Integer blockNo : blockNos)
{
if (blockNo == 0)
dataBlocks.add (emptyDiskAddress);
else
{
parentDisk.setSectorType (blockNo, parentDisk.dataSector);
dataBlocks.add (disk.getDiskAddress (blockNo));
}
}
}
// ---------------------------------------------------------------------------------//
private List<Integer> readIndex (int blockPtr)
// ---------------------------------------------------------------------------------//
{
List<Integer> blocks = new ArrayList<> (256);
if (blockPtr == 0) // master index contains a zero
for (int i = 0; i < 256; i++)
blocks.add (0);
else
{
parentDisk.setSectorType (blockPtr, parentDisk.indexSector);
indexBlocks.add (disk.getDiskAddress (blockPtr));
byte[] buffer = disk.readBlock (blockPtr);
for (int i = 0; i < 256; i++)
{
int blockNo = (buffer[i] & 0xFF) | ((buffer[i + 0x100] & 0xFF) << 8);
blocks.add (isValid (blockNo) ? blockNo : 0);
}
}
return blocks;
}
// ---------------------------------------------------------------------------------//
private List<Integer> readMasterIndex (int keyPtr)
// ---------------------------------------------------------------------------------//
{
masterIndexBlock = disk.getDiskAddress (keyPtr);
parentDisk.setSectorType (keyPtr, parentDisk.masterIndexSector);
indexBlocks.add (disk.getDiskAddress (keyPtr));
byte[] buffer = disk.readBlock (keyPtr); // master index
int highest = 0x80;
while (highest-- > 0) // decrement after test
if (buffer[highest] != 0 || buffer[highest + 0x100] != 0)
break;
List<Integer> blocks = new ArrayList<> (highest + 1);
for (int i = 0; i <= highest; i++)
{
int blockNo = (buffer[i] & 0xFF) | ((buffer[i + 256] & 0xFF) << 8);
blocks.add (isValid (blockNo) ? blockNo : 0);
}
return blocks;
}
// ---------------------------------------------------------------------------------//
private boolean isValid (int blockNo)
// ---------------------------------------------------------------------------------//
{
if (false)
{
if (!disk.isValidAddress (blockNo))
System.out.println ("--Invalid Block Address: " + blockNo);
if (parentDisk.isSectorFree (blockNo))
System.out.println ("--Free block: " + blockNo);
}
return disk.isValidAddress (blockNo) && !parentDisk.isSectorFree (blockNo);
}
// ---------------------------------------------------------------------------------//
@Override
public DataSource getDataSource ()
// ---------------------------------------------------------------------------------//
{
if (file != null)
return file;
if (invalid)
{
file = new DefaultAppleFile (name, null);
return file;
}
if (fileType == FILE_TYPE_TEXT && auxType > 0) // random access file
return getRandomAccessTextFile ();
byte[] buffer = getBuffer ();
byte[] exactBuffer = getExactBuffer (buffer);
try
{
switch (fileType)
{
case FILE_TYPE_OVL:
if (endOfFile == 0x2000 && auxType == 0)
{
file = new OriginalHiResImage (name, exactBuffer, auxType);
break;
}
else if (endOfFile == 0x800 && "SELECTOR.LIST".equals (name))
{
file = new Selector (name, exactBuffer);
break;
}
// drop through !!
case FILE_TYPE_BINARY:
case FILE_TYPE_RELOCATABLE:
case FILE_TYPE_SYS:
case FILE_TYPE_BAT:
if (SimpleText.isHTML (exactBuffer))
file = new SimpleText (name, exactBuffer);
else if (HiResImage.isGif (exactBuffer) || HiResImage.isPng (exactBuffer))
file = new OriginalHiResImage (name, exactBuffer, auxType);
else if (name.endsWith (".BMP") && HiResImage.isBmp (exactBuffer))
file = new OriginalHiResImage (name, exactBuffer, auxType);
else if (name.endsWith (".3200")) // $C1/02
file = new SHRPictureFile2 (name, exactBuffer, 0xC1, 0x02, endOfFile);
else if (name.endsWith (".3201") || HiResImage.isAPP (exactBuffer)) // $C0/04
// I made up aux=99 to test it without stepping on aux==04
file = new SHRPictureFile2 (name, exactBuffer, 0xC0, 99, endOfFile);
else if (name.endsWith (".FNT") && FontFile.isFont (exactBuffer))
file = new FontFile (name, exactBuffer, auxType);
else if (name.endsWith (".FONT") && FontFile.isFont (exactBuffer))
file = new FontFile (name, exactBuffer, auxType);
else if (ShapeTable.isShapeTable (exactBuffer))
file = new ShapeTable (name, exactBuffer);
else if (link != null)
{
if (name.endsWith (".AUX"))
file = new DoubleHiResImage (name, link.getBuffer (), exactBuffer);
else
file = new DoubleHiResImage (name, exactBuffer, link.getBuffer ());
}
else if (name.endsWith (".PAC") || name.endsWith (".A2FC")
|| (endOfFile == 0x4000 && auxType == 0x2000))
file = new DoubleHiResImage (name, exactBuffer);
else if (endOfFile == 0x4000 && auxType == 0x4000)
file = new DoubleHiResImage (name, exactBuffer);
else if (ExoBuffer.isExomizer (exactBuffer, auxType))
{
ExoBuffer exoBuffer = new ExoBuffer (exactBuffer);
byte[] outBuffer = exoBuffer.getExpandedBuffer ();
switch (outBuffer.length)
{
case 0x2000:
file = new OriginalHiResImage (name, outBuffer, 0x4000);
break;
case 0x4000:
file = new DoubleHiResImage (name, outBuffer);
break;
case 0x8000:
file =
new SHRPictureFile2 (name, outBuffer, FILE_TYPE_PIC, 0x2000, 0x8000);
break;
default:
file = new AssemblerProgram (name, exactBuffer, auxType);
}
}
else if (oneOf (endOfFile, 0x1FF8, 0x1FFF, 0x2000, 0x4000)
&& oneOf (auxType, 0x1FFF, 0x2000, 0x4000, 0x6000))
file = new OriginalHiResImage (name, exactBuffer, auxType);
else if (endOfFile == 0x9600 && name.startsWith ("LVL."))
file = new LodeRunner (name, exactBuffer);
else if (auxType == 0x1000 && CharacterRom.isRom (exactBuffer))
file = new CharacterRom (name, exactBuffer);
else if (auxType == 0 && endOfFile == 0x8000)
{
// see gs basic disk, one of the four pictures looks ok
file = new SHRPictureFile2 (name, exactBuffer, 0xC1, 0, endOfFile);
}
// else if (name.endsWith (".PIC")) // 0091 X-BASIC../../XBASIC.PIC
// file = new SHRPictureFile2 (name, exactBuffer, fileType, auxType, endOfFile);
else if ((name.equals ("DOS.3.3") || name.equals ("DDOS.3.3"))
&& endOfFile == 0x2800 && DosMasterFile.isDos33 (parentDisk, exactBuffer))
{
file = new DosMasterFile (name, exactBuffer);
}
else
{
file = new AssemblerProgram (name, exactBuffer, auxType);
if (exactBuffer.length < buffer.length)
((AssemblerProgram) file).setExtraBuffer (buffer, exactBuffer.length,
buffer.length - exactBuffer.length);
}
break;
case FILE_TYPE_TEXT:
assert auxType == 0; // auxType > 0 handled above
if (name.endsWith (".S"))
file = new MerlinSource (name, exactBuffer, auxType, endOfFile);
else if (name.endsWith ("PLA"))
file = new SimpleText (name, exactBuffer);
else if (name.endsWith (".GIF") && HiResImage.isGif (exactBuffer))
file = new OriginalHiResImage (name, exactBuffer, auxType);
else
file = new BasicTextFile (name, exactBuffer, auxType, endOfFile);
break;
case FILE_TYPE_APPLESOFT_BASIC:
file = new ApplesoftBasicProgram (name, exactBuffer);
break;
case FILE_TYPE_GS_BASIC:
// 0132 816-Paint.po has GSB files that crash because they are palettes
if (buffer[0] == 4 && buffer[1] == 16) // complete guess
file = new BasicProgramGS (name, exactBuffer);
else
file = new DefaultAppleFile (name, exactBuffer);
break;
case FILE_TYPE_INTEGER_BASIC:
file = new IntegerBasicProgram (name, exactBuffer);
break;
case FILE_TYPE_DIRECTORY:
VolumeDirectoryHeader vdh = parentDisk.getVolumeDirectoryHeader ();
file = new ProdosDirectory (parentDisk, name, buffer, vdh.totalBlocks,
vdh.freeBlocks, vdh.usedBlocks);
break;
case FILE_TYPE_APPLESOFT_BASIC_VARS:
if (endOfFile == 0)
{
System.out.println ("Stored Variables EOF = 0");
file = new StoredVariables (name, buffer);
}
else
file = new StoredVariables (name, exactBuffer);
break;
case FILE_TYPE_APPLETALK:
file = new DefaultAppleFile (name + " (Appletalk file)", buffer);
break;
case FILE_TYPE_GWP:
file = new SimpleText (name, exactBuffer);
break;
case FILE_TYPE_AWP:
file = new AppleworksWPFile (name + " (Appleworks Word Processor)", buffer);
break;
case FILE_TYPE_ADB:
file = new AppleworksADBFile (name + " (Appleworks Database File)", buffer);
break;
case FILE_TYPE_ASP:
file = new AppleworksSSFile (name + " (Appleworks Spreadsheet File)", buffer);
break;
case FILE_TYPE_IIGS_SOURCE: // I think this has a resource fork
file = new SimpleText (name, exactBuffer);
break;
case FILE_TYPE_IIGS_APPLICATION:
file = new ObjectModule (name, exactBuffer, auxType);
break;
case FILE_TYPE_IIGS_DEVICE_DRIVER:
file = new DeviceDriver (name, exactBuffer, auxType);
break;
case FILE_TYPE_TIF:
file = new DefaultAppleFile (name, exactBuffer);
break;
case FILE_TYPE_ICN:
file = new IconFile (name, exactBuffer);
break;
case FILE_TYPE_PNT:
if (auxType == 2)
file = new SHRPictureFile1 (name, exactBuffer, fileType, auxType, endOfFile);
else if (endOfFile < 0x222)
file = new DefaultAppleFile (name, exactBuffer);
else
file = new SHRPictureFile2 (name, exactBuffer, fileType, auxType, endOfFile);
break;
case FILE_TYPE_ANI:
file = new SHRPictureFile2 (name, exactBuffer, fileType, auxType, endOfFile);
break;
case FILE_TYPE_PIC:
file = new SHRPictureFile2 (name, exactBuffer, fileType, auxType, endOfFile);
break;
case FILE_TYPE_FOT:
if (auxType == HiResImage.FADDEN_AUX)
file = new FaddenHiResImage (name, exactBuffer, fileType, auxType, endOfFile);
else if (auxType < 0x4000)
{
file = new OriginalHiResImage (name, exactBuffer, 0x2000);
System.out.printf ("FOT %02X%n", exactBuffer[121]);
}
else if (auxType == 0x4000)
{
// packed hi-res
System.out.println ("FOT - packed hi res");
file = new DefaultAppleFile (name, exactBuffer);
}
else if (auxType == 0x4001)
{
// packed double hi-res
System.out.println ("FOT - double hi res");
file = new DefaultAppleFile (name, exactBuffer);
}
else
{
file = new DefaultAppleFile (name, exactBuffer);
// file =
// new OriginalHiResImage (name, exactBuffer, fileType, auxType, endOfFile);
}
break;
case FILE_TYPE_FNT:
file = new FontFile (name, exactBuffer, auxType);
break;
case FILE_TYPE_FONT:
file = new QuickDrawFont (name, exactBuffer, fileType, auxType);
break;
case FILE_TYPE_DESCRIPTOR_TABLE:
file = new FileTypeDescriptorTable (name, exactBuffer);
break;
case FILE_TYPE_GSOS_FILE_SYSTEM_TRANSLATOR:
file = new FileSystemTranslator (name, exactBuffer);
break;
case FILE_TYPE_PASCAL_VOLUME:
file = new PascalArea (name, exactBuffer);
break;
case FILE_TYPE_GEO:
case FILE_TYPE_LDF:
case FILE_TYPE_PAL:
case FILE_TYPE_IIGS_OBJECT:
file = new DefaultAppleFile (name, exactBuffer);
break;
case FILE_TYPE_PCD:
// case FILE_TYPE_PDA:
file = new PascalCode (name, exactBuffer, 0);
break;
case FILE_TYPE_NON:
if (name.endsWith (".TIFF") && HiResImage.isTiff (exactBuffer))
file = new OriginalHiResImage (name, exactBuffer, auxType);
else if (name.endsWith (".JAVA"))
file = new BasicTextFile (name, exactBuffer, auxType, endOfFile);
else
file = new DefaultAppleFile (name, exactBuffer);
break;
case FILE_TYPE_FINDER:
file = new FinderData (name, exactBuffer);
break;
default:
// System.out.format ("%02X %s %s - Unknown Prodos file type%n",
// fileType, fileTypes[fileType], name);
file = new DefaultAppleFile (name, exactBuffer);
}
}
catch (Exception e)
{
file = new ErrorMessageFile (name, buffer, e);
e.printStackTrace ();
}
if (resourceFork != null)
((AbstractFile) file).setResourceFork (resourceFork);
return file;
}
// ---------------------------------------------------------------------------------//
private boolean oneOf (int val, int... values)
// ---------------------------------------------------------------------------------//
{
for (int value : values)
if (val == value)
return true;
return false;
}
// ---------------------------------------------------------------------------------//
private DataSource getRandomAccessTextFile ()
// ---------------------------------------------------------------------------------//
{
// Text files with aux (reclen) > 0 are random access, possibly with
// non-contiguous records, so they need to be handled differently
switch (storageType)
{
case TREE:
return getTreeTextFile ();
case SAPLING:
return getSaplingTextFile ();
case SEEDLING:
return getSeedlingTextFile ();
default:
System.out.println ("Impossible: text file: " + storageType);
return null;
}
}
// ---------------------------------------------------------------------------------//
private DataSource getTreeTextFile ()
// ---------------------------------------------------------------------------------//
{
List<TextBuffer> buffers = new ArrayList<> ();
List<DiskAddress> addresses = new ArrayList<> ();
int logicalBlock = 0;
byte[] mainIndexBuffer = disk.readBlock (keyPtr);
for (int i = 0; i < 256; i++)
{
int indexBlock = Utility.intValue (mainIndexBuffer[i], mainIndexBuffer[i + 256]);
if (indexBlock > 0)
logicalBlock = readIndexBlock (indexBlock, addresses, buffers, logicalBlock);
else
{
if (addresses.size () > 0)
{
byte[] tempBuffer = disk.readBlocks (addresses);
buffers.add (
new TextBuffer (tempBuffer, auxType, logicalBlock - addresses.size ()));
addresses.clear ();
}
logicalBlock += 256;
}
}
if (buffers.size () == 1 && name.endsWith (".S"))
return new MerlinSource (name, buffers.get (0).buffer, auxType, endOfFile);
return new BasicTextFile (name, buffers, auxType, endOfFile);
}
// ---------------------------------------------------------------------------------//
private DataSource getSaplingTextFile ()
// ---------------------------------------------------------------------------------//
{
List<TextBuffer> buffers = new ArrayList<> ();
List<DiskAddress> addresses = new ArrayList<> ();
readIndexBlock (keyPtr, addresses, buffers, 0);
if (buffers.size () == 1 && name.endsWith (".S"))
return new MerlinSource (name, buffers.get (0).buffer, auxType, endOfFile);
return new BasicTextFile (name, buffers, auxType, endOfFile);
}
// ---------------------------------------------------------------------------------//
private DataSource getSeedlingTextFile ()
// ---------------------------------------------------------------------------------//
{
byte[] buffer = getBuffer ();
if (endOfFile < buffer.length)
{
byte[] exactBuffer = new byte[endOfFile];
System.arraycopy (buffer, 0, exactBuffer, 0, endOfFile);
buffer = exactBuffer;
}
if (name.endsWith (".S"))
return new MerlinSource (name, buffer, auxType, endOfFile);
return new BasicTextFile (name, buffer, auxType, endOfFile);
}
// ---------------------------------------------------------------------------------//
private byte[] getBuffer ()
// ---------------------------------------------------------------------------------//
{
switch (storageType)
{
case SEEDLING:
case SAPLING:
case TREE:
return disk.readBlocks (dataBlocks);
case SUBDIRECTORY:
byte[] fullBuffer = new byte[dataBlocks.size () * BLOCK_ENTRY_SIZE];
int offset = 0;
for (DiskAddress da : dataBlocks)
{
byte[] buffer = disk.readBlock (da);
System.arraycopy (buffer, 4, fullBuffer, offset, BLOCK_ENTRY_SIZE);
offset += BLOCK_ENTRY_SIZE;
}
return fullBuffer;
case GSOS_EXTENDED_FILE:
return disk.readBlocks (dataBlocks);
case PASCAL_ON_PROFILE:
return disk.readBlocks (dataBlocks);
default:
System.out.println ("Unknown storage type in getBuffer : " + storageType);
return new byte[512];
}
}
// ---------------------------------------------------------------------------------//
private byte[] getExactBuffer (byte[] buffer)
// ---------------------------------------------------------------------------------//
{
byte[] exactBuffer;
if (buffer.length < endOfFile)
{
exactBuffer = new byte[endOfFile];
System.arraycopy (buffer, 0, exactBuffer, 0, buffer.length);
}
// 512 seems like crap
else if (buffer.length == endOfFile || endOfFile == 512 || endOfFile == 0)
exactBuffer = buffer;
else
{
exactBuffer = new byte[endOfFile];
System.arraycopy (buffer, 0, exactBuffer, 0, endOfFile);
}
return exactBuffer;
}
// ---------------------------------------------------------------------------------//
private int readIndexBlock (int indexBlock, List<DiskAddress> addresses,
List<TextBuffer> buffers, int logicalBlock)
// ---------------------------------------------------------------------------------//
{
byte[] indexBuffer = disk.readBlock (indexBlock);
for (int j = 0; j < 256; j++)
{
int block = Utility.intValue (indexBuffer[j], indexBuffer[j + 256]);
if (block > 0)
addresses.add (disk.getDiskAddress (block));
else if (addresses.size () > 0)
{
byte[] tempBuffer = disk.readBlocks (addresses);
buffers
.add (new TextBuffer (tempBuffer, auxType, logicalBlock - addresses.size ()));
addresses.clear ();
}
logicalBlock++;
}
return logicalBlock;
}
// ---------------------------------------------------------------------------------//
@Override
public List<DiskAddress> getSectors ()
// ---------------------------------------------------------------------------------//
{
List<DiskAddress> sectors = new ArrayList<> ();
sectors.add (catalogBlock);
if (masterIndexBlock != null)
sectors.add (masterIndexBlock);
sectors.addAll (indexBlocks);
sectors.addAll (dataBlocks);
sectors.addAll (resourceBlocks);
return sectors;
}
// ---------------------------------------------------------------------------------//
@Override
public boolean contains (DiskAddress da)
// ---------------------------------------------------------------------------------//
{
if (da == null)
return false;
if (da.equals (masterIndexBlock))
return true;
for (DiskAddress block : indexBlocks)
if (da.matches (block))
return true;
for (DiskAddress block : dataBlocks)
if (da.matches (block))
return true;
for (DiskAddress block : resourceBlocks)
if (da.matches (block))
return true;
return false;
}
// called from ProdosDisk.processDirectoryBlock, used to link DoubleHires image files
// ---------------------------------------------------------------------------------//
void link (FileEntry fileEntry)
// ---------------------------------------------------------------------------------//
{
this.link = fileEntry;
}
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
if (ProdosConstants.fileTypes[fileType].equals ("DIR"))
return name;
String locked = (access == 0x00) ? "*" : " ";
if (true)
return String.format ("%s %03d %s", ProdosConstants.fileTypes[fileType],
blocksUsed, locked) + name;
String timeC = created == null ? "" : created.format (ProdosDisk.df);
String timeF = modified == null ? "" : modified.format (ProdosDisk.df);
return String.format ("%s %s%-30s %3d %,10d %15s %15s",
ProdosConstants.fileTypes[fileType], locked, parentDirectory.name + "/" + name,
blocksUsed, endOfFile, timeC, timeF);
}
}