2021-05-03 20:12:44 +00:00
|
|
|
package com.bytezone.diskbrowser.prodos.write;
|
|
|
|
|
|
|
|
import static com.bytezone.diskbrowser.prodos.ProdosConstants.BLOCK_SIZE;
|
|
|
|
import static com.bytezone.diskbrowser.prodos.ProdosConstants.SAPLING;
|
|
|
|
import static com.bytezone.diskbrowser.prodos.ProdosConstants.SEEDLING;
|
|
|
|
import static com.bytezone.diskbrowser.prodos.ProdosConstants.TREE;
|
|
|
|
|
2022-08-10 05:08:10 +00:00
|
|
|
//Assumptions:
|
|
|
|
//- file does not already exist
|
|
|
|
//- disk has no interleave
|
2021-05-03 20:12:44 +00:00
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
public class FileWriter
|
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
private final ProdosDisk disk;
|
|
|
|
|
|
|
|
private IndexBlock indexBlock = null;
|
|
|
|
private MasterIndexBlock masterIndexBlock = null;
|
|
|
|
|
|
|
|
byte storageType;
|
|
|
|
int keyPointer;
|
|
|
|
int blocksUsed;
|
|
|
|
int eof;
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-05-03 21:24:08 +00:00
|
|
|
FileWriter (ProdosDisk disk)
|
2021-05-03 20:12:44 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
this.disk = disk;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
void writeFile (byte[] dataBuffer, int eof) throws DiskFullException
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-05-05 03:10:33 +00:00
|
|
|
this.eof = Math.min (eof, dataBuffer.length);
|
2021-05-03 20:12:44 +00:00
|
|
|
|
|
|
|
int dataPtr = 0;
|
2021-05-05 03:10:33 +00:00
|
|
|
int remaining = this.eof;
|
2021-05-03 20:12:44 +00:00
|
|
|
|
2021-05-05 03:10:33 +00:00
|
|
|
while (dataPtr < this.eof)
|
2021-05-03 20:12:44 +00:00
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
int actualBlockNo = register (dataPtr / BLOCK_SIZE);
|
2021-05-03 20:12:44 +00:00
|
|
|
|
|
|
|
int bufferPtr = actualBlockNo * BLOCK_SIZE;
|
2021-05-05 03:10:33 +00:00
|
|
|
int transfer = Math.min (remaining, BLOCK_SIZE);
|
2021-05-03 20:12:44 +00:00
|
|
|
|
2021-05-05 03:10:33 +00:00
|
|
|
System.arraycopy (dataBuffer, dataPtr, disk.getBuffer (), bufferPtr, transfer);
|
2021-05-03 20:12:44 +00:00
|
|
|
|
2021-05-05 03:10:33 +00:00
|
|
|
dataPtr += transfer;
|
|
|
|
remaining -= transfer;
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
writeIndices ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2022-08-10 05:08:10 +00:00
|
|
|
void writeRecord (int recordNo, byte[] dataBuffer, int recordLength) throws DiskFullException
|
2021-05-03 20:12:44 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
assert recordLength > 0;
|
|
|
|
|
|
|
|
int destPtr = recordLength * recordNo;
|
|
|
|
int remaining = Math.min (recordLength, dataBuffer.length);
|
|
|
|
int max = destPtr + remaining;
|
|
|
|
int dataPtr = 0;
|
|
|
|
|
|
|
|
if (eof < max)
|
|
|
|
eof = max;
|
|
|
|
|
|
|
|
while (destPtr < max)
|
|
|
|
{
|
|
|
|
int logicalBlockNo = destPtr / BLOCK_SIZE;
|
|
|
|
int blockOffset = destPtr % BLOCK_SIZE;
|
|
|
|
int tfr = Math.min (BLOCK_SIZE - blockOffset, remaining);
|
|
|
|
|
|
|
|
int actualBlockNo = getActualBlockNo (logicalBlockNo);
|
|
|
|
int bufferPtr = actualBlockNo * BLOCK_SIZE + blockOffset;
|
|
|
|
|
2021-05-04 06:31:11 +00:00
|
|
|
System.arraycopy (dataBuffer, dataPtr, disk.getBuffer (), bufferPtr, tfr);
|
2021-05-03 20:12:44 +00:00
|
|
|
|
|
|
|
destPtr += tfr;
|
|
|
|
dataPtr += tfr;
|
|
|
|
remaining -= tfr;
|
|
|
|
}
|
|
|
|
|
|
|
|
writeIndices ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private int allocateNextBlock () throws DiskFullException
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
blocksUsed++;
|
|
|
|
return disk.allocateNextBlock ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private int getActualBlockNo (int logicalBlockNo) throws DiskFullException
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
int actualBlockNo = 0;
|
|
|
|
|
|
|
|
switch (storageType)
|
|
|
|
{
|
|
|
|
case TREE:
|
2022-08-10 05:08:10 +00:00
|
|
|
actualBlockNo =
|
|
|
|
masterIndexBlock.get (logicalBlockNo / 0x100).getPosition (logicalBlockNo % 0x100);
|
2021-05-03 20:12:44 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SAPLING:
|
2021-05-05 03:10:33 +00:00
|
|
|
if (logicalBlockNo < 0x100)
|
|
|
|
actualBlockNo = indexBlock.getPosition (logicalBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SEEDLING:
|
|
|
|
if (logicalBlockNo == 0)
|
|
|
|
actualBlockNo = keyPointer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actualBlockNo == 0)
|
2022-08-10 05:08:10 +00:00
|
|
|
actualBlockNo = register (logicalBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
|
|
|
|
return actualBlockNo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private void writeIndices ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
if (storageType == TREE)
|
2021-05-04 06:31:11 +00:00
|
|
|
masterIndexBlock.write (disk.getBuffer ());
|
2021-05-03 20:12:44 +00:00
|
|
|
else if (storageType == SAPLING)
|
2021-05-04 06:31:11 +00:00
|
|
|
indexBlock.write (disk.getBuffer ());
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2022-08-10 05:08:10 +00:00
|
|
|
private int register (int logicalBlockNo) throws DiskFullException
|
2021-05-03 20:12:44 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
int nextBlockNo = allocateNextBlock ();
|
|
|
|
|
2021-05-05 03:10:33 +00:00
|
|
|
if (logicalBlockNo >= 0x100) // potential TREE
|
2021-05-03 20:12:44 +00:00
|
|
|
{
|
|
|
|
if (storageType != TREE)
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
masterIndexBlock = new MasterIndexBlock (nextBlockNo);
|
|
|
|
nextBlockNo = allocateNextBlock ();
|
2021-05-03 20:12:44 +00:00
|
|
|
|
|
|
|
if (storageType == SAPLING) // sapling -> tree
|
|
|
|
{
|
|
|
|
masterIndexBlock.set (0, indexBlock);
|
|
|
|
}
|
|
|
|
else if (storageType == SEEDLING) // seedling -> sapling -> tree
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
indexBlock = new IndexBlock (nextBlockNo);
|
|
|
|
nextBlockNo = allocateNextBlock ();
|
|
|
|
|
2021-05-04 06:31:11 +00:00
|
|
|
indexBlock.setPosition (0, keyPointer);
|
2021-05-03 20:12:44 +00:00
|
|
|
masterIndexBlock.set (0, indexBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
keyPointer = masterIndexBlock.blockNo;
|
|
|
|
storageType = TREE;
|
|
|
|
indexBlock = null;
|
|
|
|
}
|
|
|
|
|
2022-08-10 05:08:10 +00:00
|
|
|
getIndexBlock (logicalBlockNo / 0x100).setPosition (logicalBlockNo % 0x100, nextBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
else if (logicalBlockNo > 0) // potential SAPLING
|
|
|
|
{
|
|
|
|
if (storageType == TREE) // already a tree
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
getIndexBlock (0).setPosition (logicalBlockNo, nextBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
else if (storageType == SAPLING) // already a sapling
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
indexBlock.setPosition (logicalBlockNo, nextBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
else // new file or already a seedling
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
indexBlock = new IndexBlock (nextBlockNo);
|
|
|
|
nextBlockNo = allocateNextBlock ();
|
|
|
|
|
2021-05-03 20:12:44 +00:00
|
|
|
if (storageType == SEEDLING) // seedling -> sapling
|
2021-05-04 06:31:11 +00:00
|
|
|
indexBlock.setPosition (0, keyPointer);
|
2021-05-03 20:12:44 +00:00
|
|
|
|
|
|
|
keyPointer = indexBlock.blockNo;
|
|
|
|
storageType = SAPLING;
|
2022-08-10 05:08:10 +00:00
|
|
|
indexBlock.setPosition (logicalBlockNo, nextBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (logicalBlockNo == 0) // potential SEEDLING
|
|
|
|
{
|
|
|
|
if (storageType == TREE) // already a tree
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
getIndexBlock (0).setPosition (0, nextBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
else if (storageType == SAPLING) // already a sapling
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
indexBlock.setPosition (0, nextBlockNo);
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-08-10 05:08:10 +00:00
|
|
|
keyPointer = nextBlockNo;
|
2021-05-03 20:12:44 +00:00
|
|
|
storageType = SEEDLING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2021-05-05 03:10:33 +00:00
|
|
|
System.out.println ("Error: " + logicalBlockNo);
|
2022-08-10 05:08:10 +00:00
|
|
|
|
|
|
|
return nextBlockNo;
|
2021-05-03 20:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private IndexBlock getIndexBlock (int position) throws DiskFullException
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
IndexBlock indexBlock = masterIndexBlock.get (position);
|
|
|
|
|
|
|
|
if (indexBlock == null)
|
|
|
|
{
|
|
|
|
indexBlock = new IndexBlock (allocateNextBlock ());
|
|
|
|
masterIndexBlock.set (position, indexBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return indexBlock;
|
|
|
|
}
|
|
|
|
}
|