more graphics

This commit is contained in:
Denis Molony 2018-08-15 12:32:11 +10:00
parent c09ddea735
commit 462744dce1
9 changed files with 270 additions and 256 deletions

View File

@ -0,0 +1,49 @@
package com.bytezone.diskbrowser.applefile;
public class FaddenHiResImage extends OriginalHiResImage
{
// https://github.com/fadden/fhpack/blob/master/fhpack.cpp
public FaddenHiResImage (String name, byte[] buffer, int fileType, int auxType,
int endOfFile)
{
super (name, buffer, fileType, auxType, endOfFile);
assert buffer[0] == 0x66;
this.buffer = new byte[0x2000];
int outPtr = 0;
int ptr = 1;
while (ptr < buffer.length)
{
int literalLen = (buffer[ptr] & 0xF0) >>> 4;
int matchLen = (buffer[ptr++] & 0x0F) + 4;
if (literalLen == 15)
literalLen = (buffer[ptr++] & 0xFF) + 15;
if (literalLen > 0)
{
System.arraycopy (buffer, ptr, this.buffer, outPtr, literalLen);
ptr += literalLen;
outPtr += literalLen;
}
if (matchLen == 19) // 15 + 4
{
matchLen = (buffer[ptr++] & 0xFF);
if (matchLen == 254) // eof
break;
if (matchLen == 253) // no match
continue;
matchLen += 19;
}
int offset = (buffer[ptr++] & 0xFF) | ((buffer[ptr++] & 0xFF) << 8);
while (matchLen-- > 0)
this.buffer[outPtr++] = this.buffer[offset++];
}
createImage ();
}
}

View File

@ -26,9 +26,10 @@ public abstract class HiResImage extends AbstractFile
// $06 BIN .3200 - SHRPictureFile2
// $06 BIN .3201 - SHRPictureFile2
// $08 PICT <$4000 Apple II Graphics File - ???
// $08 PICT $4000 Packed Hi-Res file - ???
// $08 PICT $4001 Packed Double Hi-Res file - ???
// $08 FOT <$4000 Apple II Graphics File - ???
// $08 FOT $4000 Packed Hi-Res file - ???
// $08 FOT $4001 Packed Double Hi-Res file - ???
// $08 FOT $8066 Fadden Hi-res
// * $C0 PNT $0000 Paintworks Packed Super Hi-Res - SHRPictureFile2
// * $C0 PNT $0001 Packed IIGS Super Hi-Res Image - SHRPictureFile2
@ -153,6 +154,13 @@ public abstract class HiResImage extends AbstractFile
}
/*-
* Files of type $08 and any auxiliary type less than or equal to $3FFF contain a
* standard Apple II graphics file in one of several modes. After determining that
* the auxiliary type is not $4000 or $4001 (which have been defined for high-resolution
* and double high-resolution pictures packed with the Apple IIGS PackBytes routine),
* you can determine the mode of the file by examining byte +120 (+$78). The value of
* this byte, which ranges from zero to seven, is interpreted as follows:
*
Mode Page 1 Page 2
280 x 192 Black & White 0 4
280 x 192 Limited Color 1 5
@ -172,7 +180,7 @@ public abstract class HiResImage extends AbstractFile
switch (fileType)
{
case ProdosConstants.FILE_TYPE_PICT: // 0x08
case ProdosConstants.FILE_TYPE_FOT: // 0x08
if (auxType < 0x4000)
{
auxText = "Apple II Graphics File";
@ -183,6 +191,8 @@ public abstract class HiResImage extends AbstractFile
auxText = "Packed Hi-Res File";
else if (auxType == 0x4001)
auxText = "Packed Double Hi-Res File";
else if (auxType == 0x8066)
auxText = "Fadden Hi-Res File";
else
auxText = "Unknown aux: " + auxType;
break;

View File

@ -34,7 +34,7 @@ public class OriginalHiResImage extends HiResImage
{
super (name, buffer, fileType, auxType, eof);
// createImage ();
// will call createImage () itself
}
// https://github.com/Michaelangel007/apple2_hgr_font_tutorial

View File

@ -21,8 +21,8 @@ class MC3470
private boolean restarted;
private DiskReader diskReader;
private final DiskReader diskReader16 = new DiskReader16Sector ();
private final DiskReader diskReader13 = new DiskReader13Sector ();
private final DiskReader diskReader16Sector = new DiskReader16Sector ();
private final DiskReader diskReader13Sector = new DiskReader13Sector ();
private final byte[] dataBuffer = new byte[MAX_DATA];
private int dataPtr = 0;
@ -39,7 +39,6 @@ class MC3470
List<RawDiskSector> readTrack (byte[] buffer, int offset, int bytesUsed, int bitCount)
throws DiskNibbleException
{
final int max = offset + bytesUsed;
int totalBits = 0;
int totalBytes = 0;
@ -47,21 +46,16 @@ class MC3470
diskReader = null;
currentDiskSector = null;
currentState = State.OTHER;
finished = false;
restarted = false;
byte value = 0; // value to be stored
dataPtr = 0;
expectedDataSize = MAX_DATA;
if (debug)
{
System.out.printf ("%nOffset : %06X%n", offset);
System.out.printf ("Bytes used: %06X%n", bytesUsed);
System.out.printf ("Bit count : %06X%n", bitCount);
}
int inPtr = offset; // keep offset in case we have to loop around
final int max = offset + bytesUsed;
finished = false;
int inPtr = offset; // keep offset in case we have to loop around
while (inPtr < max && !finished)
{
byte b = buffer[inPtr++];
@ -84,16 +78,23 @@ class MC3470
System.out.printf ("%02X ", value);
}
if (dataPtr >= MAX_DATA)
throw new DiskNibbleException ("No prologues found");
dataBuffer[dataPtr++] = value;
checkState (value);
value = 0;
if (currentState == State.OTHER)
checkState ();
else if (dataPtr == expectedDataSize) // DATA or ADDRESS is now complete
setState (State.OTHER);
}
if (++totalBits == bitCount)
if (++totalBits == bitCount) // only use this many bits
break;
}
// check for unfinished data block, we may need to restart the track
// check for unfinished data block, we may need to restart from the beginning
if (inPtr == max && currentState == State.DATA && !restarted)
{
inPtr = offset;
@ -114,6 +115,146 @@ class MC3470
return diskSectors;
}
// ---------------------------------------------------------------------------------//
// checkState
// ---------------------------------------------------------------------------------//
private void checkState () throws DiskNibbleException
{
assert currentState == State.OTHER;
switch (dataBuffer[dataPtr - 1]) // last byte added
{
case (byte) 0xB5:
if (isPrologue ())
{
diskReader = diskReader13Sector;
setState (State.ADDRESS);
}
break;
case (byte) 0x96:
if (isPrologue ())
{
diskReader = diskReader16Sector;
setState (State.ADDRESS);
}
break;
case (byte) 0xAD:
if (isPrologue ())
setState (State.DATA);
break;
case (byte) 0xEB:
if (isEpilogue ())
setState (State.OTHER);
break;
}
}
// ---------------------------------------------------------------------------------//
// setState
// ---------------------------------------------------------------------------------//
private void setState (State newState) throws DiskNibbleException
{
if (currentState == State.OTHER && newState == State.OTHER)
return;
assert currentState != newState : currentState + " -> " + newState;
switch (currentState) // this state is now finished
{
case ADDRESS:
if (currentDiskSector != null)
throw new DiskNibbleException ("unused ADDRESS: " + currentDiskSector);
currentDiskSector = new RawDiskSector (new DiskAddressField (dataBuffer));
if (dump)
System.out.println ("\n" + currentDiskSector);
break;
case DATA:
if (currentDiskSector == null)
throw new DiskNibbleException ("cannot store DATA without ADDRESS");
currentDiskSector.setBuffer (diskReader.decodeSector (dataBuffer));
diskSectors.add (currentDiskSector);
currentDiskSector = null;
if (diskSectors.size () == diskReader.sectorsPerTrack)
finished = true;
break;
case OTHER: // triggered by an epilogue or full address/data buffer
break;
}
switch (newState) // this state is now starting
{
case ADDRESS:
expectedDataSize = 8;
if (dump)
System.out.print ("ADDRESS ");
break;
case DATA:
expectedDataSize = diskReader.expectedDataSize ();
if (dump)
System.out.println ("DATA");
if (debug && currentDiskSector == null)
{
System.out.println ("starting DATA with no ADDRESS");
System.out.println (HexFormatter.format (dataBuffer, 0, dataPtr));
}
break;
case OTHER:
expectedDataSize = MAX_DATA; // what is the maximum filler?
if (dump)
System.out.println ("OTHER");
break;
}
currentState = newState;
dataPtr = 0; // start collecting new buffer
}
// D5 AA 96 16 sector address prologue
// D5 AA B5 13 sector address prologue
// D5 AA AD data prologue
// DE AA EB epilogue
// non-standard:
// D4 AA 96 xx sector address prologue - Bouncing Kamungas
// D5 BB CF data prologue - Hard Hat Mac
// DA AA EB address epilogue - Bouncing Kamungas
// ---------------------------------------------------------------------------------//
// isPrologue
// ---------------------------------------------------------------------------------//
private boolean isPrologue ()
{
return dataPtr >= 3
&& (dataBuffer[dataPtr - 3] == (byte) 0xD5
|| dataBuffer[dataPtr - 3] == (byte) 0xD4) // non-standard
&& dataBuffer[dataPtr - 2] == (byte) 0xAA;
}
// ---------------------------------------------------------------------------------//
// isEpilogue
// ---------------------------------------------------------------------------------//
private boolean isEpilogue ()
{
return dataPtr >= 3
&& (dataBuffer[dataPtr - 3] == (byte) 0xDE
|| dataBuffer[dataPtr - 3] == (byte) 0xDA) // non-standard
&& dataBuffer[dataPtr - 2] == (byte) 0xAA;
}
// ---------------------------------------------------------------------------------//
// storeSectors
// ---------------------------------------------------------------------------------//
@ -145,143 +286,4 @@ class MC3470
{
return diskSectors.size () == 16 && diskReader.sectorsPerTrack == 16;
}
// ---------------------------------------------------------------------------------//
// checkState
// ---------------------------------------------------------------------------------//
private void checkState (byte value) throws DiskNibbleException
{
switch (value)
{
case (byte) 0xB5:
if (isPrologue ())
{
diskReader = diskReader13;
setState (State.ADDRESS);
}
break;
case (byte) 0x96:
if (isPrologue ())
{
diskReader = diskReader16;
setState (State.ADDRESS);
}
break;
case (byte) 0xAD:
if (isPrologue ())
setState (State.DATA);
break;
case (byte) 0xEB:
if (isEpilogue ())
setState (State.OTHER);
break;
}
if (dataPtr == expectedDataSize)
{
if (currentState == State.OTHER)
throw new DiskNibbleException ("No address or data blocks found");
setState (State.OTHER);
}
}
// ---------------------------------------------------------------------------------//
// setState
// ---------------------------------------------------------------------------------//
private void setState (State newState) throws DiskNibbleException
{
if (currentState == newState && currentState == State.OTHER)
return;
assert currentState != newState : currentState + " -> " + newState;
switch (currentState) // this state is now finished
{
case ADDRESS:
if (currentDiskSector != null)
System.out.printf ("unused ADDRESS: %s%n", currentDiskSector);
currentDiskSector = new RawDiskSector (new DiskAddressField (dataBuffer));
if (dump)
System.out.println (currentDiskSector);
break;
case DATA:
if (currentDiskSector == null)
{
System.out.printf ("cannot store %d DATA no ADDRESS", dataPtr);
if (debug)
System.out.println (HexFormatter.format (dataBuffer, 0, dataPtr));
}
else
{
currentDiskSector.setBuffer (diskReader.decodeSector (dataBuffer));
diskSectors.add (currentDiskSector);
currentDiskSector = null;
if (diskSectors.size () == diskReader.sectorsPerTrack)
finished = true;
}
break;
case OTHER: // triggered by an epilogue or full address/data buffer
break;
}
switch (newState) // this state is now starting
{
case ADDRESS:
if (dump)
System.out.print ("ADDRESS ");
expectedDataSize = 8;
break;
case DATA:
if (dump)
System.out.println ("DATA");
if (debug && currentDiskSector == null)
{
System.out.println ("starting DATA with no ADDRESS");
System.out.println (HexFormatter.format (dataBuffer, 0, dataPtr));
}
expectedDataSize = diskReader.expectedDataSize ();
break;
case OTHER:
if (dump)
System.out.println ("OTHER");
expectedDataSize = MAX_DATA; // what is the maximum filler?
break;
}
currentState = newState;
dataPtr = 0; // start collecting new buffer
}
// ---------------------------------------------------------------------------------//
// isPrologue
// ---------------------------------------------------------------------------------//
private boolean isPrologue ()
{
return dataPtr >= 3
&& (dataBuffer[dataPtr - 3] == (byte) 0xD5
|| dataBuffer[dataPtr - 3] == (byte) 0xD4) // non-standard
&& dataBuffer[dataPtr - 2] == (byte) 0xAA;
}
// ---------------------------------------------------------------------------------//
// isEpilogue
// ---------------------------------------------------------------------------------//
private boolean isEpilogue ()
{
return dataPtr >= 3
&& (dataBuffer[dataPtr - 3] == (byte) 0xDE
|| dataBuffer[dataPtr - 3] == (byte) 0xDA) // non-standard
&& dataBuffer[dataPtr - 2] == (byte) 0xAA;
}
}

View File

@ -37,7 +37,6 @@ abstract class AbstractCatalogEntry implements AppleFileSource
this.disk = dosDisk.getDisk ();
this.catalogSectorDA = catalogSector;
// reportedSize = HexFormatter.intValue (entryBuffer[33], entryBuffer[34]);
reportedSize = HexFormatter.unsignedShort (entryBuffer, 33);
int type = entryBuffer[2] & 0xFF;
locked = (type & 0x80) > 0;
@ -184,16 +183,11 @@ abstract class AbstractCatalogEntry implements AppleFileSource
// buffer is a multiple of the block size, so it usually needs to be reduced
if ((reportedLength + 4) <= buffer.length)
{
exactBuffer = new byte[reportedLength];
// extraBuffer = new byte[buffer.length - reportedLength - 4];
// System.arraycopy (buffer, reportedLength + 4, extraBuffer, 0,
// extraBuffer.length);
}
else
exactBuffer = new byte[buffer.length - 4]; // reported length is too long
System.arraycopy (buffer, 4, exactBuffer, 0, exactBuffer.length);
if ((name.endsWith (".FONT") || name.endsWith (" FONT")
|| name.endsWith (".SET") || name.startsWith ("ASCII."))
&& FontFile.isFont (exactBuffer))
@ -278,9 +272,6 @@ abstract class AbstractCatalogEntry implements AppleFileSource
{
byte[] exactBuffer;
// int loadAddress = HexFormatter.intValue (buffer[0], buffer[1]);
// int loadAddress = HexFormatter.unsignedShort (buffer, 0);
// int reportedLength = HexFormatter.intValue (buffer[2], buffer[3]);
int reportedLength = HexFormatter.unsignedShort (buffer, 2);
if (reportedLength == 0)
{
@ -291,15 +282,11 @@ abstract class AbstractCatalogEntry implements AppleFileSource
// buffer is a multiple of the block size, so it usually needs to be reduced
if ((reportedLength + 4) <= buffer.length)
{
exactBuffer = new byte[reportedLength];
// extraBuffer = new byte[buffer.length - reportedLength - 4];
// System.arraycopy (buffer, reportedLength + 4, extraBuffer, 0,
// extraBuffer.length);
}
else
exactBuffer = new byte[buffer.length - 4]; // reported length is too long
System.arraycopy (buffer, 4, exactBuffer, 0, exactBuffer.length);
return exactBuffer;
}
@ -309,9 +296,6 @@ abstract class AbstractCatalogEntry implements AppleFileSource
&& reportedLength == 0x14FA)
return true;
// if (name.endsWith (".PAC"))
// return true;
if (name.equals ("BBROS LOGO SCRUNCHED") && reportedLength == 0x0FED)
return true;
@ -324,10 +308,13 @@ abstract class AbstractCatalogEntry implements AppleFileSource
for (DiskAddress sector : tsSectors)
if (sector.matches (da))
return true;
for (DiskAddress sector : dataSectors)
// random access files may have gaps, and thus null sectors
// is this still true? I thought I was using sector zero objects??
if (sector != null && sector.matches (da))
return true;
return false;
}

View File

@ -4,6 +4,8 @@ import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.prefs.Preferences;
@ -126,13 +128,37 @@ public class DiskBrowser extends JFrame implements DiskSelectionListener, QuitLi
addQuitListener (catalogPanel);
addQuitListener (this);
Desktop desktop = Desktop.getDesktop ();
desktop.setAboutHandler (e -> JOptionPane.showMessageDialog (null,
"Author - Denis Molony\nGitHub - https://github.com/dmolony/DiskBrowser",
"About DiskBrowser", JOptionPane.INFORMATION_MESSAGE));
// desktop.setPreferencesHandler (
// e -> JOptionPane.showMessageDialog (null, "Preferences dialog"));
desktop.setQuitHandler ( (e, r) -> fireQuitEvent ());
if (Desktop.isDesktopSupported ())
{
Desktop desktop = Desktop.getDesktop ();
if (false)
{
System.out.println ("Enums:");
for (Desktop.Action a : Desktop.Action.values ())
System.out.printf ("%s is%s supported%n", a.toString (),
(desktop.isSupported (a) ? "" : " not"));
}
if (desktop.isSupported (Desktop.Action.APP_ABOUT))
desktop.setAboutHandler (e -> JOptionPane.showMessageDialog (null,
"Author - Denis Molony\nGitHub - https://github.com/dmolony/DiskBrowser",
"About DiskBrowser", JOptionPane.INFORMATION_MESSAGE));
if (desktop.isSupported (Desktop.Action.APP_QUIT_HANDLER))
desktop.setQuitHandler ( (e, r) -> fireQuitEvent ());
else
{
addWindowListener (new WindowAdapter ()
{
@Override
public void windowClosing (WindowEvent e)
{
fireQuitEvent ();
}
});
}
}
else
System.out.println ("Desktop not supported");
catalogPanel.setCloseTabAction (closeTabAction);

View File

@ -354,9 +354,12 @@ class FileEntry extends CatalogEntry implements ProdosConstants
file = new SHRPictureFile2 (name, exactBuffer, fileType, auxType, endOfFile);
break;
case FILE_TYPE_PICT:
System.out.println ("*** PICT : " + name);
file = new DefaultAppleFile (name, exactBuffer);
case FILE_TYPE_FOT:
if (auxType == 0x8066) // Fadden
// file = new DefaultAppleFile (name, exactBuffer);
file = new FaddenHiResImage (name, exactBuffer, fileType, auxType, endOfFile);
else
file = new DefaultAppleFile (name, exactBuffer);
break;
case FILE_TYPE_FONT:
@ -424,6 +427,8 @@ class FileEntry extends CatalogEntry implements ProdosConstants
// Text files with aux (reclen) > 0 are random access, possibly with
// non-contiguous records, so they need to be handled differently
// Graphics files can also have gaps
switch (storageType)
{
case TREE:
@ -529,65 +534,6 @@ class FileEntry extends CatalogEntry implements ProdosConstants
}
}
// should be removed
// private byte[] getGEOSBuffer ()
// {
// switch (storageType)
// {
// case SEEDLING:
// System.out.println ("Seedling GEOS file : " + name); // not sure if possible
// return disk.readSectors (dataBlocks);
// case SAPLING:
// return getIndexFile (keyPtr);
// case TREE:
// return getMasterIndexFile (keyPtr);
// default:
// System.out.println ("Unknown storage type for GEOS file : " + storageType);
// return new byte[512];
// }
// }
// should be removed
// private byte[] getMasterIndexFile (int keyPtr)
// {
// byte[] buffer = disk.readSector (keyPtr);
// int length = HexFormatter.intValue (buffer[0xFF], buffer[0x1FF]);
// byte[] fileBuffer = new byte[length];
// int ptr = 0;
// for (int i = 0; i < 0x80; i++)
// {
// int block = HexFormatter.intValue (buffer[i], buffer[i + 256]);
// if (block == 0)
// break;
// if (block == 0xFFFF) // should this insert 131,072 zeroes?
// continue;
// byte[] temp = getIndexFile (block);
// System.arraycopy (temp, 0, fileBuffer, ptr, temp.length);
// ptr += temp.length;
// }
// return fileBuffer;
// }
// should be removed
// private byte[] getIndexFile (int keyPtr)
// {
// byte[] buffer = disk.readSector (keyPtr);
// int length = HexFormatter.intValue (buffer[0xFF], buffer[0x1FF]);
// byte[] fileBuffer = new byte[length];
// for (int i = 0; i < 0x80; i++)
// {
// int block = HexFormatter.intValue (buffer[i], buffer[i + 256]);
// if (block == 0)
// break;
// if (block == 0xFFFF) // should this insert 512 zeroes?
// continue;
// byte[] temp = disk.readSector (block);
// System.arraycopy (temp, 0, fileBuffer, i * 512, length > 512 ? 512 : length);
// length -= 512;
// }
// return fileBuffer;
// }
private int readIndexBlock (int indexBlock, List<DiskAddress> addresses,
List<TextBuffer> buffers, int logicalBlock)
{
@ -648,11 +594,12 @@ class FileEntry extends CatalogEntry implements ProdosConstants
{
if (ProdosConstants.fileTypes[fileType].equals ("DIR"))
return name;
// String locked = (access == 0x01) ? "*" : " ";
String locked = (access == 0x00) ? "*" : " ";
if (true)
return String.format ("%s %03d %s", ProdosConstants.fileTypes[fileType],
blocksUsed, locked) + name;
String timeC = created == null ? "" : parentDisk.df.format (created.getTime ());
String timeF = modified == null ? "" : parentDisk.df.format (modified.getTime ());
return String.format ("%s %s%-30s %3d %,10d %15s %15s",

View File

@ -4,7 +4,7 @@ public interface ProdosConstants
{
int FILE_TYPE_TEXT = 0x04;
int FILE_TYPE_BINARY = 0x06;
int FILE_TYPE_PICT = 0x08; // was Apple /// FotoFile
int FILE_TYPE_FOT = 0x08; // was Apple /// FotoFile
int FILE_TYPE_DIRECTORY = 0x0F;
int FILE_TYPE_ADB = 0x19;
int FILE_TYPE_AWP = 0x1A;

View File

@ -87,19 +87,12 @@ class ProdosDirectory extends AbstractFile implements ProdosConstants
case FILE_TYPE_BINARY:
case FILE_TYPE_PNT:
case FILE_TYPE_PIC:
case FILE_TYPE_FOT:
aux = HexFormatter.intValue (buffer[i + 31], buffer[i + 32]);
subType = String.format ("A=$%4X", aux);
// if (fileType == FILE_TYPE_PNT && aux == 0)
// System.out.printf ("found $C0/00 %s%n", name);
// if (fileType == FILE_TYPE_PNT && aux == 3)
// System.out.printf ("found $C0/03 %s%n", name);
// if (fileType == FILE_TYPE_PNT && aux == 4)
// System.out.printf ("found $C0/04 %s%n", name);
// if (fileType == FILE_TYPE_PIC && aux == 1)
// System.out.printf ("found $C1/01 %s%n", name);
break;
case 0x1A: // AWP file
case FILE_TYPE_AWP:
aux = HexFormatter.intValue (buffer[i + 32], buffer[i + 31]); // backwards!
if (aux != 0)
filename = convert (filename, aux);