2015-06-01 09:35:51 +00:00
|
|
|
package com.bytezone.diskbrowser.prodos;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.GregorianCalendar;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import com.bytezone.diskbrowser.applefile.*;
|
|
|
|
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;
|
2016-02-24 21:11:14 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
|
|
|
// - 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 GregorianCalendar modified;
|
2018-04-25 20:41:03 +00:00
|
|
|
// private final int headerPointer;
|
2015-06-01 09:35:51 +00:00
|
|
|
private DataSource file;
|
|
|
|
private final DiskAddress catalogBlock;
|
2018-08-18 07:56:54 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
private DiskAddress masterIndexBlock;
|
|
|
|
private final List<DiskAddress> indexBlocks = new ArrayList<DiskAddress> ();
|
2018-08-18 07:56:54 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
private boolean invalid;
|
2016-12-31 09:34:15 +00:00
|
|
|
private FileEntry link;
|
2018-07-22 20:52:41 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
public FileEntry (ProdosDisk fDisk, byte[] entryBuffer, DirectoryHeader parent,
|
2016-02-05 00:23:53 +00:00
|
|
|
int parentBlock)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
|
|
|
super (fDisk, entryBuffer);
|
2018-07-22 20:52:41 +00:00
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
assert parent != null;
|
2015-06-01 09:35:51 +00:00
|
|
|
this.parentDirectory = parent;
|
|
|
|
this.catalogBlock = this.disk.getDiskAddress (parentBlock);
|
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
fileType = entryBuffer[0x10] & 0xFF;
|
|
|
|
keyPtr = HexFormatter.unsignedShort (entryBuffer, 0x11);
|
|
|
|
blocksUsed = HexFormatter.unsignedShort (entryBuffer, 0x13);
|
2015-06-01 09:35:51 +00:00
|
|
|
endOfFile = HexFormatter.intValue (entryBuffer[21], entryBuffer[22], entryBuffer[23]);
|
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
auxType = HexFormatter.unsignedShort (entryBuffer, 0x1F);
|
|
|
|
modified = HexFormatter.getAppleDate (entryBuffer, 0x21);
|
2018-04-25 20:41:03 +00:00
|
|
|
// headerPointer = HexFormatter.unsignedShort (entryBuffer, 0x25);
|
2017-05-08 01:46:28 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
switch (storageType)
|
|
|
|
{
|
2017-05-12 10:42:20 +00:00
|
|
|
case SEEDLING:
|
|
|
|
case SAPLING:
|
|
|
|
case TREE:
|
2018-07-22 20:52:41 +00:00
|
|
|
addDataBlocks (storageType, keyPtr);
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case GSOS_EXTENDED_FILE:
|
2017-06-12 09:09:19 +00:00
|
|
|
readForks ();
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case SUBDIRECTORY:
|
2015-06-01 09:35:51 +00:00
|
|
|
int block = keyPtr;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
dataBlocks.add (disk.getDiskAddress (block));
|
|
|
|
byte[] buffer = disk.readSector (block);
|
2017-05-12 10:42:20 +00:00
|
|
|
block = HexFormatter.unsignedShort (buffer, 2);
|
2015-06-01 09:35:51 +00:00
|
|
|
} while (block > 0);
|
|
|
|
break;
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case PASCAL_ON_PROFILE:
|
2017-04-22 06:31:25 +00:00
|
|
|
indexBlocks.add (disk.getDiskAddress (keyPtr));
|
2017-06-12 09:09:19 +00:00
|
|
|
System.out.println ("PASCAL on PROFILE: " + name);
|
|
|
|
// are these blocks guaranteed to be contiguous?
|
2017-04-22 06:31:25 +00:00
|
|
|
break;
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
default:
|
|
|
|
System.out.println ("Unknown storage type: " + storageType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-12 09:09:19 +00:00
|
|
|
private void readForks ()
|
|
|
|
{
|
|
|
|
parentDisk.setSectorType (keyPtr, parentDisk.extendedKeySector);
|
|
|
|
indexBlocks.add (disk.getDiskAddress (keyPtr));
|
|
|
|
|
|
|
|
byte[] buffer2 = disk.readSector (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 = HexFormatter.unsignedShort (buffer2, i + 1);
|
2018-04-25 20:41:03 +00:00
|
|
|
// int eof = HexFormatter.intValue (buffer2[i + 3], buffer2[i + 4], buffer2[i + 5]);
|
2017-06-12 09:09:19 +00:00
|
|
|
addDataBlocks (storageType, keyBlock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
private void addDataBlocks (int storageType, int keyPtr)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2017-05-08 01:46:28 +00:00
|
|
|
DiskAddress emptyDiskAddress = disk.getDiskAddress (0);
|
|
|
|
List<Integer> blocks = new ArrayList<Integer> ();
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
switch (storageType)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2017-05-12 10:42:20 +00:00
|
|
|
case SEEDLING:
|
2018-07-25 05:48:14 +00:00
|
|
|
if (isValid (keyPtr))
|
2018-07-23 00:36:59 +00:00
|
|
|
blocks.add (keyPtr);
|
2017-05-08 01:46:28 +00:00
|
|
|
break;
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case SAPLING:
|
2018-07-25 05:48:14 +00:00
|
|
|
if (isValid (keyPtr))
|
2018-07-23 00:36:59 +00:00
|
|
|
blocks.addAll (readIndex (keyPtr));
|
2017-05-08 01:46:28 +00:00
|
|
|
break;
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case TREE:
|
2018-07-25 05:48:14 +00:00
|
|
|
if (isValid (keyPtr))
|
2018-07-23 00:36:59 +00:00
|
|
|
for (Integer indexBlock : readMasterIndex (keyPtr))
|
2018-07-25 05:48:14 +00:00
|
|
|
if (isValid (indexBlock))
|
2018-07-23 00:36:59 +00:00
|
|
|
blocks.addAll (readIndex (indexBlock));
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-03-14 04:50:23 +00:00
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
// remove trailing empty blocks
|
|
|
|
while (blocks.size () > 0 && blocks.get (blocks.size () - 1) == 0)
|
|
|
|
blocks.remove (blocks.size () - 1);
|
|
|
|
|
|
|
|
for (Integer block : blocks)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2017-05-08 01:46:28 +00:00
|
|
|
if (block == 0)
|
|
|
|
dataBlocks.add (emptyDiskAddress);
|
2015-06-01 09:35:51 +00:00
|
|
|
else
|
|
|
|
{
|
2017-05-08 01:46:28 +00:00
|
|
|
parentDisk.setSectorType (block, parentDisk.dataSector);
|
2017-05-12 10:42:20 +00:00
|
|
|
dataBlocks.add (disk.getDiskAddress (block));
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
private List<Integer> readIndex (int blockPtr)
|
2017-03-14 04:50:23 +00:00
|
|
|
{
|
2017-05-08 01:46:28 +00:00
|
|
|
List<Integer> blocks = new ArrayList<Integer> (256);
|
2017-03-14 04:50:23 +00:00
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
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));
|
2017-05-12 10:42:20 +00:00
|
|
|
|
|
|
|
byte[] buffer = disk.readSector (blockPtr);
|
|
|
|
for (int i = 0; i < 256; i++)
|
2018-07-22 20:52:41 +00:00
|
|
|
{
|
|
|
|
int blockNo = (buffer[i] & 0xFF) | ((buffer[i + 0x100] & 0xFF) << 8);
|
2018-07-25 05:48:14 +00:00
|
|
|
blocks.add (isValid (blockNo) ? blockNo : 0);
|
2018-07-22 20:52:41 +00:00
|
|
|
}
|
2017-03-14 04:50:23 +00:00
|
|
|
}
|
2017-05-08 01:46:28 +00:00
|
|
|
|
|
|
|
return blocks;
|
2017-03-14 04:50:23 +00:00
|
|
|
}
|
|
|
|
|
2018-08-18 07:56:54 +00:00
|
|
|
private List<Integer> readMasterIndex (int keyPtr)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2018-08-18 07:56:54 +00:00
|
|
|
masterIndexBlock = disk.getDiskAddress (keyPtr);
|
|
|
|
parentDisk.setSectorType (keyPtr, parentDisk.masterIndexSector);
|
|
|
|
indexBlocks.add (disk.getDiskAddress (keyPtr));
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2018-08-18 07:56:54 +00:00
|
|
|
byte[] buffer = disk.readSector (keyPtr); // master index
|
2017-05-12 10:42:20 +00:00
|
|
|
|
|
|
|
int highest = 0x80;
|
2018-08-18 07:56:54 +00:00
|
|
|
while (highest-- > 0) // decrement after test
|
2017-05-12 10:42:20 +00:00
|
|
|
if (buffer[highest] != 0 || buffer[highest + 0x100] != 0)
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
List<Integer> blocks = new ArrayList<Integer> (highest + 1);
|
|
|
|
for (int i = 0; i <= highest; i++)
|
2018-07-22 20:52:41 +00:00
|
|
|
{
|
|
|
|
int blockNo = (buffer[i] & 0xFF) | ((buffer[i + 256] & 0xFF) << 8);
|
2018-07-25 05:48:14 +00:00
|
|
|
blocks.add (isValid (blockNo) ? blockNo : 0);
|
2018-07-22 20:52:41 +00:00
|
|
|
}
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
return blocks;
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
2018-07-25 05:48:14 +00:00
|
|
|
private boolean isValid (int blockNo)
|
|
|
|
{
|
2018-08-02 22:13:49 +00:00
|
|
|
if (false)
|
2018-07-25 05:48:14 +00:00
|
|
|
{
|
|
|
|
if (!disk.isValidAddress (blockNo))
|
2018-08-02 22:13:49 +00:00
|
|
|
System.out.println ("--Invalid Block Address: " + blockNo);
|
2018-07-25 05:48:14 +00:00
|
|
|
if (parentDisk.isSectorFree (blockNo))
|
|
|
|
System.out.println ("--Free block: " + blockNo);
|
|
|
|
}
|
|
|
|
return disk.isValidAddress (blockNo) && !parentDisk.isSectorFree (blockNo);
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
@Override
|
|
|
|
public DataSource getDataSource ()
|
|
|
|
{
|
|
|
|
if (file != null)
|
|
|
|
return file;
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
if (invalid)
|
|
|
|
{
|
|
|
|
file = new DefaultAppleFile (name, null);
|
|
|
|
return file;
|
|
|
|
}
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
if (fileType == FILE_TYPE_TEXT && auxType > 0) // random access file
|
|
|
|
return getRandomAccessTextFile ();
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2018-07-22 20:52:41 +00:00
|
|
|
byte[] buffer = getBuffer ();
|
2015-06-01 09:35:51 +00:00
|
|
|
byte[] exactBuffer = getExactBuffer (buffer);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
switch (fileType)
|
|
|
|
{
|
2019-08-03 07:32:36 +00:00
|
|
|
case FILE_TYPE_USER_DEFINED_1: // OVL
|
|
|
|
if (endOfFile == 0x2000 && auxType == 0)
|
|
|
|
{
|
|
|
|
file = new OriginalHiResImage (name, exactBuffer, auxType);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// drop through
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_BINARY:
|
|
|
|
case FILE_TYPE_RELOCATABLE:
|
|
|
|
case FILE_TYPE_SYS:
|
2018-04-25 23:07:23 +00:00
|
|
|
case FILE_TYPE_BAT:
|
2018-07-25 05:48:14 +00:00
|
|
|
if (SimpleText.isHTML (exactBuffer))
|
2015-06-01 09:35:51 +00:00
|
|
|
file = new SimpleText (name, exactBuffer);
|
2017-01-25 05:26:37 +00:00
|
|
|
else if (HiResImage.isGif (exactBuffer) || HiResImage.isPng (exactBuffer))
|
|
|
|
file = new OriginalHiResImage (name, exactBuffer, auxType);
|
|
|
|
else if (name.endsWith (".BMP") && HiResImage.isBmp (exactBuffer))
|
2016-12-31 09:34:15 +00:00
|
|
|
file = new OriginalHiResImage (name, exactBuffer, auxType);
|
2018-07-28 12:00:02 +00:00
|
|
|
else if (name.endsWith (".3200")) // $C1/02
|
|
|
|
file = new SHRPictureFile2 (name, exactBuffer, 0xC1, 0x02, endOfFile);
|
2018-08-02 22:13:49 +00:00
|
|
|
else if (name.endsWith (".3201") || HiResImage.isAPP (exactBuffer)) // $C0/04
|
|
|
|
// I made up aux=99 to test it without stepping on aux==04
|
2018-08-01 07:23:12 +00:00
|
|
|
file = new SHRPictureFile2 (name, exactBuffer, 0xC0, 99, endOfFile);
|
2017-07-28 05:41:38 +00:00
|
|
|
else if (name.endsWith (".FNT") && FontFile.isFont (exactBuffer))
|
|
|
|
file = new FontFile (name, exactBuffer);
|
2018-07-25 05:48:14 +00:00
|
|
|
else if (ShapeTable.isShapeTable (exactBuffer))
|
|
|
|
file = new ShapeTable (name, exactBuffer);
|
2016-12-31 09:34:15 +00:00
|
|
|
else if (link != null)
|
|
|
|
{
|
|
|
|
if (name.endsWith (".AUX"))
|
|
|
|
file = new DoubleHiResImage (name, link.getBuffer (), exactBuffer);
|
|
|
|
else
|
|
|
|
file = new DoubleHiResImage (name, exactBuffer, link.getBuffer ());
|
|
|
|
}
|
2018-07-23 04:44:16 +00:00
|
|
|
else if (name.endsWith (".PAC") || name.endsWith (".A2FC")
|
|
|
|
|| (endOfFile == 0x4000 && auxType == 0x2000))
|
2017-01-08 23:36:10 +00:00
|
|
|
file = new DoubleHiResImage (name, exactBuffer);
|
2019-08-01 21:51:58 +00:00
|
|
|
else if (endOfFile == 0x4000 && auxType == 0x4000)
|
|
|
|
file = new DoubleHiResImage (name, exactBuffer);
|
2018-07-25 05:48:14 +00:00
|
|
|
else if (oneOf (endOfFile, 0x1FF8, 0x1FFF, 0x2000, 0x4000)
|
|
|
|
&& oneOf (auxType, 0x1FFF, 0x2000, 0x4000))
|
2016-12-31 09:34:15 +00:00
|
|
|
file = new OriginalHiResImage (name, exactBuffer, auxType);
|
2015-06-01 09:35:51 +00:00
|
|
|
else if (endOfFile == 38400 && name.startsWith ("LVL."))
|
|
|
|
file = new LodeRunner (name, exactBuffer);
|
2018-08-18 03:24:22 +00:00
|
|
|
else if (auxType == 0x1000 && endOfFile == 0x400
|
|
|
|
&& CharacterRom.isRom (exactBuffer))
|
|
|
|
file = new CharacterRom (name, exactBuffer);
|
2015-06-01 09:35:51 +00:00
|
|
|
else
|
2016-02-05 00:23:53 +00:00
|
|
|
{
|
2015-06-01 09:35:51 +00:00
|
|
|
file = new AssemblerProgram (name, exactBuffer, auxType);
|
2016-02-05 00:23:53 +00:00
|
|
|
if (exactBuffer.length < buffer.length)
|
2016-12-12 07:43:19 +00:00
|
|
|
((AssemblerProgram) file).setExtraBuffer (buffer, exactBuffer.length,
|
|
|
|
buffer.length - exactBuffer.length);
|
2016-02-05 00:23:53 +00:00
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_TEXT:
|
2016-12-31 09:34:15 +00:00
|
|
|
assert auxType == 0; // auxType > 0 handled above
|
2015-06-01 09:35:51 +00:00
|
|
|
if (name.endsWith (".S"))
|
|
|
|
file = new MerlinSource (name, exactBuffer, auxType, endOfFile);
|
2018-07-31 07:02:19 +00:00
|
|
|
else if (name.endsWith (".GIF") && HiResImage.isGif (exactBuffer))
|
|
|
|
file = new OriginalHiResImage (name, exactBuffer, auxType);
|
2015-06-01 09:35:51 +00:00
|
|
|
else
|
|
|
|
file = new TextFile (name, exactBuffer, auxType, endOfFile);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_APPLESOFT_BASIC:
|
2019-08-09 23:45:49 +00:00
|
|
|
file = new ApplesoftBasicProgram (name, exactBuffer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FILE_TYPE_GS_BASIC:
|
|
|
|
file = new BasicProgramGS (name, exactBuffer);
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_INTEGER_BASIC:
|
|
|
|
file = new IntegerBasicProgram (name, exactBuffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_DIRECTORY:
|
2016-03-24 00:17:09 +00:00
|
|
|
VolumeDirectoryHeader vdh = parentDisk.vdh;
|
2016-02-05 00:23:53 +00:00
|
|
|
file = new ProdosDirectory (parentDisk, name, buffer, vdh.totalBlocks,
|
|
|
|
vdh.freeBlocks, vdh.usedBlocks);
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
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;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_APPLETALK:
|
|
|
|
file = new DefaultAppleFile (name + " (Appletalk file)", buffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2017-01-17 00:00:51 +00:00
|
|
|
case FILE_TYPE_GWP:
|
2017-01-24 21:59:50 +00:00
|
|
|
file = new SimpleText (name, exactBuffer);
|
2017-01-17 00:00:51 +00:00
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_AWP:
|
|
|
|
file = new AppleworksWPFile (name + " (Appleworks Word Processor)", buffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_ADB:
|
|
|
|
file = new AppleworksADBFile (name + " (Appleworks Database File)", buffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_ASP:
|
|
|
|
file = new AppleworksSSFile (name + " (Appleworks Spreadsheet File)", buffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
case FILE_TYPE_IIGS_SOURCE: // I think this has a resource fork
|
2015-06-01 09:35:51 +00:00
|
|
|
file = new SimpleText (name, exactBuffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
case FILE_TYPE_IIGS_APPLICATION:
|
|
|
|
file = new AssemblerProgram (name, buffer, auxType);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2017-01-22 06:55:51 +00:00
|
|
|
case FILE_TYPE_IIGS_DEVICE_DRIVER:
|
|
|
|
file = new DeviceDriver (name, exactBuffer, auxType);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_ICN:
|
|
|
|
file = new IconFile (name, exactBuffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_PNT:
|
2017-01-26 11:30:16 +00:00
|
|
|
if (auxType == 2)
|
2018-07-25 05:48:14 +00:00
|
|
|
file = new SHRPictureFile1 (name, exactBuffer, fileType, auxType, endOfFile);
|
2017-01-24 08:59:40 +00:00
|
|
|
else
|
2017-01-26 11:30:16 +00:00
|
|
|
file = new SHRPictureFile2 (name, exactBuffer, fileType, auxType, endOfFile);
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
case FILE_TYPE_PIC:
|
2017-01-26 04:19:23 +00:00
|
|
|
file = new SHRPictureFile2 (name, exactBuffer, fileType, auxType, endOfFile);
|
2015-06-01 09:35:51 +00:00
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2018-08-15 02:32:11 +00:00
|
|
|
case FILE_TYPE_FOT:
|
|
|
|
if (auxType == 0x8066) // Fadden
|
|
|
|
file = new FaddenHiResImage (name, exactBuffer, fileType, auxType, endOfFile);
|
|
|
|
else
|
2018-08-17 11:46:47 +00:00
|
|
|
{
|
|
|
|
System.out.println ("Unwritten FOT: " + name);
|
2018-08-15 02:32:11 +00:00
|
|
|
file = new DefaultAppleFile (name, exactBuffer);
|
2018-08-17 11:46:47 +00:00
|
|
|
}
|
2018-07-25 05:48:14 +00:00
|
|
|
break;
|
|
|
|
|
2019-06-02 10:46:51 +00:00
|
|
|
case FILE_TYPE_FNT:
|
|
|
|
file = new FontFile (name, exactBuffer);
|
|
|
|
break;
|
|
|
|
|
2017-01-17 00:00:51 +00:00
|
|
|
case FILE_TYPE_FONT:
|
|
|
|
file = new QuickDrawFont (name, exactBuffer, fileType, auxType);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2017-01-22 00:01:15 +00:00
|
|
|
case FILE_TYPE_DESCRIPTOR_TABLE:
|
|
|
|
file = new FileTypeDescriptorTable (name, exactBuffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2017-01-22 00:01:15 +00:00
|
|
|
case FILE_TYPE_GSOS_FILE_SYSTEM_TRANSLATOR:
|
|
|
|
file = new FileSystemTranslator (name, exactBuffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2017-05-08 01:46:28 +00:00
|
|
|
case FILE_TYPE_FINDER:
|
2018-07-22 20:52:41 +00:00
|
|
|
case FILE_TYPE_PASCAL_VOLUME:
|
|
|
|
case FILE_TYPE_GEO:
|
2018-07-25 05:48:14 +00:00
|
|
|
case FILE_TYPE_LDF:
|
2018-07-28 12:00:02 +00:00
|
|
|
case FILE_TYPE_ANI:
|
|
|
|
case FILE_TYPE_PAL:
|
2017-05-08 01:46:28 +00:00
|
|
|
file = new DefaultAppleFile (name, exactBuffer);
|
|
|
|
break;
|
2017-07-28 05:41:38 +00:00
|
|
|
|
2019-08-09 23:45:49 +00:00
|
|
|
case FILE_TYPE_NON:
|
|
|
|
if (name.endsWith (".TIFF") && HiResImage.isTiff (exactBuffer))
|
|
|
|
file = new OriginalHiResImage (name, exactBuffer, auxType);
|
|
|
|
else
|
|
|
|
file = new DefaultAppleFile (name, exactBuffer);
|
|
|
|
break;
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
default:
|
2017-04-22 06:31:25 +00:00
|
|
|
System.out.format ("%s - Unknown Prodos file type : %02X%n", name, fileType);
|
2017-01-20 04:07:08 +00:00
|
|
|
file = new DefaultAppleFile (name, exactBuffer);
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
file = new ErrorMessageFile (name, buffer, e);
|
|
|
|
e.printStackTrace ();
|
|
|
|
}
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2018-07-25 05:48:14 +00:00
|
|
|
private boolean oneOf (int val, int... values)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2018-07-25 05:48:14 +00:00
|
|
|
for (int value : values)
|
|
|
|
if (val == value)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-24 08:59:40 +00:00
|
|
|
|
2018-07-25 05:48:14 +00:00
|
|
|
private byte[] getExactBuffer (byte[] buffer)
|
|
|
|
{
|
2015-06-01 09:35:51 +00:00
|
|
|
byte[] exactBuffer;
|
|
|
|
if (buffer.length < endOfFile)
|
|
|
|
{
|
2017-01-25 11:02:50 +00:00
|
|
|
exactBuffer = new byte[endOfFile];
|
2015-06-01 09:35:51 +00:00
|
|
|
System.arraycopy (buffer, 0, exactBuffer, 0, buffer.length);
|
|
|
|
}
|
2017-01-20 05:20:00 +00:00
|
|
|
else if (buffer.length == endOfFile || endOfFile == 512) // 512 seems like crap
|
2015-06-01 09:35:51 +00:00
|
|
|
exactBuffer = buffer;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
exactBuffer = new byte[endOfFile];
|
|
|
|
System.arraycopy (buffer, 0, exactBuffer, 0, endOfFile);
|
|
|
|
}
|
|
|
|
return exactBuffer;
|
|
|
|
}
|
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
private DataSource getRandomAccessTextFile ()
|
|
|
|
{
|
2019-06-02 10:46:51 +00:00
|
|
|
// Text files with aux (reclen) > 0 are random access, possibly with
|
2017-02-01 21:15:30 +00:00
|
|
|
// non-contiguous records, so they need to be handled differently
|
|
|
|
|
|
|
|
switch (storageType)
|
|
|
|
{
|
2017-05-12 10:42:20 +00:00
|
|
|
case TREE:
|
2017-02-01 21:15:30 +00:00
|
|
|
return getTreeTextFile ();
|
2017-05-12 10:42:20 +00:00
|
|
|
case SAPLING:
|
2017-02-01 21:15:30 +00:00
|
|
|
return getSaplingTextFile ();
|
2017-05-12 10:42:20 +00:00
|
|
|
case SEEDLING:
|
2017-02-01 21:15:30 +00:00
|
|
|
return getSeedlingTextFile ();
|
|
|
|
default:
|
|
|
|
System.out.println ("Impossible: text file: " + storageType);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
private DataSource getTreeTextFile ()
|
|
|
|
{
|
|
|
|
List<TextBuffer> buffers = new ArrayList<TextBuffer> ();
|
|
|
|
List<DiskAddress> addresses = new ArrayList<DiskAddress> ();
|
|
|
|
int logicalBlock = 0;
|
|
|
|
|
|
|
|
byte[] mainIndexBuffer = disk.readSector (keyPtr);
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
|
|
{
|
2016-02-05 00:23:53 +00:00
|
|
|
int indexBlock =
|
|
|
|
HexFormatter.intValue (mainIndexBuffer[i], mainIndexBuffer[i + 256]);
|
2015-06-01 09:35:51 +00:00
|
|
|
if (indexBlock > 0)
|
|
|
|
logicalBlock = readIndexBlock (indexBlock, addresses, buffers, logicalBlock);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (addresses.size () > 0)
|
|
|
|
{
|
|
|
|
byte[] tempBuffer = disk.readSectors (addresses);
|
2016-12-12 07:43:19 +00:00
|
|
|
buffers.add (
|
|
|
|
new TextBuffer (tempBuffer, auxType, logicalBlock - addresses.size ()));
|
2015-06-01 09:35:51 +00:00
|
|
|
addresses.clear ();
|
|
|
|
}
|
|
|
|
logicalBlock += 256;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (buffers.size () == 1 && name.endsWith (".S"))
|
|
|
|
return new MerlinSource (name, buffers.get (0).buffer, auxType, endOfFile);
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
return new TextFile (name, buffers, auxType, endOfFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataSource getSaplingTextFile ()
|
|
|
|
{
|
|
|
|
List<TextBuffer> buffers = new ArrayList<TextBuffer> ();
|
|
|
|
List<DiskAddress> addresses = new ArrayList<DiskAddress> ();
|
|
|
|
readIndexBlock (keyPtr, addresses, buffers, 0);
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
if (buffers.size () == 1 && name.endsWith (".S"))
|
|
|
|
return new MerlinSource (name, buffers.get (0).buffer, auxType, endOfFile);
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
return new TextFile (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;
|
|
|
|
}
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
if (name.endsWith (".S"))
|
|
|
|
return new MerlinSource (name, buffer, auxType, endOfFile);
|
2017-01-26 23:59:47 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
return new TextFile (name, buffer, auxType, endOfFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
private byte[] getBuffer ()
|
|
|
|
{
|
|
|
|
switch (storageType)
|
|
|
|
{
|
2017-05-12 10:42:20 +00:00
|
|
|
case SEEDLING:
|
|
|
|
case SAPLING:
|
|
|
|
case TREE:
|
2015-06-01 09:35:51 +00:00
|
|
|
return disk.readSectors (dataBlocks);
|
2017-02-01 21:15:30 +00:00
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case SUBDIRECTORY:
|
2017-02-01 21:15:30 +00:00
|
|
|
byte[] fullBuffer = new byte[dataBlocks.size () * BLOCK_ENTRY_SIZE];
|
2015-06-01 09:35:51 +00:00
|
|
|
int offset = 0;
|
|
|
|
for (DiskAddress da : dataBlocks)
|
|
|
|
{
|
|
|
|
byte[] buffer = disk.readSector (da);
|
|
|
|
System.arraycopy (buffer, 4, fullBuffer, offset, BLOCK_ENTRY_SIZE);
|
|
|
|
offset += BLOCK_ENTRY_SIZE;
|
|
|
|
}
|
|
|
|
return fullBuffer;
|
2017-02-01 21:15:30 +00:00
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case GSOS_EXTENDED_FILE:
|
2017-02-01 21:15:30 +00:00
|
|
|
return disk.readSectors (dataBlocks); // data and resource forks concatenated
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
case PASCAL_ON_PROFILE:
|
2017-04-22 06:31:25 +00:00
|
|
|
return disk.readSectors (dataBlocks);
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
default:
|
|
|
|
System.out.println ("Unknown storage type in getBuffer : " + storageType);
|
|
|
|
return new byte[512];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private int readIndexBlock (int indexBlock, List<DiskAddress> addresses,
|
2016-02-05 00:23:53 +00:00
|
|
|
List<TextBuffer> buffers, int logicalBlock)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
|
|
|
byte[] indexBuffer = disk.readSector (indexBlock);
|
|
|
|
for (int j = 0; j < 256; j++)
|
|
|
|
{
|
|
|
|
int block = HexFormatter.intValue (indexBuffer[j], indexBuffer[j + 256]);
|
|
|
|
if (block > 0)
|
|
|
|
addresses.add (disk.getDiskAddress (block));
|
|
|
|
else if (addresses.size () > 0)
|
|
|
|
{
|
|
|
|
byte[] tempBuffer = disk.readSectors (addresses);
|
2016-02-05 00:23:53 +00:00
|
|
|
buffers
|
|
|
|
.add (new TextBuffer (tempBuffer, auxType, logicalBlock - addresses.size ()));
|
2015-06-01 09:35:51 +00:00
|
|
|
addresses.clear ();
|
|
|
|
}
|
|
|
|
logicalBlock++;
|
|
|
|
}
|
|
|
|
return logicalBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<DiskAddress> getSectors ()
|
|
|
|
{
|
|
|
|
List<DiskAddress> sectors = new ArrayList<DiskAddress> ();
|
|
|
|
sectors.add (catalogBlock);
|
|
|
|
if (masterIndexBlock != null)
|
|
|
|
sectors.add (masterIndexBlock);
|
|
|
|
sectors.addAll (indexBlocks);
|
|
|
|
sectors.addAll (dataBlocks);
|
|
|
|
return sectors;
|
|
|
|
}
|
|
|
|
|
2016-02-28 07:17:58 +00:00
|
|
|
@Override
|
2015-06-01 09:35:51 +00:00
|
|
|
public boolean contains (DiskAddress da)
|
|
|
|
{
|
2017-01-27 07:11:00 +00:00
|
|
|
if (da == null)
|
|
|
|
return false;
|
2015-06-01 09:35:51 +00:00
|
|
|
if (da.equals (masterIndexBlock))
|
|
|
|
return true;
|
|
|
|
for (DiskAddress block : indexBlocks)
|
2017-01-27 07:11:00 +00:00
|
|
|
if (da.matches (block))
|
2015-06-01 09:35:51 +00:00
|
|
|
return true;
|
|
|
|
for (DiskAddress block : dataBlocks)
|
2017-01-27 07:11:00 +00:00
|
|
|
if (da.matches (block))
|
2015-06-01 09:35:51 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-25 05:48:14 +00:00
|
|
|
// called from ProdosDisk.processDirectoryBlock
|
2016-12-31 09:34:15 +00:00
|
|
|
void link (FileEntry fileEntry)
|
|
|
|
{
|
|
|
|
this.link = fileEntry;
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
@Override
|
|
|
|
public String toString ()
|
|
|
|
{
|
|
|
|
if (ProdosConstants.fileTypes[fileType].equals ("DIR"))
|
|
|
|
return name;
|
|
|
|
String locked = (access == 0x00) ? "*" : " ";
|
2018-08-15 02:32:11 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
if (true)
|
2016-02-05 00:23:53 +00:00
|
|
|
return String.format ("%s %03d %s", ProdosConstants.fileTypes[fileType],
|
2016-12-12 07:43:19 +00:00
|
|
|
blocksUsed, locked) + name;
|
2018-08-15 02:32:11 +00:00
|
|
|
|
2016-03-24 00:17:09 +00:00
|
|
|
String timeC = created == null ? "" : parentDisk.df.format (created.getTime ());
|
|
|
|
String timeF = modified == null ? "" : parentDisk.df.format (modified.getTime ());
|
2015-06-01 09:35:51 +00:00
|
|
|
return String.format ("%s %s%-30s %3d %,10d %15s %15s",
|
2016-12-12 07:43:19 +00:00
|
|
|
ProdosConstants.fileTypes[fileType], locked, parentDirectory.name + "/" + name,
|
|
|
|
blocksUsed, endOfFile, timeC, timeF);
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
}
|