Add file-put and delete capabilities for GEOS files

This commit is contained in:
2012-03-20 18:12:51 +00:00
parent 394381f561
commit 199b36d5a6
4 changed files with 281 additions and 83 deletions

View File

@ -123,7 +123,12 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
if (isDeleted()) { if (isDeleted()) {
AppleUtil.setString(fileEntry, 1, filename.toUpperCase(), 15); AppleUtil.setString(fileEntry, 1, filename.toUpperCase(), 15);
} else { } else {
AppleUtil.setProdosString(fileEntry, 0, filename.toUpperCase(), 15); if (isGEOSFile()) {
// No need to upper-case or be picky about GEOS filenames
AppleUtil.setProdosString(fileEntry, 0, filename, 15);
} else {
AppleUtil.setProdosString(fileEntry, 0, filename.toUpperCase(), 15);
}
} }
if (isAppleWorksFile()) { if (isAppleWorksFile()) {
byte lowByte = 0; byte lowByte = 0;
@ -173,6 +178,14 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
return (filetype == 0x19 || filetype == 0x1a || filetype == 0x1b); return (filetype == 0x19 || filetype == 0x1a || filetype == 0x1b);
} }
/**
* Indicate if this is a GEOS file.
*/
public boolean isGEOSFile() {
int filetype = AppleUtil.getUnsignedByte(readFileEntry()[0x10]);
return (filetype >= 0x80 && filetype <= 0x8f);
}
/** /**
* Get the key pointer. This is either the data block (seedling), * Get the key pointer. This is either the data block (seedling),
* index block (sapling), or master index block (tree). * index block (sapling), or master index block (tree).
@ -235,7 +248,7 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
public int getAuxiliaryType() { public int getAuxiliaryType() {
return AppleUtil.getWordValue(readFileEntry(), 0x1f); return AppleUtil.getWordValue(readFileEntry(), 0x1f);
} }
/** /**
* Set the auxiliary type for this file. * Set the auxiliary type for this file.
*/ */
@ -243,7 +256,7 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
entry[0x1f] = low; entry[0x1f] = low;
entry[0x20] = high; entry[0x20] = high;
} }
/** /**
* Set the auxiliary type for this file. * Set the auxiliary type for this file.
*/ */
@ -258,7 +271,7 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
public Date getLastModificationDate() { public Date getLastModificationDate() {
return AppleUtil.getProdosDate(readFileEntry(), 0x21); return AppleUtil.getProdosDate(readFileEntry(), 0x21);
} }
/** /**
* Set the last modification date. * Set the last modification date.
*/ */
@ -472,7 +485,7 @@ public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
return new AssemblySourceFileFilter(); return new AssemblySourceFileFilter();
} }
return new TextFileFilter(); return new TextFileFilter();
case 0x09: // BA3 case 0x09: // BA3
return new BusinessBASICFileFilter(); return new BusinessBASICFileFilter();
case 0xb0: // SRC case 0xb0: // SRC
return new TextFileFilter(); return new TextFileFilter();

View File

@ -73,6 +73,25 @@ filetype.6e=PRE
# PC Volume # PC Volume
filetype.6f=HDV filetype.6f=HDV
# GEOS
# $80-$8F ($82 for a GEOS 'application').
filetype.80=GEZ
filetype.81=GE1
filetype.82=GEO
filetype.83=GE3
filetype.84=GE4
filetype.85=GE5
filetype.86=GE6
filetype.87=GE7
filetype.88=GE8
filetype.89=GE9
filetype.8a=GEA
filetype.8b=GEB
filetype.8c=GEC
filetype.8d=GED
filetype.8e=GEE
filetype.8f=GEF
filetype.a0=WP_ filetype.a0=WP_
filetype.ab=GSB filetype.ab=GSB
filetype.ac=TDF filetype.ac=TDF

View File

@ -494,7 +494,7 @@ public class ProdosFormatDisk extends FormattedDisk {
public boolean canReadFileData() { public boolean canReadFileData() {
return true; return true;
} }
/** /**
* Identify if this disk format is capable of having directories. * Identify if this disk format is capable of having directories.
* @see com.webcodepro.applecommander.storage.FormattedDisk#canHaveDirectories() * @see com.webcodepro.applecommander.storage.FormattedDisk#canHaveDirectories()
@ -509,7 +509,7 @@ public class ProdosFormatDisk extends FormattedDisk {
public boolean canWriteFileData() { public boolean canWriteFileData() {
return true; return true;
} }
/** /**
* Indicates if this disk image can delete a file. * Indicates if this disk image can delete a file.
*/ */
@ -551,35 +551,50 @@ public class ProdosFormatDisk extends FormattedDisk {
} }
/** /**
* Free blocks used by a DosFileEntry. * Free blocks used by a ProdosFileEntry.
*/ */
protected void freeBlocks(ProdosFileEntry prodosFileEntry) { protected void freeBlocks(ProdosFileEntry prodosFileEntry) {
byte[] bitmap = readVolumeBitMap(); byte[] bitmap = readVolumeBitMap();
int block = prodosFileEntry.getKeyPointer(); int block = prodosFileEntry.getKeyPointer();
if (block == 0) return; // new entry if (block == 0) return; // new entry
if (prodosFileEntry.isGEOSFile()) {
// A GEOS file allocates another block, pointed to by the aux bytes.
setBlockFree(bitmap,prodosFileEntry.getAuxiliaryType());
}
setBlockFree(bitmap,block); setBlockFree(bitmap,block);
if (prodosFileEntry.isSaplingFile()) { if (prodosFileEntry.isSaplingFile()) {
freeBlocksInIndex(bitmap,block); freeBlocksInIndex(bitmap,block,false);
} else if (prodosFileEntry.isTreeFile()) { } else if (prodosFileEntry.isTreeFile()) {
byte[] masterIndexBlock = readBlock(block); byte[] masterIndexBlock = readBlock(block);
for (int i=0; i<0x100; i++) { for (int i=0; i<0x100; i++) {
int indexBlockNumber = AppleUtil.getWordValue( if (!prodosFileEntry.isGEOSFile() ||
masterIndexBlock[i], masterIndexBlock[i+0x100]); (prodosFileEntry.isGEOSFile() && (i < 0xfe)))
if (indexBlockNumber > 0) freeBlocksInIndex(bitmap,indexBlockNumber); {
// As long as we're not deleting a GEOS file, delete all index entries.
// GEOS uses records 0xfe and 0xff for space calculations, not pointers.
int indexBlockNumber = AppleUtil.getWordValue(
masterIndexBlock[i], masterIndexBlock[i+0x100]);
if (indexBlockNumber > 0) freeBlocksInIndex(bitmap,indexBlockNumber,prodosFileEntry.isGEOSFile());
}
} }
} }
writeVolumeBitMap(bitmap); writeVolumeBitMap(bitmap);
} }
/** /**
* Free the given index block and the data blocks it points to. * Free the given index block and the data blocks it points to.
*/ */
private void freeBlocksInIndex(byte[] bitmap, int indexBlockNumber) { private void freeBlocksInIndex(byte[] bitmap, int indexBlockNumber, boolean isGEOS) {
setBlockFree(bitmap, indexBlockNumber); setBlockFree(bitmap, indexBlockNumber);
byte[] indexBlock = readBlock(indexBlockNumber); byte[] indexBlock = readBlock(indexBlockNumber);
for (int i=0; i<0x100; i++) { for (int i=0; i<0x100; i++) {
int blockNumber = AppleUtil.getWordValue(indexBlock[i], indexBlock[i+0x100]); if (!isGEOS ||
if (blockNumber > 0) setBlockFree(bitmap, blockNumber); (isGEOS && (i < 0xfe))) {
// As long as we're not deleting a GEOS file, delete all entries.
// GEOS uses records 0xfe and 0xff for space calculations, not pointers.
int blockNumber = AppleUtil.getWordValue(indexBlock[i], indexBlock[i+0x100]);
if (blockNumber > 0) setBlockFree(bitmap, blockNumber);
}
} }
} }
@ -605,19 +620,118 @@ public class ProdosFormatDisk extends FormattedDisk {
/** /**
* Set the data associated with the specified ProdosFileEntry into sectors * Set the data associated with the specified ProdosFileEntry into sectors
* on the disk. * on the disk. Automatically grows the filesystem structures from seedling
* to sapling to tree.
*/ */
protected void setFileData(ProdosFileEntry fileEntry, byte[] fileData) protected void setFileData(ProdosFileEntry fileEntry, byte[] fileData)
throws DiskFullException { throws DiskFullException {
// compute free space and see if the data will fit! if (fileEntry.isGEOSFile()) {
int numberOfDataBlocks = (fileData.length + BLOCK_SIZE - 1) / BLOCK_SIZE; // If this is a GEOS file, things are a bit different.
int numberOfBlocks = numberOfDataBlocks; setGEOSFileData(fileEntry, fileData);
if (numberOfBlocks > 1) { } else {
numberOfBlocks+= ((numberOfDataBlocks-1) / 256) + 1; // that's 128K // compute free space and see if the data will fit!
if (numberOfDataBlocks > 256) { int numberOfDataBlocks = (fileData.length + BLOCK_SIZE - 1) / BLOCK_SIZE;
numberOfBlocks++; int numberOfBlocks = numberOfDataBlocks;
if (numberOfBlocks > 1) {
numberOfBlocks+= ((numberOfDataBlocks-1) / 256) + 1; // that's 128K
if (numberOfDataBlocks > 256) {
numberOfBlocks++;
}
} }
if (numberOfBlocks > getFreeBlocks() + fileEntry.getBlocksUsed()) {
throw new DiskFullException(textBundle.
format("ProdosFormatDisk.NotEnoughSpaceOnDiskError", //$NON-NLS-1$
numberOfBlocks, getFreeBlocks()));
}
// free "old" data and just rewrite stuff...
freeBlocks(fileEntry);
byte[] bitmap = readVolumeBitMap();
int blockNumber = fileEntry.getKeyPointer();
if (blockNumber == 0) {
blockNumber = findFreeBlock(bitmap);
}
int indexBlockNumber = 0;
byte[] indexBlockData = null;
int masterIndexBlockNumber = 0;
byte[] masterIndexBlockData = new byte[BLOCK_SIZE];
int offset = 0;
int blockCount = 0;
while (offset < fileData.length) {
if (blockCount > 0) blockNumber = findFreeBlock(bitmap);
setBlockUsed(bitmap, blockNumber);
blockCount++;
byte[] blockData = new byte[BLOCK_SIZE];
int length = Math.min(BLOCK_SIZE, fileData.length - offset);
System.arraycopy(fileData,offset,blockData,0,length);
writeBlock(blockNumber, blockData);
if (numberOfDataBlocks > 1) {
// growing to a tree file
if (offset > 0 && (offset / BLOCK_SIZE) % 256 == 0) {
if (masterIndexBlockNumber == 0) {
masterIndexBlockNumber = findFreeBlock(bitmap);
setBlockUsed(bitmap, masterIndexBlockNumber);
blockCount++;
}
writeBlock(indexBlockNumber, indexBlockData);
indexBlockData = null;
indexBlockNumber = 0;
}
// new index block
if (indexBlockData == null) { // sapling files
indexBlockNumber = findFreeBlock(bitmap);
indexBlockData = new byte[BLOCK_SIZE];
setBlockUsed(bitmap, indexBlockNumber);
blockCount++;
// This is only used for Tree files (but we always record it):
int position = (offset / (BLOCK_SIZE * 256));
byte low = (byte)(indexBlockNumber % 256);
byte high = (byte)(indexBlockNumber / 256);
masterIndexBlockData[position] = low;
masterIndexBlockData[position + 0x100] = high;
}
// record last block position in index block
int position = (offset / BLOCK_SIZE) % 256;
byte low = (byte)(blockNumber % 256);
byte high = (byte)(blockNumber / 256);
indexBlockData[position] = low;
indexBlockData[position + 0x100] = high;
}
offset+= BLOCK_SIZE;
}
if (numberOfDataBlocks == 1) {
fileEntry.setKeyPointer(blockNumber);
fileEntry.setSeedlingFile();
} else if (numberOfDataBlocks <= 256) {
writeBlock(indexBlockNumber, indexBlockData);
fileEntry.setKeyPointer(indexBlockNumber);
fileEntry.setSaplingFile();
} else {
writeBlock(indexBlockNumber, indexBlockData);
writeBlock(masterIndexBlockNumber, masterIndexBlockData);
fileEntry.setKeyPointer(masterIndexBlockNumber);
fileEntry.setTreeFile();
}
fileEntry.setBlocksUsed(blockCount);
fileEntry.setEofPosition(fileData.length);
fileEntry.setLastModificationDate(new Date());
writeVolumeBitMap(bitmap);
}
}
/**
* Set the data associated with the specified ProdosFileEntry into sectors
* on the disk. Take GEOS file structures into account.
*/
protected void setGEOSFileData(ProdosFileEntry fileEntry, byte[] fileData)
throws DiskFullException {
// compute free space and see if the data will fit!
int numberOfDataBlocks = (fileData.length - 1) / BLOCK_SIZE;
int numberOfBlocks = numberOfDataBlocks;
numberOfBlocks+= ((numberOfDataBlocks-1) / 254) + 1; // GEOS uses the last two blocks for eof calculations
if (numberOfDataBlocks > 254) {
numberOfBlocks++;
} }
if (numberOfBlocks > getFreeBlocks() + fileEntry.getBlocksUsed()) { if (numberOfBlocks > getFreeBlocks() + fileEntry.getBlocksUsed()) {
throw new DiskFullException(textBundle. throw new DiskFullException(textBundle.
@ -627,77 +741,129 @@ public class ProdosFormatDisk extends FormattedDisk {
// free "old" data and just rewrite stuff... // free "old" data and just rewrite stuff...
freeBlocks(fileEntry); freeBlocks(fileEntry);
byte[] bitmap = readVolumeBitMap(); byte[] bitmap = readVolumeBitMap();
int blockNumber = fileEntry.getKeyPointer();
if (blockNumber == 0) { // Place the first BLOCK_SIZE bytes of data in a block pointed to by the aux address.
blockNumber = findFreeBlock(bitmap); int headerBlockNumber = findFreeBlock(bitmap);
byte[] headerData = new byte[BLOCK_SIZE];
setBlockUsed(bitmap, headerBlockNumber);
System.arraycopy(fileData,0,headerData,0,BLOCK_SIZE);
writeBlock(headerBlockNumber, headerData);
fileEntry.setAddress(headerBlockNumber);
if (AppleUtil.getUnsignedByte(fileData[0x180]) >> 4 == 2) {
setGEOSSaplingData(bitmap, fileEntry, fileData);
} else {
setGEOSTreeData(bitmap, fileEntry, fileData);
} }
int indexBlockNumber = 0; }
byte[] indexBlockData = null;
int masterIndexBlockNumber = 0; /**
byte[] masterIndexBlockData = new byte[BLOCK_SIZE]; * Set the GEOS "sapling" file data.
int offset = 0; */
int blockCount = 0; protected void setGEOSSaplingData(byte[] bitmap, ProdosFileEntry fileEntry, byte[] fileData)
throws DiskFullException {
int indexBlockNumber = findFreeBlock(bitmap);
setBlockUsed(bitmap, indexBlockNumber);
byte[] indexBlockData = new byte[BLOCK_SIZE];
int offset = BLOCK_SIZE;
int blockNumber = 0;
int blockCount = 1; // The header block counts for one
while (offset < fileData.length) { while (offset < fileData.length) {
if (blockCount > 0) blockNumber = findFreeBlock(bitmap); blockNumber = findFreeBlock(bitmap);
setBlockUsed(bitmap, blockNumber); setBlockUsed(bitmap, blockNumber);
blockCount++; blockCount++;
byte[] blockData = new byte[BLOCK_SIZE]; byte[] blockData = new byte[BLOCK_SIZE];
int length = Math.min(BLOCK_SIZE, fileData.length - offset); int length = Math.min(BLOCK_SIZE, fileData.length - offset);
System.arraycopy(fileData,offset,blockData,0,length); System.arraycopy(fileData,offset,blockData,0,length);
writeBlock(blockNumber, blockData); writeBlock(blockNumber, blockData);
if (numberOfDataBlocks > 1) { // record last block position in index block
// growing to a tree file int position = ((offset - BLOCK_SIZE) / BLOCK_SIZE) % 256;
if (offset > 0 && (offset / BLOCK_SIZE) % 256 == 0) { byte low = (byte)(blockNumber % 256);
if (masterIndexBlockNumber == 0) { byte high = (byte)(blockNumber / 256);
masterIndexBlockNumber = findFreeBlock(bitmap); indexBlockData[position] = low;
setBlockUsed(bitmap, masterIndexBlockNumber); indexBlockData[position + 0x100] = high;
blockCount++;
}
writeBlock(indexBlockNumber, indexBlockData);
indexBlockData = null;
indexBlockNumber = 0;
}
// new index block
if (indexBlockData == null) { // sapling files
indexBlockNumber = findFreeBlock(bitmap);
indexBlockData = new byte[BLOCK_SIZE];
setBlockUsed(bitmap, indexBlockNumber);
blockCount++;
// This is only used for Tree files (but we always record it):
int position = (offset / (BLOCK_SIZE * 256));
byte low = (byte)(indexBlockNumber % 256);
byte high = (byte)(indexBlockNumber / 256);
masterIndexBlockData[position] = low;
masterIndexBlockData[position + 0x100] = high;
}
// record last block position in index block
int position = (offset / BLOCK_SIZE) % 256;
byte low = (byte)(blockNumber % 256);
byte high = (byte)(blockNumber / 256);
indexBlockData[position] = low;
indexBlockData[position + 0x100] = high;
}
offset+= BLOCK_SIZE; offset+= BLOCK_SIZE;
} }
if (numberOfDataBlocks == 1) { indexBlockData[255] = (byte)((fileData.length - BLOCK_SIZE) % 256); // Lo file eof
fileEntry.setKeyPointer(blockNumber); indexBlockData[511] = (byte)((fileData.length - BLOCK_SIZE) / 256); // Med file eof
fileEntry.setSeedlingFile(); writeBlock(indexBlockNumber, indexBlockData);
} else if (numberOfDataBlocks <= 256) { fileEntry.setKeyPointer(indexBlockNumber);
writeBlock(indexBlockNumber, indexBlockData); fileEntry.setSaplingFile();
fileEntry.setKeyPointer(indexBlockNumber);
fileEntry.setSaplingFile();
} else {
writeBlock(indexBlockNumber, indexBlockData);
writeBlock(masterIndexBlockNumber, masterIndexBlockData);
fileEntry.setKeyPointer(masterIndexBlockNumber);
fileEntry.setTreeFile();
}
fileEntry.setBlocksUsed(blockCount); fileEntry.setBlocksUsed(blockCount);
fileEntry.setEofPosition(fileData.length); fileEntry.setEofPosition(fileData.length - BLOCK_SIZE);
fileEntry.setLastModificationDate(new Date()); fileEntry.setLastModificationDate(new Date());
writeVolumeBitMap(bitmap); writeVolumeBitMap(bitmap);
} }
/**
* Set the GEOS "tree" file data.
*/
protected void setGEOSTreeData(byte[] bitmap, ProdosFileEntry fileEntry, byte[] fileData)
throws DiskFullException {
int masterIndexBlockNumber = findFreeBlock(bitmap);
setBlockUsed(bitmap, masterIndexBlockNumber);
byte[] masterIndexBlockData = new byte[BLOCK_SIZE];
int offset = BLOCK_SIZE;
int blockCount = 2; // Start by counting the header block and master index
int eofCount = 0;
for (int masterIterator = 0; masterIterator < 254; masterIterator++) {
if ((fileData[0x100+masterIterator] != 0xff) && (offset < fileData.length)){
byte[] lengthData = new byte[BLOCK_SIZE];
System.arraycopy(fileData,offset,lengthData,0,BLOCK_SIZE);
offset += BLOCK_SIZE;
int recordLength = AppleUtil.getUnsignedByte(lengthData[0xff])
+ AppleUtil.getUnsignedByte(lengthData[0x1ff])*256;
int indexBlockNumber = findFreeBlock(bitmap);
setBlockUsed(bitmap, indexBlockNumber);
blockCount +=1;
byte[] indexBlockData = new byte[BLOCK_SIZE];
int blockNumber = 0;
int startingPoint = offset;
while (offset < startingPoint + recordLength) {
blockNumber = findFreeBlock(bitmap);
setBlockUsed(bitmap, blockNumber);
blockCount +=1;
byte[] blockData = new byte[BLOCK_SIZE];
int length = Math.min(BLOCK_SIZE, recordLength - offset + startingPoint);
System.arraycopy(fileData,offset,blockData,0,length);
writeBlock(blockNumber, blockData);
eofCount += length;
// record last block position in index block
int position = ((offset-startingPoint) / BLOCK_SIZE) % 256;
byte low = (byte)(blockNumber % 256);
byte high = (byte)(blockNumber / 256);
indexBlockData[position] = low;
indexBlockData[position + 0x100] = high;
offset+= BLOCK_SIZE;
}
indexBlockData[0xff] = (byte) (recordLength & 0x0000ff);
indexBlockData[0x1ff] = (byte)((recordLength & 0x00ff00) >> 8);
indexBlockData[0x1fe] = (byte)((recordLength & 0xff0000) >> 16);
writeBlock(indexBlockNumber, indexBlockData);
byte low = (byte)(indexBlockNumber % 256);
byte high = (byte)(indexBlockNumber / 256);
masterIndexBlockData[masterIterator] = low;
masterIndexBlockData[masterIterator + 0x100] = high;
} else if (fileData[0x100+masterIterator] == 0xff) {
masterIndexBlockData[masterIterator] = (byte)0xff;
masterIndexBlockData[masterIterator+0x100] = (byte)0xff;
}
}
masterIndexBlockData[0x0ff] = (byte) (eofCount & 0x0000ff);
masterIndexBlockData[0x1ff] = (byte)((eofCount & 0x00ff00) >> 8);
masterIndexBlockData[0x1fe] = (byte)((eofCount & 0xff0000) >> 16);
writeBlock(masterIndexBlockNumber, masterIndexBlockData);
fileEntry.setKeyPointer(masterIndexBlockNumber);
fileEntry.setTreeFile();
fileEntry.setBlocksUsed(blockCount);
fileEntry.setEofPosition(eofCount);
fileEntry.setLastModificationDate(new Date());
writeVolumeBitMap(bitmap);
}
/** /**
* Locate a free block in the Volume Bitmap. * Locate a free block in the Volume Bitmap.
*/ */

View File

@ -151,8 +151,8 @@ public class ac {
FormattedDisk formattedDisk = formattedDisks[0]; FormattedDisk formattedDisk = formattedDisks[0];
FileEntry entry = name.createEntry(formattedDisk); FileEntry entry = name.createEntry(formattedDisk);
if (entry != null) { if (entry != null) {
entry.setFilename(name.name);
entry.setFiletype(fileType); entry.setFiletype(fileType);
entry.setFilename(name.name);
entry.setFileData(buf.toByteArray()); entry.setFileData(buf.toByteArray());
if (entry.needsAddress()) { if (entry.needsAddress()) {
entry.setAddress(stringToInt(address)); entry.setAddress(stringToInt(address));