This commit is contained in:
Denis Molony 2021-04-16 13:50:28 +10:00
parent 645c6a4a36
commit b0b00b4029
6 changed files with 150 additions and 259 deletions

View File

@ -1,6 +1,10 @@
package com.bytezone.diskbrowser.prodos.write;
import static com.bytezone.diskbrowser.prodos.write.ProdosDisk.ENTRY_SIZE;
import static com.bytezone.diskbrowser.utilities.Utility.getAppleDate;
import static com.bytezone.diskbrowser.utilities.Utility.putAppleDate;
import static com.bytezone.diskbrowser.utilities.Utility.readShort;
import static com.bytezone.diskbrowser.utilities.Utility.writeShort;
import java.time.LocalDateTime;
@ -40,15 +44,13 @@ public class DirectoryHeader
storageType = (byte) ((buffer[ptr] & 0xF0) >>> 4);
int nameLength = buffer[ptr] & 0x0F;
fileName = new String (buffer, ptr + 1, nameLength);
creationDate = ProdosDisk.getAppleDate (buffer, ptr + 0x18);
creationDate = getAppleDate (buffer, ptr + 0x18);
version = buffer[ptr + 0x1C];
minVersion = buffer[ptr + 0x1D];
access = buffer[ptr + 0x1E];
entryLength = buffer[ptr + 0x1F];
entriesPerBlock = buffer[ptr + 0x20];
fileCount = ProdosDisk.readShort (buffer, ptr + 0x21);
// System.out.println (this);
fileCount = readShort (buffer, ptr + 0x21);
}
// ---------------------------------------------------------------------------------//
@ -58,15 +60,13 @@ public class DirectoryHeader
buffer[ptr] = (byte) ((storageType << 4) | fileName.length ());
System.arraycopy (fileName.getBytes (), 0, buffer, ptr + 1, fileName.length ());
ProdosDisk.putAppleDate (buffer, ptr + 0x18, creationDate);
putAppleDate (buffer, ptr + 0x18, creationDate);
buffer[ptr + 0x1C] = version;
buffer[ptr + 0x1D] = minVersion;
buffer[ptr + 0x1E] = access;
buffer[ptr + 0x1F] = entryLength;
buffer[ptr + 0x20] = entriesPerBlock;
ProdosDisk.writeShort (buffer, ptr + 0x21, fileCount);
// System.out.println (this);
writeShort (buffer, ptr + 0x21, fileCount);
}
// ---------------------------------------------------------------------------------//

View File

@ -2,6 +2,12 @@ package com.bytezone.diskbrowser.prodos.write;
import static com.bytezone.diskbrowser.prodos.write.ProdosDisk.BLOCK_SIZE;
import static com.bytezone.diskbrowser.prodos.write.ProdosDisk.UNDERLINE;
import static com.bytezone.diskbrowser.utilities.Utility.getAppleDate;
import static com.bytezone.diskbrowser.utilities.Utility.putAppleDate;
import static com.bytezone.diskbrowser.utilities.Utility.readShort;
import static com.bytezone.diskbrowser.utilities.Utility.readTriple;
import static com.bytezone.diskbrowser.utilities.Utility.writeShort;
import static com.bytezone.diskbrowser.utilities.Utility.writeTriple;
import java.time.LocalDateTime;
@ -55,18 +61,18 @@ public class FileEntry
fileName = "";
fileType = buffer[ptr + 0x10];
keyPointer = ProdosDisk.readShort (buffer, ptr + 0x11);
blocksUsed = ProdosDisk.readShort (buffer, ptr + 0x13);
eof = ProdosDisk.readTriple (buffer, ptr + 0x15);
creationDate = ProdosDisk.getAppleDate (buffer, ptr + 0x18);
keyPointer = readShort (buffer, ptr + 0x11);
blocksUsed = readShort (buffer, ptr + 0x13);
eof = readTriple (buffer, ptr + 0x15);
creationDate = getAppleDate (buffer, ptr + 0x18);
version = buffer[ptr + 0x1C];
minVersion = buffer[ptr + 0x1D];
access = buffer[ptr + 0x1E];
auxType = ProdosDisk.readShort (buffer, ptr + 0x1F);
modifiedDate = ProdosDisk.getAppleDate (buffer, ptr + 0x21);
headerPointer = ProdosDisk.readShort (buffer, ptr + 0x25);
auxType = readShort (buffer, ptr + 0x1F);
modifiedDate = getAppleDate (buffer, ptr + 0x21);
headerPointer = readShort (buffer, ptr + 0x25);
}
// ---------------------------------------------------------------------------------//
@ -77,18 +83,18 @@ public class FileEntry
System.arraycopy (fileName.getBytes (), 0, buffer, ptr + 1, fileName.length ());
buffer[ptr + 0x10] = fileType;
ProdosDisk.writeShort (buffer, ptr + 0x11, keyPointer);
ProdosDisk.writeShort (buffer, ptr + 0x13, blocksUsed);
ProdosDisk.writeTriple (buffer, ptr + 0x15, eof);
ProdosDisk.putAppleDate (buffer, ptr + 0x18, creationDate);
writeShort (buffer, ptr + 0x11, keyPointer);
writeShort (buffer, ptr + 0x13, blocksUsed);
writeTriple (buffer, ptr + 0x15, eof);
putAppleDate (buffer, ptr + 0x18, creationDate);
buffer[ptr + 0x1C] = version;
buffer[ptr + 0x1D] = minVersion;
buffer[ptr + 0x1E] = access;
ProdosDisk.writeShort (buffer, ptr + 0x1F, auxType);
ProdosDisk.putAppleDate (buffer, ptr + 0x21, modifiedDate);
ProdosDisk.writeShort (buffer, ptr + 0x25, headerPointer);
writeShort (buffer, ptr + 0x1F, auxType);
putAppleDate (buffer, ptr + 0x21, modifiedDate);
writeShort (buffer, ptr + 0x25, headerPointer);
}
// ---------------------------------------------------------------------------------//

View File

@ -1,10 +1,10 @@
package com.bytezone.diskbrowser.prodos.write;
import static com.bytezone.diskbrowser.utilities.Utility.readShort;
import static com.bytezone.diskbrowser.utilities.Utility.writeShort;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.BitSet;
import java.util.HashMap;
@ -24,14 +24,10 @@ public class ProdosDisk
{ "Deleted", "Seedling", "Sapling", "Tree", "", "", "", "", "", "", "", "", "",
"Subdirectory", "Subdirectory Header", "Volume Directory Header" };
private static byte[] dummySeedling = new byte[50];
private static byte[] dummySapling = new byte[2530];
private static byte[] dummyTree = new byte[132000];
private BitSet volumeBitMap;
private final int maxBlocks;
private final byte[] buffer;
private final byte[] bootSector = new byte[512];
private final byte[] bootSector = new byte[BLOCK_SIZE];
private VolumeDirectoryHeader volumeDirectoryHeader;
private Map<Integer, SubdirectoryHeader> subdirectoryHeaders = new HashMap<> ();
@ -41,10 +37,10 @@ public class ProdosDisk
// ---------------------------------------------------------------------------------//
{
try (DataInputStream in = new DataInputStream (ProdosDisk.class.getClassLoader ()
.getResourceAsStream ("com/bytezone/prodos/block-00.bin")))
.getResourceAsStream ("com/bytezone/diskbrowser/prodos/write/block-00.bin")))
{
int count = in.read (bootSector);
if (count != 512)
if (count != BLOCK_SIZE)
System.out.println ("Error with prodos boot sector");
}
catch (IOException e)
@ -52,10 +48,6 @@ public class ProdosDisk
e.printStackTrace ();
}
setBuffer (dummySeedling, "This is a seedling file.~");
setBuffer (dummySapling, "This is a sapling file.~It could be a bit longer.~");
setBuffer (dummyTree, "This is a tree file.~It is a bit short though.~");
maxBlocks = blocks;
buffer = new byte[blocks * BLOCK_SIZE];
@ -65,13 +57,6 @@ public class ProdosDisk
createCatalog (volumeName);
for (int i = 1; i < 4; i++)
test (String.format ("FRED/MARY/FILE%02d.TXT", i), i);
for (int i = 4; i <= 4; i++)
test (String.format ("BOB/JANE/TMART%02d.TXT", i), i);
writeVolumeBitMap ();
volumeDirectoryHeader.write ();
for (SubdirectoryHeader subdirectoryHeader : subdirectoryHeaders.values ())
subdirectoryHeader.write ();
@ -119,26 +104,6 @@ public class ProdosDisk
writeVolumeBitMap ();
}
// ---------------------------------------------------------------------------------//
public void saveDisk (Path path)
// ---------------------------------------------------------------------------------//
{
// if (Files.exists (path))
// {
// System.out.println ("File already exists: " + path);
// return;
// }
try
{
Files.write (path, buffer);
}
catch (IOException ex)
{
ex.printStackTrace ();
}
}
// ---------------------------------------------------------------------------------//
public int getFreeBlocks ()
// ---------------------------------------------------------------------------------//
@ -147,86 +112,54 @@ public class ProdosDisk
}
// ---------------------------------------------------------------------------------//
void test (String path, int type)
FileEntry addFile (String path, int type)
// ---------------------------------------------------------------------------------//
{
String[] paths;
String[] subdirectories;
String fileName = "";
int pos = path.lastIndexOf ('/');
if (pos > 0)
{
subdirectories = path.substring (0, pos).split ("/");
fileName = path.substring (pos + 1);
path = path.substring (0, pos);
paths = path.split ("/");
}
else
{
subdirectories = new String[0];
fileName = path;
paths = new String[0];
}
int catalogBlock = 2;
int catalogBlockNo = 2;
for (int i = 0; i < paths.length; i++)
for (int i = 0; i < subdirectories.length; i++)
{
String name = paths[i];
FileEntry fileEntry = searchDirectory (catalogBlock, name);
FileEntry fileEntry = searchDirectory (catalogBlockNo, subdirectories[i]);
if (fileEntry == null)
{
FileEntry fileEntry2 = createSubdirectory (catalogBlock, name);
catalogBlock = fileEntry2.keyPointer;
}
else
catalogBlock = fileEntry.keyPointer;
fileEntry = createSubdirectory (catalogBlockNo, subdirectories[i]);
catalogBlockNo = fileEntry.keyPointer;
}
FileEntry fileEntry = searchDirectory (catalogBlock, fileName);
if (fileEntry == null)
fileEntry = createFileEntry (catalogBlock, fileName, type);
}
FileEntry fileEntry = searchDirectory (catalogBlockNo, fileName);
if (fileEntry != null)
{
System.out.println ("File already exists: " + path);
return null; // throw something?
}
// ---------------------------------------------------------------------------------//
FileEntry createFileEntry (int blockNo, String name, int type)
// ---------------------------------------------------------------------------------//
{
FileEntry fileEntry = findFreeSlot (blockNo);
fileEntry = findFreeSlot (catalogBlockNo);
if (fileEntry != null)
{
fileEntry.fileName = name;
fileEntry.fileName = fileName;
fileEntry.creationDate = LocalDateTime.now ();
fileEntry.modifiedDate = LocalDateTime.now ();
fileEntry.version = 0x00;
fileEntry.minVersion = 0x00;
fileEntry.headerPointer = blockNo;
fileEntry.headerPointer = catalogBlockNo;
fileEntry.fileType = 4; // text
switch (type)
{
case 1:
fileEntry.writeFile (dummySeedling);
break;
case 2:
fileEntry.writeFile (dummySapling);
break;
case 3:
fileEntry.writeFile (dummyTree);
break;
case 4:
fileEntry.auxType = 60; // record length
fileEntry.writeRecord (17, getBuffer (String.format ("Record: %5d~", 17)));
fileEntry.writeRecord (45, getBuffer (String.format ("Record: %5d~", 45)));
fileEntry.writeRecord (50, getBuffer (String.format ("Record: %5d~", 50)));
fileEntry.writeRecord (51, getBuffer (String.format ("Record: %5d~", 51)));
fileEntry.writeRecord (500, getBuffer (String.format ("Record: %5d~", 500)));
fileEntry.writeRecord (2000, getBuffer (String.format ("Record: %5d~", 2000)));
fileEntry.writeRecord (3000, getBuffer (String.format ("Record: %5d~", 3000)));
break;
}
fileEntry.write ();
updateFileCount (fileEntry.headerPointer);
@ -237,34 +170,7 @@ public class ProdosDisk
}
// ---------------------------------------------------------------------------------//
private byte[] getBuffer (String text)
// ---------------------------------------------------------------------------------//
{
byte[] dataBuffer = text.getBytes ();
for (int i = 0; i < dataBuffer.length; i++)
if (dataBuffer[i] == '~')
dataBuffer[i] = 0x0D;
else
dataBuffer[i] |= 0x80;
return dataBuffer;
}
// ---------------------------------------------------------------------------------//
private void setBuffer (byte[] buffer, String text)
// ---------------------------------------------------------------------------------//
{
byte[] dataBuffer = text.getBytes ();
for (int i = 0; i < dataBuffer.length; i++)
if (dataBuffer[i] == '~')
buffer[i] = 0x0D;
else
buffer[i] = (byte) (dataBuffer[i] | 0x80);
buffer[dataBuffer.length - 1] = 0x0D;
}
// ---------------------------------------------------------------------------------//
FileEntry searchDirectory (int blockNo, String fileName)
private FileEntry searchDirectory (int blockNo, String fileName)
// ---------------------------------------------------------------------------------//
{
int emptySlotPtr = 0;
@ -296,14 +202,14 @@ public class ProdosDisk
ptr += ENTRY_SIZE;
}
blockNo = ProdosDisk.readShort (buffer, offset + 2);
blockNo = readShort (buffer, offset + 2);
} while (blockNo > 0);
return null;
}
// ---------------------------------------------------------------------------------//
FileEntry createSubdirectory (int blockNo, String name)
private FileEntry createSubdirectory (int blockNo, String name)
// ---------------------------------------------------------------------------------//
{
FileEntry fileEntry = findFreeSlot (blockNo);
@ -346,7 +252,7 @@ public class ProdosDisk
}
// ---------------------------------------------------------------------------------//
void updateFileCount (int catalogBlock)
private void updateFileCount (int catalogBlock)
// ---------------------------------------------------------------------------------//
{
if (catalogBlock == 2)
@ -373,24 +279,14 @@ public class ProdosDisk
}
// ---------------------------------------------------------------------------------//
int getFreeBlock ()
private int getFreeBlock ()
// ---------------------------------------------------------------------------------//
{
return volumeBitMap.nextSetBit (0);
}
// ---------------------------------------------------------------------------------//
String getPrefix (String fileName)
// ---------------------------------------------------------------------------------//
{
int pos = fileName.indexOf ('/', 1);
if (pos < 0)
return fileName;
return fileName.substring (0, pos);
}
// ---------------------------------------------------------------------------------//
FileEntry findFreeSlot (int blockNo)
private FileEntry findFreeSlot (int blockNo)
// ---------------------------------------------------------------------------------//
{
SubdirectoryHeader subdirectoryHeader = subdirectoryHeaders.get (blockNo);
@ -410,7 +306,7 @@ public class ProdosDisk
}
lastBlockNo = blockNo;
blockNo = ProdosDisk.readShort (buffer, offset + 2); // next block
blockNo = readShort (buffer, offset + 2); // next block
} while (blockNo > 0);
// no free slots, so add a new catalog block
@ -435,11 +331,10 @@ public class ProdosDisk
}
// ---------------------------------------------------------------------------------//
void writeVolumeBitMap ()
private void writeVolumeBitMap ()
// ---------------------------------------------------------------------------------//
{
int ptr = (2 + CATALOG_SIZE) * BLOCK_SIZE;
// int count = 0;
int val = 0;
int blockNo = 0;
@ -456,96 +351,4 @@ public class ProdosDisk
}
}
}
// ---------------------------------------------------------------------------------//
public static LocalDateTime getAppleDate (byte[] buffer, int offset)
// ---------------------------------------------------------------------------------//
{
int yymmdd = readShort (buffer, offset);
if (yymmdd != 0)
{
int year = (yymmdd & 0xFE00) >> 9;
int month = (yymmdd & 0x01E0) >> 5;
int day = yymmdd & 0x001F;
int minute = buffer[offset + 2] & 0x3F;
int hour = buffer[offset + 3] & 0x1F;
if (year < 70)
year += 2000;
else
year += 1900;
return LocalDateTime.of (year, month - 1, day, hour, minute);
}
return null;
}
// ---------------------------------------------------------------------------------//
public static void putAppleDate (byte[] buffer, int offset, LocalDateTime date)
// ---------------------------------------------------------------------------------//
{
if (date == null)
{
System.out.println ("ignoring null date");
}
else
{
int year = date.getYear ();
int month = date.getMonthValue ();
int day = date.getDayOfMonth ();
int hour = date.getHour ();
int minute = date.getMinute ();
if (year < 2000)
year -= 1900;
else
year -= 2000;
int val1 = year << 9 | month << 5 | day;
writeShort (buffer, offset, val1);
buffer[offset + 2] = (byte) minute;
buffer[offset + 3] = (byte) hour;
}
}
// ---------------------------------------------------------------------------------//
static void writeShort (byte[] buffer, int ptr, int value)
// ---------------------------------------------------------------------------------//
{
buffer[ptr] = (byte) (value & 0xFF);
buffer[ptr + 1] = (byte) ((value & 0xFF00) >>> 8);
}
// ---------------------------------------------------------------------------------//
static void writeTriple (byte[] buffer, int ptr, int value)
// ---------------------------------------------------------------------------------//
{
buffer[ptr] = (byte) (value & 0xFF);
buffer[ptr + 1] = (byte) ((value & 0xFF00) >>> 8);
buffer[ptr + 2] = (byte) ((value & 0xFF0000) >>> 16);
}
// ---------------------------------------------------------------------------------//
static int readShort (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
return buffer[ptr] | buffer[ptr + 1] << 8;
}
// ---------------------------------------------------------------------------------//
static int readTriple (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
return buffer[ptr] | buffer[ptr + 1] << 8 | buffer[ptr + 2] << 16;
}
// ---------------------------------------------------------------------------------//
public static void main (String[] args) throws IOException
// ---------------------------------------------------------------------------------//
{
String base = System.getProperty ("user.home");
Path path = Paths.get (base + "/Dropbox/Examples/Testing/Test01.po");
ProdosDisk disk = new ProdosDisk (1600, "DENIS.DISK");
disk.saveDisk (path);
}
}

View File

@ -2,6 +2,8 @@ package com.bytezone.diskbrowser.prodos.write;
import static com.bytezone.diskbrowser.prodos.write.ProdosDisk.ENTRY_SIZE;
import static com.bytezone.diskbrowser.prodos.write.ProdosDisk.UNDERLINE;
import static com.bytezone.diskbrowser.utilities.Utility.readShort;
import static com.bytezone.diskbrowser.utilities.Utility.writeShort;
// -----------------------------------------------------------------------------------//
public class SubdirectoryHeader extends DirectoryHeader
@ -28,7 +30,7 @@ public class SubdirectoryHeader extends DirectoryHeader
{
super.read ();
parentPointer = ProdosDisk.readShort (buffer, ptr + 0x23);
parentPointer = readShort (buffer, ptr + 0x23);
parentEntry = buffer[ptr + 0x25];
parentEntryLength = buffer[ptr + 0x26];
}
@ -49,7 +51,7 @@ public class SubdirectoryHeader extends DirectoryHeader
// buffer[ptr + 0x15] = entriesPerBlock;
// fields specific to subdirectory headers
ProdosDisk.writeShort (buffer, ptr + 0x23, parentPointer);
writeShort (buffer, ptr + 0x23, parentPointer);
buffer[ptr + 0x25] = parentEntry;
buffer[ptr + 0x26] = parentEntryLength;
}

View File

@ -1,6 +1,8 @@
package com.bytezone.diskbrowser.prodos.write;
import static com.bytezone.diskbrowser.prodos.write.ProdosDisk.UNDERLINE;
import static com.bytezone.diskbrowser.utilities.Utility.readShort;
import static com.bytezone.diskbrowser.utilities.Utility.writeShort;
// -----------------------------------------------------------------------------------//
public class VolumeDirectoryHeader extends DirectoryHeader
@ -25,8 +27,8 @@ public class VolumeDirectoryHeader extends DirectoryHeader
{
super.read ();
bitMapPointer = ProdosDisk.readShort (buffer, ptr + 0x23);
totalBlocks = ProdosDisk.readShort (buffer, ptr + 0x25);
bitMapPointer = readShort (buffer, ptr + 0x23);
totalBlocks = readShort (buffer, ptr + 0x25);
}
// ---------------------------------------------------------------------------------//
@ -36,8 +38,8 @@ public class VolumeDirectoryHeader extends DirectoryHeader
{
super.write ();
ProdosDisk.writeShort (buffer, ptr + 0x23, bitMapPointer);
ProdosDisk.writeShort (buffer, ptr + 0x25, totalBlocks);
writeShort (buffer, ptr + 0x23, bitMapPointer);
writeShort (buffer, ptr + 0x25, totalBlocks);
}
// ---------------------------------------------------------------------------------//

View File

@ -181,6 +181,84 @@ public class Utility
return val;
}
// ---------------------------------------------------------------------------------//
public static LocalDateTime getAppleDate (byte[] buffer, int offset)
// ---------------------------------------------------------------------------------//
{
int yymmdd = readShort (buffer, offset);
if (yymmdd != 0)
{
int year = (yymmdd & 0xFE00) >> 9;
int month = (yymmdd & 0x01E0) >> 5;
int day = yymmdd & 0x001F;
int minute = buffer[offset + 2] & 0x3F;
int hour = buffer[offset + 3] & 0x1F;
if (year < 70)
year += 2000;
else
year += 1900;
return LocalDateTime.of (year, month - 1, day, hour, minute);
}
return null;
}
// ---------------------------------------------------------------------------------//
public static void putAppleDate (byte[] buffer, int offset, LocalDateTime date)
// ---------------------------------------------------------------------------------//
{
if (date != null)
{
int year = date.getYear ();
int month = date.getMonthValue ();
int day = date.getDayOfMonth ();
int hour = date.getHour ();
int minute = date.getMinute ();
if (year < 2000)
year -= 1900;
else
year -= 2000;
int val1 = year << 9 | month << 5 | day;
writeShort (buffer, offset, val1);
buffer[offset + 2] = (byte) minute;
buffer[offset + 3] = (byte) hour;
}
}
// ---------------------------------------------------------------------------------//
public static void writeShort (byte[] buffer, int ptr, int value)
// ---------------------------------------------------------------------------------//
{
buffer[ptr] = (byte) (value & 0xFF);
buffer[ptr + 1] = (byte) ((value & 0xFF00) >>> 8);
}
// ---------------------------------------------------------------------------------//
public static void writeTriple (byte[] buffer, int ptr, int value)
// ---------------------------------------------------------------------------------//
{
buffer[ptr] = (byte) (value & 0xFF);
buffer[ptr + 1] = (byte) ((value & 0xFF00) >>> 8);
buffer[ptr + 2] = (byte) ((value & 0xFF0000) >>> 16);
}
// ---------------------------------------------------------------------------------//
public static int readShort (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
return buffer[ptr] | buffer[ptr + 1] << 8;
}
// ---------------------------------------------------------------------------------//
public static int readTriple (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
return buffer[ptr] | buffer[ptr + 1] << 8 | buffer[ptr + 2] << 16;
}
// ---------------------------------------------------------------------------------//
public static String matchFlags (int flag, String[] chars)
// ---------------------------------------------------------------------------------//