2002-12-01 02:21:00 +00:00
|
|
|
/*
|
|
|
|
* AppleCommander - An Apple ][ image utility.
|
|
|
|
* Copyright (C) 2002 by Robert Greene
|
|
|
|
* robgreene at users.sourceforge.net
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
package com.webcodepro.applecommander.storage;
|
|
|
|
|
|
|
|
import java.io.ByteArrayOutputStream;
|
2002-12-09 05:44:12 +00:00
|
|
|
import java.io.File;
|
2002-12-01 02:21:00 +00:00
|
|
|
import java.io.FileInputStream;
|
2002-12-09 05:44:12 +00:00
|
|
|
import java.io.FileOutputStream;
|
2002-12-01 02:21:00 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
2002-12-09 05:44:12 +00:00
|
|
|
import java.io.OutputStream;
|
2002-12-01 02:21:00 +00:00
|
|
|
import java.util.zip.GZIPInputStream;
|
2002-12-09 05:44:12 +00:00
|
|
|
import java.util.zip.GZIPOutputStream;
|
2002-12-01 02:21:00 +00:00
|
|
|
|
2004-06-09 03:58:00 +00:00
|
|
|
import com.webcodepro.applecommander.storage.os.cpm.CpmFileEntry;
|
|
|
|
import com.webcodepro.applecommander.storage.os.cpm.CpmFormatDisk;
|
|
|
|
import com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk;
|
|
|
|
import com.webcodepro.applecommander.storage.os.dos33.OzDosFormatDisk;
|
|
|
|
import com.webcodepro.applecommander.storage.os.dos33.UniDosFormatDisk;
|
2010-08-09 06:40:08 +00:00
|
|
|
import com.webcodepro.applecommander.storage.os.nakedos.NakedosFormatDisk;
|
2008-12-26 20:36:47 +00:00
|
|
|
import com.webcodepro.applecommander.storage.os.gutenberg.GutenbergFormatDisk;
|
2004-06-09 03:58:00 +00:00
|
|
|
import com.webcodepro.applecommander.storage.os.pascal.PascalFormatDisk;
|
|
|
|
import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
|
|
|
|
import com.webcodepro.applecommander.storage.os.rdos.RdosFormatDisk;
|
|
|
|
import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout;
|
|
|
|
import com.webcodepro.applecommander.storage.physical.DosOrder;
|
|
|
|
import com.webcodepro.applecommander.storage.physical.ImageOrder;
|
|
|
|
import com.webcodepro.applecommander.storage.physical.NibbleOrder;
|
|
|
|
import com.webcodepro.applecommander.storage.physical.ProdosOrder;
|
|
|
|
import com.webcodepro.applecommander.storage.physical.UniversalDiskImageLayout;
|
|
|
|
import com.webcodepro.applecommander.util.AppleUtil;
|
2004-06-20 17:01:20 +00:00
|
|
|
import com.webcodepro.applecommander.util.StreamUtil;
|
2004-07-11 15:07:56 +00:00
|
|
|
import com.webcodepro.applecommander.util.TextBundle;
|
2004-06-09 03:58:00 +00:00
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Abstract representation of an Apple2 disk (floppy, 800k, hard disk).
|
|
|
|
* <p>
|
|
|
|
* Date created: Oct 3, 2002 10:59:47 PM
|
2004-06-18 05:15:33 +00:00
|
|
|
* @author Rob Greene
|
2002-12-01 02:21:00 +00:00
|
|
|
*/
|
|
|
|
public class Disk {
|
|
|
|
/**
|
|
|
|
* Specifies a filter to be used in determining filetypes which are supported.
|
|
|
|
* This works from a file extension, so it may or may not apply to the Macintosh.
|
|
|
|
*/
|
|
|
|
public class FilenameFilter {
|
|
|
|
private String names;
|
2017-11-18 05:34:49 +00:00
|
|
|
private String[] extensions;
|
|
|
|
public FilenameFilter(String names, String... extensions) {
|
2002-12-01 02:21:00 +00:00
|
|
|
this.names = names;
|
|
|
|
this.extensions = extensions;
|
|
|
|
}
|
|
|
|
public String getExtensions() {
|
2017-11-18 05:34:49 +00:00
|
|
|
return String.join(";", extensions);
|
|
|
|
}
|
|
|
|
public String[] getExtensionList() {
|
2002-12-01 02:21:00 +00:00
|
|
|
return extensions;
|
|
|
|
}
|
|
|
|
public String getNames() {
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
}
|
2012-07-31 21:06:01 +00:00
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
public static final int BLOCK_SIZE = 512;
|
|
|
|
public static final int SECTOR_SIZE = 256;
|
2004-06-03 03:15:32 +00:00
|
|
|
public static final int PRODOS_BLOCKS_ON_140KB_DISK = 280;
|
|
|
|
public static final int DOS33_SECTORS_ON_140KB_DISK = 560;
|
2002-12-01 02:21:00 +00:00
|
|
|
public static final int APPLE_140KB_DISK = 143360;
|
2003-12-26 21:41:14 +00:00
|
|
|
public static final int APPLE_140KB_NIBBLE_DISK = 232960;
|
2002-12-01 02:21:00 +00:00
|
|
|
public static final int APPLE_800KB_DISK = 819200;
|
2008-06-04 16:08:15 +00:00
|
|
|
public static final int APPLE_800KB_2IMG_DISK =
|
|
|
|
APPLE_800KB_DISK + UniversalDiskImageLayout.OFFSET;
|
2002-12-12 01:45:04 +00:00
|
|
|
public static final int APPLE_5MB_HARDDISK = 5242880;
|
|
|
|
public static final int APPLE_10MB_HARDDISK = 10485760;
|
|
|
|
public static final int APPLE_20MB_HARDDISK = 20971520;
|
|
|
|
public static final int APPLE_32MB_HARDDISK = 33553920; // short one block!
|
2002-12-01 02:21:00 +00:00
|
|
|
|
|
|
|
private static FilenameFilter[] filenameFilters;
|
2008-06-08 03:02:53 +00:00
|
|
|
private static String[] allFileExtensions = null;
|
2004-07-11 15:07:56 +00:00
|
|
|
private TextBundle textBundle = StorageBundle.getInstance();
|
2002-12-01 02:21:00 +00:00
|
|
|
private String filename;
|
2003-12-08 00:50:06 +00:00
|
|
|
private boolean newImage = false;
|
2012-08-02 03:18:07 +00:00
|
|
|
private boolean isDC42 = false;
|
2003-12-28 20:42:51 +00:00
|
|
|
private ByteArrayImageLayout diskImageManager;
|
2012-07-06 21:14:54 +00:00
|
|
|
private ImageOrder imageOrder = null;
|
2012-07-06 16:45:49 +00:00
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Get the supported file filters supported by the Disk interface.
|
2019-10-07 00:40:19 +00:00
|
|
|
* This is due to the fact that FilenameFilter is an inner class of Disk -
|
2002-12-01 02:21:00 +00:00
|
|
|
* without an instance of the class, the filters cannot be created.
|
|
|
|
*/
|
|
|
|
public static FilenameFilter[] getFilenameFilters() {
|
|
|
|
if (filenameFilters == null) {
|
|
|
|
new Disk();
|
|
|
|
}
|
|
|
|
return filenameFilters;
|
|
|
|
}
|
2012-07-06 16:45:49 +00:00
|
|
|
|
2008-06-08 03:02:53 +00:00
|
|
|
/**
|
|
|
|
* Get the supported file extensions supported by the Disk interface.
|
|
|
|
* This is used by the Swing UI to populate the open file dialog box.
|
|
|
|
*/
|
|
|
|
public static String[] getAllExtensions() {
|
|
|
|
if (allFileExtensions == null) {
|
|
|
|
new Disk();
|
|
|
|
}
|
|
|
|
return allFileExtensions;
|
|
|
|
}
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Constructor for a Disk - used only to generate FilenameFilter objects.
|
|
|
|
*/
|
|
|
|
private Disk() {
|
|
|
|
filenameFilters = new FilenameFilter[] {
|
2004-07-11 15:07:56 +00:00
|
|
|
new FilenameFilter(textBundle.get("Disk.AllImages"), //$NON-NLS-1$
|
2017-11-18 05:34:49 +00:00
|
|
|
"*.do", "*.dsk", "*.po", "*.nib", "*.2mg", "*.2img", "*.hdv", "*.do.gz", "*.dsk.gz", "*.po.gz", "*.nib.gz", "*.2mg.gz", "*.2img.gz"), //$NON-NLS-1$
|
2004-07-11 15:07:56 +00:00
|
|
|
new FilenameFilter(textBundle.get("Disk.140kDosImages"), //$NON-NLS-1$
|
2017-11-18 05:34:49 +00:00
|
|
|
"*.do", "*.dsk", "*.do.gz", "*.dsk.gz"), //$NON-NLS-1$
|
2004-07-11 15:07:56 +00:00
|
|
|
new FilenameFilter(textBundle.get("Disk.140kNibbleImages"), //$NON-NLS-1$
|
2017-11-18 05:34:49 +00:00
|
|
|
"*.nib", "*.nib.gz"), //$NON-NLS-1$
|
2004-07-11 15:07:56 +00:00
|
|
|
new FilenameFilter(textBundle.get("Disk.140kProdosImages"), //$NON-NLS-1$
|
2017-11-18 05:34:49 +00:00
|
|
|
"*.po", "*.po.gz"), //$NON-NLS-1$
|
2004-07-11 15:07:56 +00:00
|
|
|
new FilenameFilter(textBundle.get("Disk.800kProdosImages"), //$NON-NLS-1$
|
2017-11-18 05:34:49 +00:00
|
|
|
"*.2mg", "*.2img", "*.2mg.gz", "*.2img.gz"), //$NON-NLS-1$
|
2004-07-11 15:07:56 +00:00
|
|
|
new FilenameFilter(textBundle.get("Disk.ApplePcImages"), //$NON-NLS-1$
|
|
|
|
"*.hdv"), //$NON-NLS-1$
|
|
|
|
new FilenameFilter(textBundle.get("Disk.CompressedImages"), //$NON-NLS-1$
|
2017-11-18 05:34:49 +00:00
|
|
|
"*.sdk", "*.shk", "*.do.gz", "*.dsk.gz", "*.po.gz", "*.2mg.gz", "*.2img.gz"), //$NON-NLS-1$
|
2004-07-11 15:07:56 +00:00
|
|
|
new FilenameFilter(textBundle.get("Disk.AllFiles"), //$NON-NLS-1$
|
|
|
|
"*.*") //$NON-NLS-1$
|
2002-12-01 02:21:00 +00:00
|
|
|
};
|
2008-06-08 03:02:53 +00:00
|
|
|
allFileExtensions = new String[] {
|
2017-11-18 05:34:49 +00:00
|
|
|
".do", //$NON-NLS-1$
|
|
|
|
".dsk", //$NON-NLS-1$
|
|
|
|
".po", //$NON-NLS-1$
|
|
|
|
".nib", //$NON-NLS-1$
|
|
|
|
".sdk", //$NON-NLS-1$
|
|
|
|
".shk", //$NON-NLS-1$
|
|
|
|
".2mg", //$NON-NLS-1$
|
|
|
|
".2img", //$NON-NLS-1$
|
|
|
|
".hdv", //$NON-NLS-1$
|
|
|
|
".do.gz", //$NON-NLS-1$
|
|
|
|
".dsk.gz", //$NON-NLS-1$
|
|
|
|
".po.gz", //$NON-NLS-1$
|
|
|
|
".nib.gz", //$NON-NLS-1$
|
|
|
|
".2mg.gz", //$NON-NLS-1$
|
|
|
|
".2img.gz" //$NON-NLS-1$
|
2008-06-08 03:02:53 +00:00
|
|
|
};
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2012-07-06 16:45:49 +00:00
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Construct a Disk with the given byte array.
|
|
|
|
*/
|
2012-10-25 00:33:53 +00:00
|
|
|
|
|
|
|
protected Disk(String filename, ImageOrder imageOrder) {
|
2003-12-26 21:41:14 +00:00
|
|
|
this.imageOrder = imageOrder;
|
2002-12-01 02:21:00 +00:00
|
|
|
this.filename = filename;
|
2003-12-08 00:50:06 +00:00
|
|
|
this.newImage = true;
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2012-07-06 16:45:49 +00:00
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Construct a Disk and load the specified file.
|
|
|
|
* Read in the entire contents of the file.
|
|
|
|
*/
|
|
|
|
public Disk(String filename) throws IOException {
|
2012-10-25 00:33:53 +00:00
|
|
|
this(filename, 0, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a Disk and load the specified file.
|
|
|
|
* Read in the entire contents of the file.
|
|
|
|
*/
|
|
|
|
public Disk(String filename, boolean knownProDOSOrder) throws IOException {
|
|
|
|
this(filename, 0, knownProDOSOrder);
|
2012-08-12 17:30:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a Disk and load the specified file.
|
|
|
|
* Read in the entire contents of the file.
|
|
|
|
*/
|
|
|
|
public Disk(String filename, int startBlocks) throws IOException {
|
2012-10-25 00:33:53 +00:00
|
|
|
this(filename, startBlocks, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a Disk and load the specified file.
|
|
|
|
* Read in the entire contents of the file.
|
|
|
|
*/
|
|
|
|
public Disk(String filename, int startBlocks, boolean knownProDOSOrder) throws IOException {
|
2002-12-01 02:21:00 +00:00
|
|
|
this.filename = filename;
|
2012-07-06 16:45:49 +00:00
|
|
|
int diskSize = 0;
|
|
|
|
byte[] diskImage = null;
|
2012-08-01 21:35:54 +00:00
|
|
|
byte[] diskImageDC42 = null;
|
2012-07-06 21:14:54 +00:00
|
|
|
|
2012-08-11 20:57:44 +00:00
|
|
|
if (isSDK() || isSHK() || isBXY()) {
|
2012-07-06 21:14:54 +00:00
|
|
|
// If we have an SDK, unpack it and send along the byte array
|
2012-08-02 14:18:25 +00:00
|
|
|
// If we have a SHK, build a new disk and unpack the contents on to it
|
2018-03-09 03:31:22 +00:00
|
|
|
diskImage = com.webcodepro.applecommander.util.ShrinkItUtilities.unpackSHKFile(filename, startBlocks);
|
2012-07-31 21:06:01 +00:00
|
|
|
diskSize = diskImage.length;
|
2012-08-02 14:18:25 +00:00
|
|
|
// Since we don't want to overwrite their shrinkit with a raw ProDOS image,
|
|
|
|
// add a .po extension to it
|
|
|
|
this.filename += ".po"; //$NON-NLS-1$
|
2012-07-06 16:45:49 +00:00
|
|
|
} else {
|
2012-07-06 21:14:54 +00:00
|
|
|
File file = new File(filename);
|
2012-07-06 16:45:49 +00:00
|
|
|
diskSize = (int) file.length();
|
|
|
|
InputStream input = new FileInputStream(file);
|
|
|
|
if (isCompressed()) {
|
|
|
|
input = new GZIPInputStream(input);
|
|
|
|
}
|
|
|
|
ByteArrayOutputStream diskImageByteArray = new ByteArrayOutputStream(diskSize);
|
|
|
|
StreamUtil.copy(input, diskImageByteArray);
|
|
|
|
diskImage = diskImageByteArray.toByteArray();
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2012-08-02 03:18:07 +00:00
|
|
|
boolean is2img = false;
|
2012-07-06 16:45:49 +00:00
|
|
|
/* Does it have the 2IMG header? */
|
2012-08-02 03:18:07 +00:00
|
|
|
if ((diskImage[0] == 0x32) && (diskImage[1] == 0x49) && (diskImage[2] == 0x4D) && (diskImage[3]) == 0x47) {
|
2012-07-06 16:45:49 +00:00
|
|
|
is2img = true;
|
2012-08-01 21:35:54 +00:00
|
|
|
}
|
|
|
|
/* Does it have the DiskCopy 4.2 header? */
|
2012-08-02 03:18:07 +00:00
|
|
|
else if (Disk.isDC42(diskImage)) {
|
2012-08-01 21:35:54 +00:00
|
|
|
isDC42 = true;
|
|
|
|
long end = AppleUtil.getLongValue(diskImage,0x40);
|
|
|
|
if (end < diskImage.length - 83) {
|
|
|
|
diskImageDC42 = new byte[(int)end];
|
2012-08-02 14:18:25 +00:00
|
|
|
System.arraycopy(diskImage, 84, diskImageDC42, 0, (int)end); // 84 bytes into the DC42 stream is where the real data starts
|
2012-08-01 21:35:54 +00:00
|
|
|
diskImageManager = new ByteArrayImageLayout(diskImageDC42);
|
2012-08-02 14:18:25 +00:00
|
|
|
// Since we don't want to overwrite their dmg or dc42 with a raw ProDOS image,
|
|
|
|
// add a .po extension to it
|
|
|
|
this.filename += ".po"; //$NON-NLS-1$
|
2012-08-01 21:35:54 +00:00
|
|
|
}
|
|
|
|
else
|
2012-08-02 03:18:07 +00:00
|
|
|
throw new IllegalArgumentException(textBundle.get("CommandLineDC42Bad")); //$NON-NLS-1$
|
2012-08-01 21:35:54 +00:00
|
|
|
}
|
2012-08-02 03:18:07 +00:00
|
|
|
if (is2img == true || diskImage.length == APPLE_800KB_DISK + UniversalDiskImageLayout.OFFSET
|
|
|
|
|| diskImage.length == APPLE_5MB_HARDDISK + UniversalDiskImageLayout.OFFSET
|
|
|
|
|| diskImage.length == APPLE_10MB_HARDDISK + UniversalDiskImageLayout.OFFSET
|
|
|
|
|| diskImage.length == APPLE_20MB_HARDDISK + UniversalDiskImageLayout.OFFSET
|
|
|
|
|| diskImage.length == APPLE_32MB_HARDDISK + UniversalDiskImageLayout.OFFSET) {
|
2003-12-26 21:41:14 +00:00
|
|
|
diskImageManager = new UniversalDiskImageLayout(diskImage);
|
2012-08-01 21:35:54 +00:00
|
|
|
} else if (isDC42) {
|
|
|
|
diskImageManager = new ByteArrayImageLayout(diskImageDC42);
|
2003-12-26 21:41:14 +00:00
|
|
|
} else {
|
|
|
|
diskImageManager = new ByteArrayImageLayout(diskImage);
|
|
|
|
}
|
2012-07-06 16:45:49 +00:00
|
|
|
|
|
|
|
ImageOrder dosOrder = new DosOrder(diskImageManager);
|
|
|
|
ImageOrder proDosOrder = new ProdosOrder(diskImageManager);
|
|
|
|
|
2012-07-06 21:14:54 +00:00
|
|
|
if (isSDK()) {
|
|
|
|
imageOrder = proDosOrder; // SDKs are always in ProDOS order
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* First step: test physical disk orders for viable file systems.
|
|
|
|
*/
|
|
|
|
int rc = -1;
|
|
|
|
if (diskSize == APPLE_140KB_DISK) {
|
|
|
|
// First, test the really-really likely orders/formats for
|
|
|
|
// 5-1/4" disks.
|
|
|
|
imageOrder = dosOrder;
|
2012-10-25 00:33:53 +00:00
|
|
|
if ((isProdosFormat() || isDosFormat()) && !knownProDOSOrder) {
|
2012-07-06 16:45:49 +00:00
|
|
|
rc = 0;
|
2012-07-06 21:14:54 +00:00
|
|
|
} else {
|
|
|
|
imageOrder = proDosOrder;
|
2012-10-25 00:33:53 +00:00
|
|
|
if (knownProDOSOrder || isProdosFormat() || isDosFormat()) {
|
2012-07-06 21:14:54 +00:00
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
}
|
2012-10-09 13:36:37 +00:00
|
|
|
if (rc == -1) {
|
|
|
|
/*
|
|
|
|
* Check filenames for something deterministic.
|
|
|
|
*/
|
|
|
|
if (isProdosOrder() || is2ImgOrder()) {
|
|
|
|
imageOrder = proDosOrder;
|
|
|
|
rc = 0;
|
|
|
|
} else if (isDosOrder()) {
|
|
|
|
imageOrder = dosOrder;
|
|
|
|
rc = 0;
|
|
|
|
} else if (isNibbleOrder()) {
|
|
|
|
imageOrder = new NibbleOrder(diskImageManager);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
}
|
2012-07-06 21:14:54 +00:00
|
|
|
if (rc == -1) {
|
|
|
|
/*
|
|
|
|
* Ok, it's not one of those. Now, let's go back to DOS
|
|
|
|
* order, and see if we recognize other things. If not,
|
|
|
|
* we'll fall through to other processing later.
|
|
|
|
*/
|
|
|
|
imageOrder = dosOrder;
|
|
|
|
rc = testImageOrder();
|
2012-07-06 16:45:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rc == -1) {
|
2012-07-06 21:14:54 +00:00
|
|
|
imageOrder = proDosOrder;
|
2012-07-06 16:45:49 +00:00
|
|
|
rc = testImageOrder();
|
2012-07-06 21:14:54 +00:00
|
|
|
if (rc == -1) {
|
|
|
|
/*
|
|
|
|
* Couldn't find anything recognizable. Final step:
|
|
|
|
* just punt and start testing filenames.
|
|
|
|
*/
|
|
|
|
if (isProdosOrder() || is2ImgOrder()) {
|
|
|
|
imageOrder = proDosOrder;
|
|
|
|
} else if (isDosOrder()) {
|
|
|
|
imageOrder = dosOrder;
|
|
|
|
} else if (isNibbleOrder()) {
|
|
|
|
imageOrder = new NibbleOrder(diskImageManager);
|
|
|
|
} else {
|
|
|
|
imageOrder = proDosOrder;
|
|
|
|
}
|
2012-07-06 16:45:49 +00:00
|
|
|
}
|
|
|
|
}
|
2003-12-26 21:41:14 +00:00
|
|
|
}
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2002-12-09 05:44:12 +00:00
|
|
|
|
2012-07-06 16:45:49 +00:00
|
|
|
/**
|
|
|
|
* Test the image order to see if we can recognize a file system. Returns: 0
|
|
|
|
* on recognition; -1 on failure.
|
|
|
|
*/
|
|
|
|
public int testImageOrder()
|
|
|
|
{
|
|
|
|
int rc = (true == isProdosFormat() ? 1 : 0) + (true == isDosFormat() ? 2 : 0) + (true == isCpmFormat() ? 4 : 0) + (true == isUniDosFormat() ? 8 : 0) + (true == isPascalFormat() ? 16 : 0) + (true == isOzDosFormat() ? 32 : 0);
|
|
|
|
if (rc == 0)
|
|
|
|
rc = -1;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2002-12-09 05:44:12 +00:00
|
|
|
/**
|
|
|
|
* Save a Disk image to its file.
|
|
|
|
*/
|
|
|
|
public void save() throws IOException {
|
|
|
|
File file = new File(getFilename());
|
|
|
|
if (!file.exists()) {
|
|
|
|
file.createNewFile();
|
|
|
|
}
|
|
|
|
OutputStream output = new FileOutputStream(file);
|
|
|
|
if (isCompressed()) {
|
|
|
|
output = new GZIPOutputStream(output);
|
|
|
|
}
|
2003-12-26 21:41:14 +00:00
|
|
|
output.write(getDiskImageManager().getDiskImage());
|
2002-12-09 05:44:12 +00:00
|
|
|
output.close();
|
2003-12-26 21:41:14 +00:00
|
|
|
getDiskImageManager().setChanged(false);
|
2003-12-08 00:50:06 +00:00
|
|
|
newImage = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save a Disk image as a new/different file.
|
|
|
|
*/
|
|
|
|
public void saveAs(String filename) throws IOException {
|
|
|
|
this.filename = filename;
|
|
|
|
save();
|
2002-12-09 05:44:12 +00:00
|
|
|
}
|
2002-12-01 02:21:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine type of disk, and return the appropriate
|
2018-01-08 11:21:45 +00:00
|
|
|
* FormattedDisk object. Throws an Exception if none is recognized.
|
|
|
|
* @throws DiskUnrecognizedException
|
2002-12-01 02:21:00 +00:00
|
|
|
*/
|
2018-01-08 11:21:45 +00:00
|
|
|
public FormattedDisk[] getFormattedDisks() throws DiskUnrecognizedException {
|
2002-12-01 02:21:00 +00:00
|
|
|
if (isProdosFormat()) {
|
2002-12-14 05:39:41 +00:00
|
|
|
return new FormattedDisk[]
|
2003-12-26 21:41:14 +00:00
|
|
|
{ new ProdosFormatDisk(filename, imageOrder) };
|
2002-12-14 05:39:41 +00:00
|
|
|
} else if (isUniDosFormat()) {
|
|
|
|
return new FormattedDisk[] {
|
2003-12-26 21:41:14 +00:00
|
|
|
new UniDosFormatDisk(filename, imageOrder,
|
2002-12-14 05:39:41 +00:00
|
|
|
UniDosFormatDisk.UNIDOS_DISK_1),
|
2003-12-26 21:41:14 +00:00
|
|
|
new UniDosFormatDisk(filename, imageOrder,
|
2002-12-14 05:39:41 +00:00
|
|
|
UniDosFormatDisk.UNIDOS_DISK_2) };
|
2002-12-17 05:24:19 +00:00
|
|
|
} else if (isOzDosFormat()) {
|
|
|
|
return new FormattedDisk[] {
|
2003-12-26 21:41:14 +00:00
|
|
|
new OzDosFormatDisk(filename, imageOrder,
|
2002-12-17 05:24:19 +00:00
|
|
|
OzDosFormatDisk.OZDOS_DISK_1),
|
2003-12-26 21:41:14 +00:00
|
|
|
new OzDosFormatDisk(filename, imageOrder,
|
2002-12-17 05:24:19 +00:00
|
|
|
OzDosFormatDisk.OZDOS_DISK_2) };
|
2002-12-01 02:21:00 +00:00
|
|
|
} else if (isDosFormat()) {
|
2002-12-14 05:39:41 +00:00
|
|
|
return new FormattedDisk[]
|
2003-12-26 21:41:14 +00:00
|
|
|
{ new DosFormatDisk(filename, imageOrder) };
|
2010-08-09 06:40:08 +00:00
|
|
|
} else if (isNakedosFormat()) {
|
|
|
|
return new FormattedDisk[]
|
|
|
|
{ new NakedosFormatDisk(filename, imageOrder) };
|
2002-12-01 02:21:00 +00:00
|
|
|
} else if (isPascalFormat()) {
|
2002-12-14 05:39:41 +00:00
|
|
|
return new FormattedDisk[]
|
2003-12-26 21:41:14 +00:00
|
|
|
{ new PascalFormatDisk(filename, imageOrder) };
|
2002-12-01 02:21:00 +00:00
|
|
|
} else if (isRdosFormat()) {
|
2002-12-14 05:39:41 +00:00
|
|
|
return new FormattedDisk[]
|
2003-12-26 21:41:14 +00:00
|
|
|
{ new RdosFormatDisk(filename, imageOrder) };
|
2003-12-22 04:41:50 +00:00
|
|
|
} else if (isCpmFormat()) {
|
|
|
|
return new FormattedDisk[]
|
2003-12-26 21:41:14 +00:00
|
|
|
{ new CpmFormatDisk(filename, imageOrder) };
|
2008-12-26 20:36:47 +00:00
|
|
|
} else if (isWPFormat()) {
|
|
|
|
return new FormattedDisk[]
|
|
|
|
{ new GutenbergFormatDisk(filename, imageOrder) };
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2018-01-08 11:21:45 +00:00
|
|
|
throw new DiskUnrecognizedException(filename);
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2008-12-26 20:36:47 +00:00
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
2012-08-02 03:18:07 +00:00
|
|
|
* Returns the diskImageManager.
|
|
|
|
* @return ByteArrayImageLayout diskImageManager The disk Image Manager of this disk
|
2002-12-01 02:21:00 +00:00
|
|
|
*/
|
2003-12-26 21:41:14 +00:00
|
|
|
public ByteArrayImageLayout getDiskImageManager() {
|
2003-12-28 20:42:51 +00:00
|
|
|
if (imageOrder != null) {
|
|
|
|
return imageOrder.getDiskImageManager();
|
|
|
|
}
|
2004-07-11 15:07:56 +00:00
|
|
|
return diskImageManager;
|
2002-12-09 05:44:12 +00:00
|
|
|
}
|
2002-12-01 02:21:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the filename.
|
|
|
|
* @return String
|
|
|
|
*/
|
|
|
|
public String getFilename() {
|
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
2004-06-03 03:15:32 +00:00
|
|
|
/**
|
|
|
|
* Sets the filename.
|
|
|
|
*/
|
|
|
|
public void setFilename(String filename) {
|
|
|
|
this.filename = filename;
|
|
|
|
}
|
|
|
|
|
2012-07-06 16:45:49 +00:00
|
|
|
/**
|
|
|
|
* Returns the name of the underlying image order.
|
|
|
|
* @return String
|
|
|
|
*/
|
|
|
|
public String getOrderName() {
|
|
|
|
return (imageOrder == null) ? textBundle.get("FormattedDisk.Unknown") : imageOrder.getName();
|
|
|
|
}
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Indicate if this disk is GZIP compressed.
|
|
|
|
*/
|
|
|
|
public boolean isCompressed() {
|
2004-07-11 15:07:56 +00:00
|
|
|
return filename.toLowerCase().endsWith(".gz"); //$NON-NLS-1$
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
2012-07-06 16:45:49 +00:00
|
|
|
/**
|
|
|
|
* Indicate if this disk is a ShrinkIt-compressed disk image.
|
|
|
|
*/
|
|
|
|
public boolean isSDK()
|
|
|
|
{
|
|
|
|
return filename.toLowerCase().endsWith(".sdk"); //$NON-NLS-1$
|
|
|
|
}
|
|
|
|
|
2012-07-26 21:00:58 +00:00
|
|
|
/**
|
|
|
|
* Indicate if this disk is a ShrinkIt-compressed package.
|
|
|
|
*/
|
|
|
|
public boolean isSHK()
|
|
|
|
{
|
|
|
|
return filename.toLowerCase().endsWith(".shk"); //$NON-NLS-1$
|
|
|
|
}
|
|
|
|
|
2012-08-11 20:57:44 +00:00
|
|
|
/**
|
|
|
|
* Indicate if this disk is a ShrinkIt-compressed binary II archive.
|
|
|
|
*/
|
|
|
|
public boolean isBXY()
|
|
|
|
{
|
|
|
|
return filename.toLowerCase().endsWith(".bxy"); //$NON-NLS-1$
|
|
|
|
}
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Indicate if this disk is ProDOS ordered (beginning with block 0).
|
|
|
|
*/
|
|
|
|
public boolean isProdosOrder() {
|
2004-07-11 15:07:56 +00:00
|
|
|
return filename.toLowerCase().endsWith(".po") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".po.gz") //$NON-NLS-1$
|
2002-12-01 02:21:00 +00:00
|
|
|
|| is2ImgOrder()
|
2004-07-11 15:07:56 +00:00
|
|
|
|| filename.toLowerCase().endsWith(".hdv") //$NON-NLS-1$
|
2004-06-20 17:01:20 +00:00
|
|
|
|| getPhysicalSize() >= APPLE_800KB_2IMG_DISK;
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicate if this disk is DOS ordered (T0,S0 - T35,S15).
|
|
|
|
*/
|
|
|
|
public boolean isDosOrder() {
|
2004-07-11 15:07:56 +00:00
|
|
|
return filename.toLowerCase().endsWith(".do") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".do.gz") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".dsk") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".dsk.gz"); //$NON-NLS-1$
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicate if this disk is a 2IMG disk.
|
|
|
|
* This is ProDOS ordered, but with a header on the disk.
|
|
|
|
*/
|
|
|
|
public boolean is2ImgOrder() {
|
2004-07-11 15:07:56 +00:00
|
|
|
return filename.toLowerCase().endsWith(".2img") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".2img.gz") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".2mg") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".2mg.gz"); //$NON-NLS-1$
|
2003-12-26 21:41:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicate if this disk is a nibbilized disk..
|
|
|
|
*/
|
|
|
|
public boolean isNibbleOrder() {
|
2004-07-11 15:07:56 +00:00
|
|
|
return filename.toLowerCase().endsWith(".nib") //$NON-NLS-1$
|
|
|
|
|| filename.toLowerCase().endsWith(".nib.gz"); //$NON-NLS-1$
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Identify the size of this disk.
|
|
|
|
*/
|
|
|
|
public int getPhysicalSize() {
|
2003-12-28 20:42:51 +00:00
|
|
|
if (getDiskImageManager() != null) {
|
|
|
|
return getDiskImageManager().getPhysicalSize();
|
|
|
|
}
|
2004-07-11 15:07:56 +00:00
|
|
|
return getImageOrder().getPhysicalSize();
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
2003-05-02 02:54:52 +00:00
|
|
|
/**
|
|
|
|
* Resize a disk image up to a larger size. The primary intention is to
|
|
|
|
* "fix" disk images that have been created too small. The primary culprit
|
|
|
|
* is ApplePC HDV images which dynamically grow. Since AppleCommander
|
|
|
|
* works with a byte array, the image must grow to its full size.
|
|
|
|
* @param newSize
|
|
|
|
*/
|
|
|
|
protected void resizeDiskImage(int newSize) {
|
2003-12-26 21:41:14 +00:00
|
|
|
if (newSize < getPhysicalSize()) {
|
2003-05-02 02:54:52 +00:00
|
|
|
throw new IllegalArgumentException(
|
2004-07-11 15:07:56 +00:00
|
|
|
textBundle.get("Disk.ResizeDiskError")); //$NON-NLS-1$
|
2003-05-02 02:54:52 +00:00
|
|
|
}
|
|
|
|
byte[] newDiskImage = new byte[newSize];
|
2003-12-26 21:41:14 +00:00
|
|
|
byte[] oldDiskImage = imageOrder.getDiskImageManager().getDiskImage();
|
|
|
|
System.arraycopy(oldDiskImage, 0, newDiskImage, 0, oldDiskImage.length);
|
|
|
|
imageOrder.getDiskImageManager().setDiskImage(newDiskImage);
|
2003-05-02 02:54:52 +00:00
|
|
|
}
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
2002-12-10 05:11:53 +00:00
|
|
|
* Read the block from the disk image.
|
2002-12-01 02:21:00 +00:00
|
|
|
*/
|
|
|
|
public byte[] readBlock(int block) {
|
2003-12-26 21:41:14 +00:00
|
|
|
return imageOrder.readBlock(block);
|
2002-12-10 05:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write the block to the disk image.
|
|
|
|
*/
|
|
|
|
public void writeBlock(int block, byte[] data) {
|
2003-12-26 21:41:14 +00:00
|
|
|
imageOrder.writeBlock(block, data);
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the specified sector.
|
|
|
|
*/
|
2002-12-09 05:44:12 +00:00
|
|
|
public byte[] readSector(int track, int sector) throws IllegalArgumentException {
|
2003-12-26 21:41:14 +00:00
|
|
|
return imageOrder.readSector(track, sector);
|
2002-12-09 05:44:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write the specified sector.
|
|
|
|
*/
|
|
|
|
public void writeSector(int track, int sector, byte[] bytes)
|
|
|
|
throws IllegalArgumentException {
|
2003-12-26 21:41:14 +00:00
|
|
|
imageOrder.writeSector(track, sector, bytes);
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a ProDOS formatted
|
|
|
|
* disk.
|
|
|
|
*/
|
|
|
|
public boolean isProdosFormat() {
|
|
|
|
byte[] prodosVolumeDirectory = readBlock(2);
|
2012-07-06 21:14:54 +00:00
|
|
|
int volDirEntryLength = prodosVolumeDirectory[0x23];
|
|
|
|
int volDirEntriesPerBlock = prodosVolumeDirectory[0x24];
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
return prodosVolumeDirectory[0] == 0 &&
|
|
|
|
prodosVolumeDirectory[1] == 0 &&
|
2012-07-06 21:14:54 +00:00
|
|
|
(prodosVolumeDirectory[4]&0xf0) == 0xf0 &&
|
2012-07-07 13:59:59 +00:00
|
|
|
(volDirEntryLength * volDirEntriesPerBlock <= BLOCK_SIZE);
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a DOS 3.3 formatted
|
2002-12-12 04:36:16 +00:00
|
|
|
* disk. This is a little nasty - since 800KB and 140KB images have
|
2003-12-26 21:41:14 +00:00
|
|
|
* different characteristics. This just tests 140KB images.
|
2002-12-01 02:21:00 +00:00
|
|
|
*/
|
|
|
|
public boolean isDosFormat() {
|
2012-07-06 21:14:54 +00:00
|
|
|
boolean good = false;
|
|
|
|
if (!is140KbDisk()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
byte[] vtoc = readSector(17, 0);
|
|
|
|
good = (imageOrder.isSizeApprox(APPLE_140KB_DISK)
|
|
|
|
|| imageOrder.isSizeApprox(APPLE_140KB_NIBBLE_DISK))
|
|
|
|
&& vtoc[0x01] == 17 // expect catalog to start on track 17
|
|
|
|
// can vary && vtoc[0x02] == 15 // expect catalog to start on sector 15 (140KB disk only!)
|
|
|
|
&& vtoc[0x27] == 122 // expect 122 track/sector pairs per sector
|
2012-07-07 05:06:29 +00:00
|
|
|
&& (vtoc[0x34] == 35 || vtoc[0x34] == 40) // expect 35 or 40 tracks per disk (140KB disk only!)
|
2012-07-06 21:14:54 +00:00
|
|
|
&& vtoc[0x35] == 16 // expect 16 sectors per disk (140KB disk only!)
|
|
|
|
;
|
|
|
|
if (good) {
|
|
|
|
int catTrack = vtoc[0x01]; // Pull out the first catalog track/sector
|
|
|
|
int catSect = vtoc[0x02];
|
|
|
|
byte[] cat = readSector(catTrack, catSect);
|
|
|
|
if (catTrack == cat[1] && catSect == cat[2] + 1) {
|
|
|
|
// Still good... let's follow one more
|
|
|
|
catTrack = cat[1];
|
|
|
|
catSect = cat[2];
|
|
|
|
cat = readSector(catTrack, catSect);
|
|
|
|
if (catTrack == cat[1] && catSect == cat[2] + 1) {
|
|
|
|
good = true;
|
|
|
|
} else {
|
|
|
|
good = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
|
|
/*
|
|
|
|
* If we get various exceptions from reading tracks and sectors, then we
|
|
|
|
* definitely don't have a valid DOS image.
|
|
|
|
*/
|
|
|
|
good = false;
|
|
|
|
}
|
|
|
|
return good;
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2002-12-14 05:39:41 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a UniDOS formatted
|
|
|
|
* disk. UniDOS creates two logical disks on an 800KB physical disk.
|
2002-12-17 05:24:19 +00:00
|
|
|
* The first logical disk takes up the first 400KB and the second
|
|
|
|
* logical disk takes up the second 400KB.
|
2002-12-14 05:39:41 +00:00
|
|
|
*/
|
|
|
|
public boolean isUniDosFormat() {
|
2004-06-20 17:01:20 +00:00
|
|
|
if (!is800KbDisk()) return false;
|
2002-12-14 05:39:41 +00:00
|
|
|
byte[] vtoc1 = readSector(17, 0); // logical disk #1
|
|
|
|
byte[] vtoc2 = readSector(67, 0); // logical disk #2
|
|
|
|
return
|
|
|
|
// LOGICAL DISK #1
|
|
|
|
vtoc1[0x01] == 17 // expect catalog to start on track 17
|
|
|
|
&& vtoc1[0x02] == 31 // expect catalog to start on sector 31
|
|
|
|
&& vtoc1[0x27] == 122 // expect 122 tract/sector pairs per sector
|
|
|
|
&& vtoc1[0x34] == 50 // expect 50 tracks per disk
|
|
|
|
&& vtoc1[0x35] == 32 // expect 32 sectors per disk
|
|
|
|
&& vtoc1[0x36] == 0 // bytes per sector (low byte)
|
2002-12-17 05:24:19 +00:00
|
|
|
&& vtoc1[0x37] == 1 // bytes per sector (high byte)
|
2002-12-14 05:39:41 +00:00
|
|
|
// LOGICAL DISK #2
|
|
|
|
&& vtoc2[0x01] == 17 // expect catalog to start on track 17
|
|
|
|
&& vtoc2[0x02] == 31 // expect catalog to start on sector 31
|
|
|
|
&& vtoc2[0x27] == 122 // expect 122 tract/sector pairs per sector
|
|
|
|
&& vtoc2[0x34] == 50 // expect 50 tracks per disk
|
|
|
|
&& vtoc2[0x35] == 32 // expect 32 sectors per disk
|
|
|
|
&& vtoc2[0x36] == 0 // bytes per sector (low byte)
|
|
|
|
&& vtoc2[0x37] == 1; // bytes per sector (high byte)
|
|
|
|
}
|
2002-12-17 05:24:19 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a OzDOS formatted
|
|
|
|
* disk. OzDOS creates two logical disks on an 800KB physical disk.
|
|
|
|
* The first logical disk takes the first half of each block and
|
|
|
|
* the second logical disk takes the second half of each block.
|
|
|
|
*/
|
|
|
|
public boolean isOzDosFormat() {
|
2004-06-20 17:01:20 +00:00
|
|
|
if (!is800KbDisk()) return false;
|
2002-12-17 05:24:19 +00:00
|
|
|
byte[] vtoc = readBlock(544); // contains BOTH VTOCs!
|
|
|
|
return
|
|
|
|
// LOGICAL DISK #1
|
|
|
|
vtoc[0x001] == 17 // expect catalog to start on track 17
|
|
|
|
&& vtoc[0x002] == 31 // expect catalog to start on sector 31
|
|
|
|
&& vtoc[0x027] == 122 // expect 122 tract/sector pairs per sector
|
|
|
|
&& vtoc[0x034] == 50 // expect 50 tracks per disk
|
|
|
|
&& vtoc[0x035] == 32 // expect 32 sectors per disk
|
|
|
|
&& vtoc[0x036] == 0 // bytes per sector (low byte)
|
|
|
|
&& vtoc[0x037] == 1 // bytes per sector (high byte)
|
|
|
|
// LOGICAL DISK #2
|
|
|
|
&& vtoc[0x137] == 1 // bytes per sector (high byte)
|
|
|
|
&& vtoc[0x101] == 17 // expect catalog to start on track 17
|
|
|
|
&& vtoc[0x102] == 31 // expect catalog to start on sector 31
|
|
|
|
&& vtoc[0x127] == 122 // expect 122 tract/sector pairs per sector
|
|
|
|
&& vtoc[0x134] == 50 // expect 50 tracks per disk
|
|
|
|
&& vtoc[0x135] == 32 // expect 32 sectors per disk
|
|
|
|
&& vtoc[0x136] == 0 // bytes per sector (low byte)
|
|
|
|
&& vtoc[0x137] == 1; // bytes per sector (high byte)
|
|
|
|
}
|
2002-12-01 02:21:00 +00:00
|
|
|
|
2010-08-09 06:40:08 +00:00
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a NakedOS formatted
|
|
|
|
* disk.
|
|
|
|
*/
|
|
|
|
public boolean isNakedosFormat() {
|
|
|
|
if (!is140KbDisk()) return false;
|
|
|
|
byte[] vtoc = readSector(0, 3); // VTOC starts on sector 9 (mapped to 3)
|
|
|
|
return (imageOrder.isSizeApprox(APPLE_140KB_DISK)
|
|
|
|
|| imageOrder.isSizeApprox(APPLE_140KB_NIBBLE_DISK))
|
|
|
|
&& vtoc[0xd0] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd1] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd2] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd3] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd4] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd5] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd6] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd7] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd8] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xd9] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xda] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xdb] == -2 // expect DOS as reserved
|
|
|
|
&& vtoc[0xdc] != -2 // expect something besides DOS next
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a Pascal formatted
|
2007-03-30 21:49:20 +00:00
|
|
|
* disk. Pascal disks may be either 140K or 800K.
|
2002-12-01 02:21:00 +00:00
|
|
|
*/
|
|
|
|
public boolean isPascalFormat() {
|
2007-03-30 21:49:20 +00:00
|
|
|
if (!(is140KbDisk() || is800KbDisk())) return false;
|
2002-12-01 02:21:00 +00:00
|
|
|
byte[] directory = readBlock(2);
|
|
|
|
return directory[0] == 0 && directory[1] == 0
|
|
|
|
&& directory[2] == 6 && directory[3] == 0
|
|
|
|
&& directory[4] == 0 && directory[5] == 0;
|
|
|
|
}
|
|
|
|
|
2003-12-22 04:41:50 +00:00
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a CP/M formatted disk.
|
|
|
|
* Check the first 256 bytes of the CP/M directory for validity.
|
|
|
|
*/
|
|
|
|
public boolean isCpmFormat() {
|
2004-06-20 17:01:20 +00:00
|
|
|
if (!is140KbDisk()) return false;
|
2003-12-22 04:41:50 +00:00
|
|
|
byte[] directory = readSector(3, 0);
|
|
|
|
int bytes[] = new int[256];
|
|
|
|
for (int i=0; i<directory.length; i++) {
|
|
|
|
bytes[i] = AppleUtil.getUnsignedByte(directory[i]);
|
|
|
|
}
|
|
|
|
int offset = 0;
|
|
|
|
while (offset < directory.length) {
|
|
|
|
// Check if this is an empty directory entry (and ignore it)
|
|
|
|
int e5count = 0;
|
|
|
|
for (int i=0; i<CpmFileEntry.ENTRY_LENGTH; i++) {
|
|
|
|
e5count+= bytes[offset+i] == 0xe5 ? 1 : 0;
|
|
|
|
}
|
2003-12-22 07:14:10 +00:00
|
|
|
if (e5count != CpmFileEntry.ENTRY_LENGTH) { // Not all bytes were 0xE5
|
|
|
|
// Check user number. Should be 0-15 or 0xE5
|
|
|
|
if (bytes[offset] > 15 && bytes[offset] != 0xe5) return false;
|
|
|
|
// Validate filename has highbit off
|
|
|
|
for (int i=0; i<8; i++) {
|
|
|
|
if (bytes[offset+1+i] > 127) return false;
|
|
|
|
}
|
|
|
|
// Extent should be 0-31 (low = 0-31 and high = 0)
|
|
|
|
if (bytes[offset+0xc] > 31 || bytes[offset+0xe] > 0) return false;
|
|
|
|
// Number of used records cannot exceed 0x80
|
|
|
|
if (bytes[offset+0xf] > 0x80) return false;
|
2003-12-22 04:41:50 +00:00
|
|
|
}
|
|
|
|
// Next entry
|
|
|
|
offset+= CpmFileEntry.ENTRY_LENGTH;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2004-06-20 17:01:20 +00:00
|
|
|
/**
|
|
|
|
* Answers true if this disk image is within the expected 140K
|
|
|
|
* disk size. Can vary if a header has been applied or if this is
|
|
|
|
* a nibblized disk image.
|
|
|
|
*/
|
|
|
|
protected boolean is140KbDisk() {
|
|
|
|
return getPhysicalSize() >= APPLE_140KB_DISK
|
|
|
|
&& getPhysicalSize() <= APPLE_140KB_NIBBLE_DISK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Answers true if this disk image is within the expected 800K
|
|
|
|
* disk size. Can vary if a 2IMG header has been applied.
|
|
|
|
*/
|
|
|
|
protected boolean is800KbDisk() {
|
|
|
|
return getPhysicalSize() >= APPLE_800KB_DISK
|
|
|
|
&& getPhysicalSize() <= APPLE_800KB_2IMG_DISK;
|
|
|
|
}
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a RDOS formatted
|
|
|
|
* disk.
|
|
|
|
*/
|
|
|
|
public boolean isRdosFormat() {
|
2004-06-20 17:01:20 +00:00
|
|
|
if (!is140KbDisk()) return false;
|
2002-12-01 02:21:00 +00:00
|
|
|
byte[] block = readSector(0, 0x0d);
|
|
|
|
String id = AppleUtil.getString(block, 0xe0, 4);
|
2004-07-11 15:07:56 +00:00
|
|
|
return "RDOS".equals(id); //$NON-NLS-1$
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|
2002-12-09 05:44:12 +00:00
|
|
|
|
2008-12-26 20:36:47 +00:00
|
|
|
/**
|
|
|
|
* Test the disk format to see if this is a WP formatted
|
|
|
|
* disk.
|
|
|
|
*/
|
|
|
|
public boolean isWPFormat() {
|
|
|
|
if (!is140KbDisk()) return false;
|
|
|
|
byte[] vtoc = readSector(17, 7);
|
|
|
|
return (imageOrder.isSizeApprox(APPLE_140KB_DISK)
|
|
|
|
|| imageOrder.isSizeApprox(APPLE_140KB_NIBBLE_DISK))
|
|
|
|
&& vtoc[0x00] == 17 // expect catalog to start on track 17
|
|
|
|
&& vtoc[0x01] == 7 // expect catalog to start on sector 7
|
|
|
|
&& vtoc[0x0f] == -115; // expect 0x8d's every 16 bytes
|
|
|
|
}
|
2012-08-02 03:18:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates if a given byte sequence is likely to be a DiskCopy 42 stream.
|
|
|
|
*
|
|
|
|
* @return boolean liklihood it is a DC42 stream
|
|
|
|
*/
|
|
|
|
private static boolean isDC42(byte[] buffer) {
|
|
|
|
return (((buffer[0x52] == 0x01) && (buffer[0x53] == 0x00)) &&
|
|
|
|
((buffer[0x51] == 0x02) || (buffer[0x51] == 0x22) || (buffer[0x51] == 0x24)));
|
|
|
|
}
|
|
|
|
public boolean isDC42() {
|
|
|
|
return isDC42;
|
|
|
|
}
|
2002-12-09 05:44:12 +00:00
|
|
|
/**
|
|
|
|
* Indicates if the disk has changed. Triggered when data is
|
|
|
|
* written and cleared when data is saved.
|
|
|
|
*/
|
|
|
|
public boolean hasChanged() {
|
2003-12-26 21:41:14 +00:00
|
|
|
return getDiskImageManager().hasChanged();
|
2002-12-09 05:44:12 +00:00
|
|
|
}
|
2003-12-08 00:50:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates if the disk image is new. This can be used
|
|
|
|
* for Save As processing.
|
|
|
|
*/
|
|
|
|
public boolean isNewImage() {
|
|
|
|
return newImage;
|
|
|
|
}
|
2003-12-26 21:41:14 +00:00
|
|
|
|
|
|
|
/**
|
2012-08-02 03:18:07 +00:00
|
|
|
* Answer with the physical ordering of the disk.
|
2003-12-26 21:41:14 +00:00
|
|
|
*/
|
|
|
|
public ImageOrder getImageOrder() {
|
|
|
|
return imageOrder;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the physical ordering of the disk.
|
|
|
|
*/
|
2004-06-03 03:15:32 +00:00
|
|
|
protected void setImageOrder(ImageOrder imageOrder) {
|
2003-12-26 21:41:14 +00:00
|
|
|
this.imageOrder = imageOrder;
|
|
|
|
}
|
2012-07-03 00:37:44 +00:00
|
|
|
|
|
|
|
/**
|
2012-07-06 16:45:49 +00:00
|
|
|
* Change underlying image order to DOS ImageOrder.
|
|
|
|
* Assumes this is a 140k disk image.
|
2012-07-03 00:37:44 +00:00
|
|
|
*
|
|
|
|
* @see ImageOrder
|
|
|
|
*/
|
|
|
|
public void makeDosOrder()
|
|
|
|
{
|
|
|
|
DosOrder doso = new DosOrder(new ByteArrayImageLayout(Disk.APPLE_140KB_DISK));
|
|
|
|
changeImageOrderByTrackAndSector(getImageOrder(), doso);
|
|
|
|
setImageOrder(doso);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change to a different ImageOrder. Remains in ProDOS format but the
|
|
|
|
* underlying order can change.
|
|
|
|
*
|
|
|
|
* @see ImageOrder
|
|
|
|
*/
|
|
|
|
public void makeProdosOrder()
|
|
|
|
{
|
|
|
|
ProdosOrder pdo = new ProdosOrder(new ByteArrayImageLayout(Disk.APPLE_140KB_DISK));
|
|
|
|
changeImageOrderByBlock(getImageOrder(), pdo);
|
|
|
|
setImageOrder(pdo);
|
|
|
|
}
|
|
|
|
|
2012-07-31 21:06:01 +00:00
|
|
|
/**
|
|
|
|
* Find the standard sized disk that will fit the requested number of bytes.
|
2018-03-10 04:49:40 +00:00
|
|
|
* @return int size of the disk if it will satisfy the request, -1 otherwise
|
2012-07-31 21:06:01 +00:00
|
|
|
*/
|
|
|
|
public static int sizeToFit(long bytes) {
|
|
|
|
if (bytes < APPLE_140KB_DISK) {
|
|
|
|
return APPLE_140KB_DISK;
|
|
|
|
} else if (bytes < APPLE_800KB_DISK) {
|
|
|
|
return APPLE_800KB_DISK;
|
|
|
|
} else if (bytes < APPLE_5MB_HARDDISK) {
|
|
|
|
return APPLE_5MB_HARDDISK;
|
|
|
|
} else if (bytes < APPLE_10MB_HARDDISK) {
|
|
|
|
return APPLE_10MB_HARDDISK;
|
|
|
|
} else if (bytes < APPLE_20MB_HARDDISK) {
|
|
|
|
return APPLE_20MB_HARDDISK;
|
|
|
|
} else if (bytes < APPLE_32MB_HARDDISK) {
|
|
|
|
return APPLE_20MB_HARDDISK;
|
|
|
|
} else if (bytes < APPLE_32MB_HARDDISK) {
|
|
|
|
return APPLE_32MB_HARDDISK;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-03 00:37:44 +00:00
|
|
|
/**
|
|
|
|
* Change ImageOrder from source order to target order by copying sector by
|
|
|
|
* sector.
|
|
|
|
*/
|
|
|
|
private void changeImageOrderByTrackAndSector(ImageOrder sourceOrder, ImageOrder targetOrder)
|
|
|
|
{
|
|
|
|
if (!sameSectorsPerDisk(sourceOrder, targetOrder)) {
|
|
|
|
throw new IllegalArgumentException(textBundle.get("Disk.ResizeDiskError"));
|
|
|
|
}
|
|
|
|
for (int track = 0; track < sourceOrder.getTracksPerDisk(); track++) {
|
|
|
|
for (int sector = 0; sector < sourceOrder.getSectorsPerTrack(); sector++) {
|
|
|
|
byte[] data = sourceOrder.readSector(track, sector);
|
|
|
|
targetOrder.writeSector(track, sector, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change ImageOrder from source order to target order by copying block by
|
|
|
|
* block.
|
|
|
|
*/
|
|
|
|
private void changeImageOrderByBlock(ImageOrder sourceOrder, ImageOrder targetOrder)
|
|
|
|
{
|
|
|
|
if (!sameBlocksPerDisk(sourceOrder, targetOrder)) {
|
|
|
|
throw new IllegalArgumentException(textBundle.get("Disk.ResizeDiskError"));
|
|
|
|
}
|
|
|
|
for (int block = 0; block < sourceOrder.getBlocksOnDevice(); block++) {
|
|
|
|
byte[] blockData = sourceOrder.readBlock(block);
|
|
|
|
targetOrder.writeBlock(block, blockData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Answers true if the two disks have the same number of blocks per disk.
|
|
|
|
*/
|
|
|
|
private static boolean sameBlocksPerDisk(ImageOrder sourceOrder, ImageOrder targetOrder)
|
|
|
|
{
|
|
|
|
return sourceOrder.getBlocksOnDevice() == targetOrder.getBlocksOnDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Answers true if the two disks have the same number of sectors per disk.
|
|
|
|
*/
|
|
|
|
private static boolean sameSectorsPerDisk(ImageOrder sourceOrder, ImageOrder targetOrder)
|
|
|
|
{
|
|
|
|
return sourceOrder.getSectorsPerDisk() == targetOrder.getSectorsPerDisk();
|
|
|
|
}
|
|
|
|
|
2002-12-01 02:21:00 +00:00
|
|
|
}
|