dmolony-DiskBrowser/src/com/bytezone/diskbrowser/disk/DiskFactory.java
2017-03-13 18:16:20 +11:00

585 lines
17 KiB
Java
Executable File

package com.bytezone.diskbrowser.disk;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.GZIPInputStream;
import com.bytezone.diskbrowser.cpm.CPMDisk;
import com.bytezone.diskbrowser.dos.DosDisk;
import com.bytezone.diskbrowser.infocom.InfocomDisk;
import com.bytezone.diskbrowser.pascal.PascalDisk;
import com.bytezone.diskbrowser.prodos.ProdosDisk;
import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.NuFX;
import com.bytezone.diskbrowser.utilities.Utility;
import com.bytezone.diskbrowser.wizardry.Wizardry4BootDisk;
import com.bytezone.diskbrowser.wizardry.WizardryScenarioDisk;
public class DiskFactory
{
private static boolean debug = false;
private DiskFactory ()
{
}
public static FormattedDisk createDisk (File file)
{
return createDisk (file.getAbsolutePath ());
}
public static FormattedDisk createDisk (String path)
{
if (debug)
System.out.println ("\nFactory : " + path);
File file = new File (path);
if (!file.exists ())
return null;
String suffix = path.substring (path.lastIndexOf (".") + 1).toLowerCase ();
Boolean compressed = false;
Path originalPath = Paths.get (path);
if (suffix.equals ("gz"))
{
if (debug)
System.out.println (" ** gzip **");
try
{
InputStream in = new GZIPInputStream (new FileInputStream (path));
File tmp = File.createTempFile ("gzip", null);
FileOutputStream fos = new FileOutputStream (tmp);
int bytesRead;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read (buffer)) > 0)
fos.write (buffer, 0, bytesRead);
fos.close ();
in.close ();
tmp.deleteOnExit ();
suffix = Utility.getSuffix (file.getName ()); // ignores the .gz
file = tmp;
compressed = true;
}
catch (IOException e) // can get EOFException: Unexpected end of ZLIB input stream
{
e.printStackTrace ();
return null;
}
}
if (suffix.equals ("sdk"))
{
if (debug)
System.out.println (" ** sdk **");
try
{
NuFX nuFX = new NuFX (file);
File tmp = File.createTempFile ("sdk", null);
FileOutputStream fos = new FileOutputStream (tmp);
fos.write (nuFX.getBuffer ());
fos.close ();
tmp.deleteOnExit ();
file = tmp;
suffix = "dsk";
compressed = true;
}
catch (IOException e)
{
e.printStackTrace ();
return null;
}
catch (FileFormatException e)
{
return null;
}
}
FormattedDisk disk = null;
FormattedDisk disk2 = null;
if (suffix.equals ("hdv"))
{
if (debug)
System.out.println (" ** hdv **");
ProdosDisk prodosDisk = checkHardDisk (file);
if (prodosDisk != null)
return prodosDisk;
disk2 = check2mgDisk (file);
if (disk2 != null)
return disk2;
AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8);
return new DataDisk (appleDisk);
}
if (suffix.equals ("2mg"))
{
if (debug)
System.out.println (" ** 2mg **");
disk2 = check2mgDisk (file);
if (disk2 != null)
return disk2;
AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8);
return new DataDisk (appleDisk);
}
if (((suffix.equals ("po") || suffix.equals ("dsk")) && file.length () > 143360))
{
if (debug)
System.out.println (" Checking po or dsk hard drive: " + file.length ());
disk = checkHardDisk (file);
if (disk != null)
{
if (compressed)
disk.setOriginalPath (originalPath);
return disk;
}
if (debug)
System.out.println (" Creating a data disk from bad length");
try
{
AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8);
if (debug)
System.out.println (" created data usk");
return new DataDisk (appleDisk);
}
catch (FileFormatException e)
{
if (debug)
System.out.println (" Creating AppleDisk failed");
return null;
}
}
if (suffix.equals ("v2d"))
{
if (debug)
System.out.println (" ** v2d **");
V2dDisk v2dDisk = new V2dDisk (file);
AppleDisk appleDisk16 = new AppleDisk (v2dDisk);
disk = checkDos (appleDisk16);
return disk;
}
if (suffix.equals ("nib"))
{
if (debug)
System.out.println (" ** nib **");
NibDisk nibDisk = new NibDisk (file);
AppleDisk appleDisk16 = new AppleDisk (nibDisk);
disk = checkDos (appleDisk16);
return null;
}
long length = file.length ();
if (length == 116480) // 13 sector disk
{
if (debug)
System.out.println (" ** 13 sector **");
if (!suffix.equals ("d13"))
System.out.printf ("%s should have a d13 suffix%n", file.getName ());
AppleDisk appleDisk = new AppleDisk (file, 35, 13);
disk = checkDos (appleDisk);
return disk == null ? new DataDisk (appleDisk) : disk;
}
if (length != 143360)
{
System.out.printf ("%s: invalid file length : %,d%n", file.getName (),
file.length ());
return null;
}
AppleDisk appleDisk256 = new AppleDisk (file, 35, 16);
AppleDisk appleDisk512 = new AppleDisk (file, 35, 8);
if (true)
{
long checksum = appleDisk256.getBootChecksum ();
if (checksum == 227968344L) // empty boot sector
{
// could be wizardry data, visialc data ...
if (debug)
System.out.println (" empty sector checksum : " + checksum);
}
else if (checksum == 3176296590L //
|| checksum == 108825457L //
|| checksum == 1439356606L //
|| checksum == 1550012074L //
|| checksum == 1614602459L //
|| checksum == 940889336L //
|| checksum == 2936955085L //
|| checksum == 1348415927L //
|| checksum == 3340889101L //
|| checksum == 18315788L //
|| checksum == 993895235L //
|| checksum == 2378342794L) // LazerPascal1.dsk
{
if (debug)
System.out.println (" known DOS checksum : " + checksum);
disk = checkDos (appleDisk256);
disk2 = checkProdos (appleDisk512); // no need for this
if (disk2 != null && disk != null) // should be impossible
{
if (debug)
System.out.println (" --> Dual dos/prodos 1");
System.out.println ("** impossible **");
disk = new DualDosDisk (disk, disk2);
}
}
else if (checksum == 1737448647L //
|| checksum == 170399908L //
|| checksum == 990032697) // Apple Assembly Line
{
if (debug)
System.out.println (" known PRODOS checksum : " + checksum);
disk = checkProdos (appleDisk512);
disk2 = checkDos (appleDisk256);
if (disk2 != null && disk != null)
{
if (debug)
System.out.println (" --> Dual prodos/dos 2");
disk = new DualDosDisk (disk, disk2);
}
}
else if (checksum == 2803644711L // Apple Pascal disk 0
|| checksum == 3317783349L //
|| checksum == 1728863694L // Wizardry_I_boot.dsk
|| checksum == 198094178L) //
{
if (debug)
System.out.println (" known PASCAL checksum : " + checksum);
disk = checkPascalDisk (appleDisk512);
}
else if (checksum == 3028642627L //
|| checksum == 2070151659L) // Enchanter
{
if (debug)
System.out.println (" known INFOCOM checksum : " + checksum);
disk = checkInfocomDisk (appleDisk256);
}
else if (debug)
System.out.println (" unknown checksum : " + checksum);
// else if (checksum == 1212926910L || checksum == 1365043894L
// || checksum == 2128073918L)
// disk = checkCPMDisk (file);
// System.out.println (checksum);
if (disk != null)
{
if (compressed)
disk.setOriginalPath (originalPath);
return disk;
}
// empty boot sector
if (checksum != 227968344L && false)
System.out.println ("Unknown checksum : " + checksum + " : " + path);
}
if (debug)
System.out.println (" checksum no help");
if (debug)
System.out.println (" Suffix : " + suffix);
if (suffix.equals ("dsk") || suffix.equals ("do"))
{
disk = checkDos (appleDisk256);
if (disk == null)
disk = checkProdos (appleDisk512);
else
{
if (debug)
System.out.println ("Checking DualDos disk");
disk2 = checkProdos (appleDisk512);
if (disk2 != null)
disk = new DualDosDisk (disk, disk2);
}
}
else if (suffix.equals ("po"))
{
disk = checkProdos (appleDisk512);
if (disk == null)
disk = checkDos (appleDisk256);
}
if (disk == null)
disk = checkPascalDisk (appleDisk512);
if (disk == null)
disk = checkCPMDisk (appleDisk256);
if (disk == null)
{
disk2 = checkInfocomDisk (appleDisk256);
if (disk2 != null)
disk = disk2;
}
if (disk == null)
disk = new DataDisk (appleDisk256);
if (debug)
System.out.println (
"Factory creating disk : " + disk.getDisk ().getFile ().getAbsolutePath ());
if (disk != null && compressed)
disk.setOriginalPath (originalPath);
return disk;
}
private static DosDisk checkDos (AppleDisk disk)
{
if (debug)
System.out.println ("Checking DOS disk");
try
{
if (DosDisk.isCorrectFormat (disk))
{
if (debug)
System.out.println (" --> DOS");
return new DosDisk (disk);
}
}
catch (Exception e)
{
}
if (debug)
System.out.println (" not a DOS disk");
return null;
}
private static ProdosDisk checkProdos (AppleDisk disk)
{
if (debug)
System.out.println ("Checking Prodos disk");
try
{
if (ProdosDisk.isCorrectFormat (disk))
{
if (debug)
System.out.println (" --> PRODOS");
return new ProdosDisk (disk);
}
}
catch (Exception e)
{
}
if (debug)
System.out.println (" not a Prodos disk");
return null;
}
private static ProdosDisk checkHardDisk (File file)
{
if (debug)
{
System.out.println ("\nChecking Prodos hard disk");
System.out.printf ("Total blocks : %f%n", (float) file.length () / 512);
System.out.printf ("Total tracks : %f%n", (float) file.length () / 4096);
System.out.printf ("File length : %d%n", file.length ());
System.out.println ();
}
// assumes a sector is 512 bytes
if ((file.length () % 512) != 0)
{
if (debug)
System.out.printf ("file length not divisible by 512 : %,d%n%n", file.length ());
return null;
}
try
{
// truncate the file if necessary
AppleDisk disk = new AppleDisk (file, (int) file.length () / 4096, 8);
if (ProdosDisk.isCorrectFormat (disk))
{
if (debug)
System.out.println (" --> PRODOS hard disk");
return new ProdosDisk (disk);
}
}
catch (Exception e)
{
System.out.println (e);
}
if (debug)
System.out.println (" not a Prodos hard disk\n");
return null;
}
/*
offset | size | description
------ | ---- | -----------
+$000 | Long | The integer constant '2IMG'. This integer should be little-endian, so on the Apple IIgs, this is equivalent to the four characters 'GMI2'; in ORCA/C 2.1, you can use the integer constant '2IMG'.
+$004 | Long | A four-character tag identifying the application that created the file.
+$008 | Word | The length of this header, in bytes. Should be 52.
+$00A | Word | The version number of the image file format. Should be 1.
+$00C | Long | The image format. See table below.
+$010 | Long | Flags. See table below.
+$014 | Long | The number of 512-byte blocks in the disk image. This value should be zero unless the image format is 1 (ProDOS order).
+$018 | Long | Offset to the first byte of the first block of the disk in the image file, from the beginning of the file. The disk data must come before the comment and creator-specific chunks.
+$01C | Long | Length of the disk data in bytes. This should be the number of blocks * 512.
+$020 | Long | Offset to the first byte of the image comment. Can be zero if there's no comment. The comment must come after the data chunk, but before the creator-specific chunk. The comment, if it exists, should be raw text; no length byte or C-style null terminator byte is required (that's what the next field is for).
+$024 | Long | Length of the comment chunk. Zero if there's no comment.
+$028 | Long | Offset to the first byte of the creator-specific data chunk, or zero if there is none.
+$02C | Long | Length of the creator-specific chunk; zero if there is no creator-specific data.
+$030 | 16 bytes | Reserved space; this pads the header to 64 bytes. These values must all be zero.
*/
private static FormattedDisk check2mgDisk (File file)
{
if (debug)
System.out.println ("Checking Prodos 2mg disk");
try
{
AppleDisk disk = new AppleDisk (file, 0, 0);
if (ProdosDisk.isCorrectFormat (disk))
return new ProdosDisk (disk);
}
catch (Exception e)
{
}
if (debug)
System.out.println ("Not a Prodos 2mg disk");
return null;
}
private static FormattedDisk checkPascalDisk (AppleDisk disk)
{
if (debug)
System.out.println ("Checking Pascal disk");
File file = disk.getFile ();
if (!PascalDisk.isCorrectFormat (disk, debug))
return null;
if (debug)
System.out.println ("Pascal disk OK - Checking Wizardry disk");
if (WizardryScenarioDisk.isWizardryFormat (disk, debug))
return new WizardryScenarioDisk (disk);
if (debug)
System.out.println ("Not a Wizardry 1-3 disk");
// check for compressed disk
if (file.getName ().endsWith (".tmp"))
return new PascalDisk (disk); // complicated joining up compressed disks
if (Wizardry4BootDisk.isWizardryIVorV (disk, debug))
{
String fileName = file.getAbsolutePath ().toLowerCase ();
int pos = file.getAbsolutePath ().indexOf ('.');
char c = fileName.charAt (pos - 1);
String suffix = fileName.substring (pos + 1);
int requiredDisks = c == '1' ? 6 : c == 'a' ? 10 : 0;
if (requiredDisks > 0)
{
// collect extra data disks
AppleDisk[] disks = new AppleDisk[requiredDisks];
disks[0] = new AppleDisk (file, 256, 8); // will become a PascalDisk
disks[0].setInterleave (1);
disks[1] = new AppleDisk (file, 256, 8); // will remain a DataDisk
disks[1].setInterleave (1);
if (pos > 0 && requiredDisks > 0)
{
if (collectDataDisks (file.getAbsolutePath (), pos, disks))
return new Wizardry4BootDisk (disks);
}
}
}
if (debug)
System.out.println ("Not a Wizardry IV disk");
PascalDisk pascalDisk = new PascalDisk (disk);
return pascalDisk;
}
private static boolean collectDataDisks (String fileName, int dotPos, AppleDisk[] disks)
{
char c = fileName.charAt (dotPos - 1);
String suffix = fileName.substring (dotPos + 1);
for (int i = 2; i < disks.length; i++)
{
String old = new String (c + "." + suffix);
String rep = new String ((char) (c + i - 1) + "." + suffix);
File f = new File (fileName.replace (old, rep));
if (!f.exists () || !f.isFile ())
return false;
AppleDisk dataDisk = new AppleDisk (f, 35, 8);
dataDisk.setInterleave (1);
disks[i] = dataDisk;
}
return true;
}
private static InfocomDisk checkInfocomDisk (AppleDisk disk)
{
if (debug)
System.out.println ("Checking Infocom disk");
if (InfocomDisk.isCorrectFormat (disk))
{
if (debug)
System.out.println (" --> INFOCOM");
return new InfocomDisk (disk);
}
if (debug)
System.out.println ("Not an InfocomDisk disk");
return null;
}
private static CPMDisk checkCPMDisk (AppleDisk disk)
{
if (debug)
System.out.println ("Checking CPM disk");
if (CPMDisk.isCorrectFormat (disk))
return new CPMDisk (disk);
if (debug)
System.out.println ("Not a CPM disk");
return null;
}
}