diff --git a/src/com/webcodepro/applecommander/storage/Disk.java b/src/com/webcodepro/applecommander/storage/Disk.java index 42c6a23..6b943db 100644 --- a/src/com/webcodepro/applecommander/storage/Disk.java +++ b/src/com/webcodepro/applecommander/storage/Disk.java @@ -34,6 +34,7 @@ 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; +import com.webcodepro.applecommander.storage.os.gutenberg.GutenbergFormatDisk; import com.webcodepro.applecommander.storage.os.pascal.PascalFormatDisk; import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk; import com.webcodepro.applecommander.storage.os.rdos.RdosFormatDisk; @@ -191,7 +192,7 @@ public class Disk { if (isProdosOrder()) { imageOrder = new ProdosOrder(diskImageManager); } else if (isDosOrder()) { - imageOrder = new DosOrder(diskImageManager); + imageOrder = new DosOrder(diskImageManager); } else if (isNibbleOrder()) { imageOrder = new NibbleOrder(diskImageManager); } @@ -256,10 +257,13 @@ public class Disk { } else if (isCpmFormat()) { return new FormattedDisk[] { new CpmFormatDisk(filename, imageOrder) }; + } else if (isWPFormat()) { + return new FormattedDisk[] + { new GutenbergFormatDisk(filename, imageOrder) }; } return null; } - + /** * Returns the diskImage. * @return byte[] @@ -556,6 +560,20 @@ public class Disk { return "RDOS".equals(id); //$NON-NLS-1$ } + /** + * 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 + } + /** * Indicates if the disk has changed. Triggered when data is * written and cleared when data is saved. diff --git a/src/com/webcodepro/applecommander/storage/StorageBundle.properties b/src/com/webcodepro/applecommander/storage/StorageBundle.properties index b766f47..1544aeb 100644 --- a/src/com/webcodepro/applecommander/storage/StorageBundle.properties +++ b/src/com/webcodepro/applecommander/storage/StorageBundle.properties @@ -23,6 +23,7 @@ DiskNameN={0} (Disk {1}) Dos33=DOS 3.3 LockedQ=Locked? DirectoryCreationNotSupported=Unable to create directories. +Gutenberg=Gutenberg ##### FIX ##### ############### @@ -155,6 +156,9 @@ DosFormatDisk.InvalidTrackAndSectorCombinationError=Invalid track ({0}), sector DosFileEntry.DosFileEntryLengthError=A DOS 3.3 file entry must be {0} bytes long\! DosFileEntry.UnableToSetAddressError=Unable to set address for DosFileEntry [{0}] +# GutenbergFileEntry +GutenbergFileEntry.GutenbergFileEntryLengthError=A Gutenberg file entry must be {0} bytes long\! + # CpmFormatDisk CpmFormatDisk.DiskName=CP/M Volume CpmFormatDisk.Cpm=CP/M diff --git a/src/com/webcodepro/applecommander/storage/filters/GutenbergFileFilter.java b/src/com/webcodepro/applecommander/storage/filters/GutenbergFileFilter.java new file mode 100644 index 0000000..430733d --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/filters/GutenbergFileFilter.java @@ -0,0 +1,553 @@ +/* + * AppleCommander - An Apple ][ image utility. + * Copyright (C) 2002, 2008 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.filters; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.StringTokenizer; + +import com.webcodepro.applecommander.storage.FileEntry; +import com.webcodepro.applecommander.storage.FileFilter; +import com.webcodepro.applecommander.ui.AppleCommander; +import com.webcodepro.applecommander.util.AppleUtil; + +/** + * Extract the contents of an ancient word processor file (might be Word + * Perfect) and convert to a text format. Currently supported formats are plain + * text, HTML, or RTF. These are not exact duplicates, but they are close + * approximations. RTF format is suitable for conversion to other word + * processors. + *
+ * To choose export format, use the appropriately named select method. + *
+ * Date created: Dec 18, 2008 9:09:21 AM
+ *
+ * @author David Schmidt
+ */
+public class GutenbergFileFilter implements FileFilter {
+ /*
+ * This list identifies the various rendering options. As the internal
+ * format may change in the future, the internal representation is hidden
+ * and the developer should use the appropriate select method.
+ */
+ private static final int RENDER_AS_TEXT = 0;
+ private static final int RENDER_AS_HTML = 1;
+ private static final int RENDER_AS_RTF = 2;
+ private int rendering = RENDER_AS_HTML;
+ /**
+ * Constructor for GutenbergFileFilter.
+ */
+ public GutenbergFileFilter() {
+ super();
+ }
+
+ /**
+ * Process the given FileEntry and return a byte array with filtered data.
+ *
+ * @see com.webcodepro.applecommander.storage.FileFilter#filter(FileEntry)
+ */
+ public byte[] filter(FileEntry fileEntry) {
+ byte[] fileData = fileEntry.getFileData();
+ int offset = 0;
+ ByteArrayOutputStream byteArray = new ByteArrayOutputStream(
+ fileData.length);
+ PrintWriter printWriter = new PrintWriter(byteArray, true);
+ while (offset < fileData.length) {
+ fileData[offset] = (byte) (fileData[offset++] & 0x7f);
+ }
+ String preprocess = new String(fileData).trim();
+ handleTranslation(preprocess, printWriter, rendering);
+ printWriter.flush();
+ return byteArray.toByteArray();
+ }
+
+ /**
+ * Transform text into desired destination format
+ */
+ protected void handleTranslation(String raw, PrintWriter output, int rendering) {
+ boolean ignoreBr = false;
+ boolean inHeader = false;
+ boolean inItalics = false;
+ boolean inBold = false;
+ boolean inCenter = false;
+ boolean inUnderline = false;
+ boolean inSuperscript = false;
+ String cooked = raw.replaceAll("\\x00", ""); //$NON-NLS-1$ $NON-NLS-2$ Remove nulls
+ cooked=cooked.replaceAll("<[a|A]1>", ""); //$NON-NLS-1$ $NON-NLS-2$ File start
+ cooked=cooked.replaceAll(" "); //$NON-NLS-1$ $NON-NLS-2$ Tab level 1
+ cooked=cooked.replaceAll("<[t|T]2>(.*)", " "); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("\\par "); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ ignoreBr = true;
+ }
+ else if (t.equalsIgnoreCase("UL") && (!inUnderline)) //$NON-NLS-1$
+ {
+ switch (rendering)
+ {
+ // Underline on
+ case RENDER_AS_HTML:
+ output.print(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("\\ul "); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ inUnderline = true;
+ }
+ else if ((t.equalsIgnoreCase("KU") || t.equalsIgnoreCase("KL") || t.equalsIgnoreCase("UK")) && (inUnderline)) //$NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
+ {
+ // Underline off
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("\\ulnone "); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ inUnderline = false;
+ }
+ else if ((t.equalsIgnoreCase("BO") || t.equalsIgnoreCase("b1")) && (!inBold)) //$NON-NLS-1$ $NON-NLS-2$
+ {
+ // Bold on
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("\\b "); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ inBold = true;
+ }
+ else if (t.equalsIgnoreCase("KB")) //$NON-NLS-1$
+ {
+ // Bold off
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("\\b0 "); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ inUnderline = false;
+ }
+ else if ((t.equalsIgnoreCase("UFA") || t.equalsIgnoreCase("UFP") || t.equals("UFY") || t.equalsIgnoreCase("f1")) && (inSuperscript == false)) //$NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ $NON-NLS-4$
+ {
+ // Superscript on
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("\\super "); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ inSuperscript = true;
+ }
+ else if ((t.equalsIgnoreCase("UFM") || t.equalsIgnoreCase("f2")) && (inSuperscript == true)) //$NON-NLS-1$ $NON-NLS-2$
+ {
+ // Superscript off
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("\\nosupersub"); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ inSuperscript = false;
+ }
+ else if (t.equalsIgnoreCase("co") && (inCenter == false)) //$NON-NLS-1$
+ {
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print("
+ * Date created: Oct 4, 2002 5:15:25 PM
+ * @author Rob Greene
+ */
+public class GutenbergFileEntry implements FileEntry {
+ private TextBundle textBundle = StorageBundle.getInstance();
+ /**
+ * Indicates the length in bytes of the DOS file entry field.
+ */
+ public static final int FILE_DESCRIPTIVE_ENTRY_LENGTH = 16;
+ /**
+ * Holds the disk the FileEntry is attached to.
+ */
+ private GutenbergFormatDisk disk;
+ /**
+ * Track of the FileEntry location.
+ */
+ private int track;
+ /**
+ * Sector of the FileEntry location.
+ */
+ private int sector;
+ /**
+ * Offset into sector of FileEntry location.
+ */
+ private int offset;
+
+ /**
+ * Constructor for GutenbergFileEntry.
+ */
+ public GutenbergFileEntry(GutenbergFormatDisk disk, int track, int sector, int offset) {
+ super();
+ this.disk = disk;
+ this.track = track;
+ this.sector = sector;
+ this.offset = offset;
+ }
+
+ /**
+ * Read the FileEntry from the disk image.
+ */
+ protected byte[] readFileEntry() {
+ byte[] sectorData = disk.readSector(track, sector);
+ byte[] fileEntry = new byte[FILE_DESCRIPTIVE_ENTRY_LENGTH];
+ System.arraycopy(sectorData, offset, fileEntry, 0, fileEntry.length);
+ return fileEntry;
+ }
+
+ /**
+ * Write the FileEntry to the disk image.
+ */
+ protected void writeFileEntry(byte[] fileEntry) {
+ if (fileEntry.length != FILE_DESCRIPTIVE_ENTRY_LENGTH) {
+ throw new IllegalArgumentException(textBundle.
+ format("GutenbergFileEntry.GutenbergFileEntryLengthError", //$NON-NLS-1$
+ FILE_DESCRIPTIVE_ENTRY_LENGTH));
+ }
+ byte[] sectorData = disk.readSector(track, sector);
+ System.arraycopy(fileEntry, 0, sectorData, offset, fileEntry.length);
+ disk.writeSector(track, sector, sectorData);
+ }
+
+ /**
+ * Return the maximum filename length.
+ */
+ public int getMaximumFilenameLength() {
+ return 12;
+ }
+
+ /**
+ * Return the name of this file.
+ * @see com.webcodepro.applecommander.storage.FileEntry#getFilename()
+ */
+ public String getFilename() {
+ return AppleUtil.getString(readFileEntry(), 0, getMaximumFilenameLength()).trim();
+ }
+
+ /**
+ * Set the name of this file.
+ */
+ public void setFilename(String filename) {
+ byte[] data = readFileEntry();
+ AppleUtil.setString(data, 0, filename.toUpperCase(), getMaximumFilenameLength());
+ writeFileEntry(data);
+ }
+
+ /**
+ * Return the filetype of this file.
+ * @see com.webcodepro.applecommander.storage.FileEntry#getFiletype()
+ */
+ public String getFiletype() {
+ return "T"; // Only one file type... text //$NON-NLS-1$
+ }
+
+ /**
+ * Set the filetype (typeless - unused)
+ */
+ public void setFiletype(String filetype) {
+ }
+
+ /**
+ * Identify if this file is locked.
+ * @see com.webcodepro.applecommander.storage.FileEntry#isLocked()
+ */
+ public boolean isLocked() {
+ return true;
+ }
+
+ /**
+ * Set the lock indicator (unused)
+ */
+ public void setLocked(boolean lock) {
+ }
+
+ /**
+ * Compute the size of this file (in bytes).
+ * @see com.webcodepro.applecommander.storage.FileEntry#getSize()
+ */
+ public int getSize() {
+ // Nothing special, just compute from number of sectors
+ int size = getSectorsUsed() * Disk.SECTOR_SIZE;
+ return size;
+ }
+
+ /**
+ * Compute the number of sectors used.
+ */
+ public int getSectorsUsed() {
+ // Follow the chain of sectors to find the end.
+ int track = getTrack();
+ int sector = getSector();
+ int sectors = 0;
+ while (track < 128) {
+ byte[] sectorData = disk.readSector(track, sector);
+ track = AppleUtil.getUnsignedByte(sectorData[0x04]);
+ sector = AppleUtil.getUnsignedByte(sectorData[0x05]);
+ sectors++;
+ }
+ return sectors;
+ }
+
+ /**
+ * Set the number of sectors used.
+ */
+ public void setSectorsUsed(int sectorsUsed) {
+ }
+
+ /**
+ * Identify if this is a directory file.
+ * @see com.webcodepro.applecommander.storage.FileEntry#isDirectory()
+ */
+ public boolean isDirectory() {
+ return false;
+ }
+
+ /**
+ * Identify if this file has been deleted.
+ * @see com.webcodepro.applecommander.storage.FileEntry#isDeleted()
+ */
+ public boolean isDeleted() {
+ return AppleUtil.getUnsignedByte(readFileEntry()[0x0d]) == 0x40;
+ }
+
+ /**
+ * Delete this file.
+ */
+ public void delete() {
+ }
+
+ /**
+ * Get the standard file column header information.
+ * This default implementation is intended only for standard mode.
+ * displayMode is specified in FormattedDisk.
+ */
+ public List getFileColumnData(int displayMode) {
+ NumberFormat numberFormat = NumberFormat.getNumberInstance();
+ List list = new ArrayList();
+ switch (displayMode) {
+ case FormattedDisk.FILE_DISPLAY_NATIVE:
+ list.add(isLocked() ? "*" : " "); //$NON-NLS-1$ //$NON-NLS-2$
+ list.add(getFiletype());
+ numberFormat.setMinimumIntegerDigits(3);
+ list.add(numberFormat.format(getSectorsUsed()));
+ list.add(getFilename());
+ break;
+ case FormattedDisk.FILE_DISPLAY_DETAIL:
+ list.add(isLocked() ? "*" : " "); //$NON-NLS-1$ //$NON-NLS-2$
+ list.add(getFiletype());
+ list.add(getFilename());
+ list.add(numberFormat.format(getSize()));
+ numberFormat.setMinimumIntegerDigits(3);
+ list.add(numberFormat.format(getSectorsUsed()));
+ list.add(isDeleted() ? textBundle.get("Deleted") : ""); //$NON-NLS-1$//$NON-NLS-2$
+ list.add("T" + getTrack() + " S" + getSector()); //$NON-NLS-1$ //$NON-NLS-2$
+ break;
+ default: // FILE_DISPLAY_STANDARD
+ list.add(getFilename());
+ list.add(getFiletype());
+ list.add(numberFormat.format(getSize()));
+ list.add(isLocked() ? textBundle.get("Locked") : ""); //$NON-NLS-1$//$NON-NLS-2$
+ break;
+ }
+ return list;
+ }
+
+ /**
+ * Get the track of first track/sector list sector.
+ */
+ public int getTrack() {
+ return AppleUtil.getUnsignedByte(readFileEntry()[0x0c]);
+ }
+
+ /**
+ * Set the track of the first track/sector list sector.
+ */
+ public void setTrack(int track) {
+ byte[] data = readFileEntry();
+ data[0x0c] = (byte) track;
+ writeFileEntry(data);
+ }
+
+ /**
+ * Get the sector of first track/sector list sector.
+ */
+ public int getSector() {
+ return AppleUtil.getUnsignedByte(readFileEntry()[0x0d]);
+ }
+
+ /**
+ * Set the sector of the first track/sector list sector.
+ */
+ public void setSector(int sector) {
+ byte[] data = readFileEntry();
+ data[0x0d] = (byte) sector;
+ writeFileEntry(data);
+ }
+
+ /**
+ * Get file data. This handles any operating-system specific issues.
+ */
+ public byte[] getFileData() {
+ return disk.getFileData(this);
+ }
+
+ /**
+ * Set the file data.
+ *
+ * Note: The address can be set before the data is saved or
+ * after the data is saved. This is an attempt to make the
+ * API more easily usable.
+ *
+ * Empirically, the data must be set before the address is set.
+ */
+ public void setFileData(byte[] data) throws DiskFullException {
+ disk.setFileData(this, data);
+ }
+
+ /**
+ * Get the suggested FileFilter. This appears to be operating system
+ * specific, so each operating system needs to implement some manner
+ * of guessing the appropriate filter.
+ * FIXME - this code should be a helper class for DOS and RDOS!
+ */
+ public FileFilter getSuggestedFilter() {
+ return new GutenbergFileFilter();
+ }
+
+ /**
+ * Determine if this is an assembly source code file.
+ */
+ public boolean isAssemblySourceFile() {
+ return false;
+ }
+
+ /**
+ * Get the FormattedDisk associated with this FileEntry.
+ * This is useful to interfaces that need to retrieve the associated
+ * disk.
+ */
+ public FormattedDisk getFormattedDisk() {
+ return disk;
+ }
+
+ /**
+ * Indicates if this filetype requires an address component.
+ * Note that the FormattedDisk also has this method - normally,
+ * this will defer to the method on FormattedDisk, as it will be
+ * more generic.
+ */
+ public boolean needsAddress() {
+ return false;
+ }
+
+ /**
+ * Set the address that this file loads at.
+ */
+ public void setAddress(int address) {
+ }
+
+ /**
+ * Indicates that this filetype can be compiled.
+ */
+ public boolean canCompile() {
+ return false;
+ }
+}
diff --git a/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java b/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java
new file mode 100644
index 0000000..0a242c5
--- /dev/null
+++ b/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java
@@ -0,0 +1,718 @@
+/*
+ * AppleCommander - An Apple ][ image utility.
+ * Copyright (C) 2002, 2008 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.os.gutenberg;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.webcodepro.applecommander.storage.DirectoryEntry;
+import com.webcodepro.applecommander.storage.DiskFullException;
+import com.webcodepro.applecommander.storage.FileEntry;
+import com.webcodepro.applecommander.storage.FormattedDisk;
+import com.webcodepro.applecommander.storage.StorageBundle;
+import com.webcodepro.applecommander.storage.physical.ImageOrder;
+import com.webcodepro.applecommander.util.AppleUtil;
+import com.webcodepro.applecommander.util.TextBundle;
+
+/**
+ * Manages a disk that is in Gutenberg Word Processor format.
+ *
+ * Date created: Dec 17, 2008 04:29:23 PM
+ * @author David Schmidt
+ */
+public class GutenbergFormatDisk extends FormattedDisk {
+ private TextBundle textBundle = StorageBundle.getInstance();
+ /**
+ * Indicates the index of the track in the location array.
+ */
+ public static final int TRACK_LOCATION_INDEX = 0;
+ /**
+ * Indicates the index of the sector in the location array.
+ */
+ public static final int SECTOR_LOCATION_INDEX = 1;
+ /**
+ * The catalog track.
+ */
+ public static final int CATALOG_TRACK = 17;
+ /**
+ * The VTOC sector.
+ */
+ public static final int VTOC_SECTOR = 7;
+ /**
+ * The standard track/sector pairs in a track/sector list.
+ */
+ public static final int TRACK_SECTOR_PAIRS = 122;
+ /**
+ * The list of filetypes available.
+ */
+ private static final String[] filetypes = {
+ "T" //$NON-NLS-1$
+ };
+
+ /**
+ * Use this inner interface for managing the disk usage data.
+ * This offloads format-specific implementation to the implementing class.
+ */
+ private class WPDiskUsage implements DiskUsage {
+ private int[] location = null;
+ public boolean hasNext() {
+ return location == null
+ || (location[TRACK_LOCATION_INDEX] < getTracks()
+ && location[SECTOR_LOCATION_INDEX] < getSectors());
+ }
+ public void next() {
+ if (location == null) {
+ location = new int[2];
+ } else {
+ location[SECTOR_LOCATION_INDEX]++;
+ if (location[SECTOR_LOCATION_INDEX] >= getSectors()) {
+ location[SECTOR_LOCATION_INDEX] = 0;
+ location[TRACK_LOCATION_INDEX]++;
+ }
+ }
+ }
+ /**
+ * Get the free setting for the bitmap at the current location.
+ * I don't think there is a map stored on disk, however.
+ */
+ public boolean isFree() {
+ if (location == null || location.length != 2) {
+ throw new IllegalArgumentException(StorageBundle.getInstance()
+ .get("DosFormatDisk.InvalidDimensionError")); //$NON-NLS-1$
+ }
+ return false;
+ }
+ public boolean isUsed() {
+ return !isFree();
+ }
+ }
+
+ /**
+ * Constructor for GutenbergFormatDisk.
+ */
+ public GutenbergFormatDisk(String filename, ImageOrder imageOrder) {
+ super(filename, imageOrder);
+ }
+
+ /**
+ * Create a GutenbergFormatDisk. All DOS disk images are expected to
+ * be 140K in size.
+ */
+ public static GutenbergFormatDisk[] create(String filename, ImageOrder imageOrder) {
+ GutenbergFormatDisk disk = new GutenbergFormatDisk(filename, imageOrder);
+ disk.format();
+ return new GutenbergFormatDisk[] { disk };
+ }
+
+ /**
+ * Identify the operating system format of this disk as Gutenberg.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#getFormat()
+ */
+ public String getFormat() {
+ return textBundle.get("Gutenberg"); //$NON-NLS-1$
+ }
+
+ /**
+ * Retrieve a list of files.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#getFiles()
+ */
+ public List getFiles() {
+ List list = new ArrayList();
+ int track = CATALOG_TRACK;
+ int sector = VTOC_SECTOR;
+ while (track < 40) { // iterate through all catalog sectors
+ byte[] catalogSector = readSector(track, sector);
+ int offset = 0x20; // First entry is 0x20 deep
+ while (offset < 0xff) { // iterate through all entries
+ if (catalogSector[offset] != -96) {
+ list.add(new GutenbergFileEntry(this, track, sector, offset));
+ }
+ offset+= GutenbergFileEntry.FILE_DESCRIPTIVE_ENTRY_LENGTH;
+ }
+ track = AppleUtil.getUnsignedByte(catalogSector[4]); // Pull in the next catalog sector
+ sector = AppleUtil.getUnsignedByte(catalogSector[5]);
+ }
+ return list;
+ }
+
+ /**
+ * Create a FileEntry.
+ */
+ public FileEntry createFile() throws DiskFullException {
+ byte[] vtoc = readVtoc();
+ int track = AppleUtil.getUnsignedByte(vtoc[1]);
+ int sector = AppleUtil.getUnsignedByte(vtoc[2]);
+ while (sector != 0) { // bug fix: iterate through all catalog _sectors_
+ byte[] catalogSector = readSector(track, sector);
+ int offset = 0x0b;
+ while (offset < 0xff) { // iterate through all entries
+ int value = AppleUtil.getUnsignedByte(catalogSector[offset]);
+ if (value == 0 || value == 0xff) {
+ return new GutenbergFileEntry(this, track, sector, offset);
+ }
+ offset+= GutenbergFileEntry.FILE_DESCRIPTIVE_ENTRY_LENGTH;
+ }
+ track = catalogSector[1];
+ sector = catalogSector[2];
+ }
+ throw new DiskFullException(textBundle.get("DosFormatDisk.NoMoreSpaceError")); //$NON-NLS-1$
+ }
+
+ /**
+ * Identify if additional directories can be created. This
+ * may indicate that directories are not available to this
+ * operating system or simply that the disk image is "locked"
+ * to writing.
+ */
+ public boolean canCreateDirectories() {
+ return false;
+ }
+
+ /**
+ * Indicates if this disk image can create a file.
+ * If not, the reason may be as simple as it has not beem implemented
+ * to something specific about the disk.
+ */
+ public boolean canCreateFile() {
+ return false;
+ }
+
+ /**
+ * Compute the amount of freespace available on the disk.
+ * This algorithm completely ignores tracks and sectors by
+ * running through the entire bitmap stored on the VTOC.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#getFreeSpace()
+ */
+ public int getFreeSpace() {
+ return getFreeSectors() * SECTOR_SIZE;
+ }
+
+ /**
+ * Comput the number of free sectors available on the disk.
+ */
+ public int getFreeSectors() {
+ return 0;
+ }
+
+ /**
+ * Return the amount of used space in bytes.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#getUsedSpace()
+ */
+ public int getUsedSpace() {
+ return APPLE_140KB_DISK;
+ }
+
+ /**
+ * Compute the number of used sectors on the disk.
+ */
+ public int getUsedSectors() {
+ return getTotalSectors() - getFreeSectors();
+ }
+
+ /**
+ * Compute the total number of sectors available on the disk.
+ */
+ public int getTotalSectors() {
+ int tracks = getTracks();
+ int sectors = getSectors();
+ return tracks * sectors;
+ }
+
+ /**
+ * Return the WP disk name.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#getDiskName()
+ */
+ public String getDiskName() {
+ // Pull the disk name out...
+ return AppleUtil.getString(readVtoc(), 6, 9).trim();
+ }
+
+ /**
+ * Return the VTOC (Volume Table Of Contents).
+ */
+ protected byte[] readVtoc() {
+ return readSector(CATALOG_TRACK, VTOC_SECTOR);
+ }
+
+ /**
+ * Save the VTOC (Volume Table Of Contents) to disk.
+ */
+ protected void writeVtoc(byte[] vtoc) {
+ writeSector(CATALOG_TRACK, VTOC_SECTOR, vtoc);
+ }
+
+ /**
+ * Get the disk usage iterator.
+ */
+ public DiskUsage getDiskUsage() {
+ return new WPDiskUsage();
+ }
+
+ /**
+ * Get the number of tracks on this disk.
+ */
+ public int getTracks() {
+ byte[] vtoc = readVtoc();
+ return AppleUtil.getUnsignedByte(vtoc[0x34]);
+ }
+
+ /**
+ * Get the number of sectors on this disk.
+ */
+ public int getSectors() {
+ byte[] vtoc = readVtoc();
+ return AppleUtil.getUnsignedByte(vtoc[0x35]);
+ }
+
+ /**
+ * Get suggested dimensions for display of bitmap. For DOS 3.3, that information
+ * is stored in the VTOC, and that information is fairly important.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#getBitmapDimensions()
+ */
+ public int[] getBitmapDimensions() {
+ int tracks = getTracks();
+ int sectors = getSectors();
+ return new int[] { tracks, sectors };
+ }
+
+ /**
+ * Get the length of the bitmap.
+ */
+ public int getBitmapLength() {
+ return getTotalSectors();
+ }
+
+ /**
+ * Get the labels to use in the bitmap.
+ */
+ public String[] getBitmapLabels() {
+ return new String[] { textBundle.get("DosFormatDisk.Track"), textBundle.get("DosFormatDisk.Sector") }; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Get WP-specific disk information.
+ */
+ public List getDiskInformation() {
+ List list = super.getDiskInformation();
+ return list;
+ }
+
+ /**
+ * Get the standard file column header information.
+ * This default implementation is intended only for standard mode.
+ */
+ public List getFileColumnHeaders(int displayMode) {
+ List list = new ArrayList();
+ switch (displayMode) {
+ case FILE_DISPLAY_NATIVE:
+ list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
+ list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
+ list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
+ list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$
+ FileColumnHeader.ALIGN_LEFT));
+ break;
+ case FILE_DISPLAY_DETAIL:
+ list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
+ list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
+ list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$
+ FileColumnHeader.ALIGN_LEFT));
+ list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, //$NON-NLS-1$
+ FileColumnHeader.ALIGN_RIGHT));
+ list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$
+ list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, //$NON-NLS-1$
+ FileColumnHeader.ALIGN_CENTER));
+ list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.TrackAndSectorList"), 7, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$
+ break;
+ default: // FILE_DISPLAY_STANDARD
+ list.addAll(super.getFileColumnHeaders(displayMode));
+ break;
+ }
+ return list;
+ }
+
+ /**
+ * Indicates if this disk format supports "deleted" files.
+ */
+ public boolean supportsDeletedFiles() {
+ return true;
+ }
+
+ /**
+ * Indicates if this disk image can read data from a file.
+ */
+ public boolean canReadFileData() {
+ return true;
+ }
+
+ /**
+ * Indicates if this disk image can write data to a file.
+ */
+ public boolean canWriteFileData() {
+ return false;
+ }
+
+ /**
+ * Identify if this disk format as not capable of having directories.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#canHaveDirectories()
+ */
+ public boolean canHaveDirectories() {
+ return false;
+ }
+
+ /**
+ * Indicates if this disk image can delete a file.
+ */
+ public boolean canDeleteFile() {
+ return false;
+ }
+
+ /**
+ * Get the data associated with the specified FileEntry.
+ */
+ public byte[] getFileData(FileEntry fileEntry) {
+ if ( !(fileEntry instanceof GutenbergFileEntry)) {
+ throw new IllegalArgumentException(textBundle.get("DosFormatDisk.InvalidFileEntryError")); //$NON-NLS-1$
+ }
+ GutenbergFileEntry wpEntry = (GutenbergFileEntry) fileEntry;
+ // Size is calculated by sectors used - not actual size - as size varies
+ // on filetype, etc.
+ int filesize = wpEntry.getSectorsUsed();
+ byte[] fileData = null;
+ if (filesize > 0) {
+ fileData = new byte[(wpEntry.getSectorsUsed()) * SECTOR_SIZE];
+ } else {
+ fileData = new byte[0];
+ // don't need to load it - also bypass potential issues
+ return fileData;
+ }
+ int track = wpEntry.getTrack();
+ int sector = wpEntry.getSector();
+ int offset = 0;
+ while (track < 128) {
+ byte[] sectorData = readSector(track,sector);
+ track = AppleUtil.getUnsignedByte(sectorData[0x04]);
+ sector = AppleUtil.getUnsignedByte(sectorData[0x05]);
+ System.arraycopy(sectorData, 6, fileData, offset, sectorData.length-6);
+ offset+= sectorData.length-6;
+ }
+ return fileData;
+ }
+
+ /**
+ * Writes the raw bytes into the file. This bypasses any special formatting
+ * of the data (such as prepending the data with a length and/or an address).
+ * Typically, the FileEntry.setFileData method should be used.
+ */
+ public void setFileData(FileEntry fileEntry, byte[] fileData) throws DiskFullException {
+ setFileData((GutenbergFileEntry)fileEntry, fileData);
+ }
+
+ /**
+ * Set the data associated with the specified GutenbergFileEntry into sectors
+ * on the disk.
+ */
+ protected void setFileData(GutenbergFileEntry fileEntry, byte[] data) throws DiskFullException {
+ // compute free space and see if the data will fit!
+ int numberOfDataSectors = (data.length + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ int numberOfSectors = numberOfDataSectors +
+ (numberOfDataSectors + TRACK_SECTOR_PAIRS - 1) / TRACK_SECTOR_PAIRS;
+ if (numberOfSectors > getFreeSectors() + fileEntry.getSectorsUsed()) {
+ throw new DiskFullException(
+ textBundle.format("DosFormatDisk.NotEnoughSectorsError", //$NON-NLS-1$
+ numberOfSectors, getFreeSectors()));
+ }
+ // free "old" data and just rewrite stuff...
+ // freeSectors(fileEntry); (not going to work...)
+ byte[] vtoc = readVtoc();
+ int track = fileEntry.getTrack();
+ int sector = fileEntry.getSector();
+ if (track == 0 || track == 255) {
+ track = 1;
+ sector = 0;
+ while (true) {
+ if (isSectorFree(track,sector,vtoc)) {
+ break;
+ }
+ sector++;
+ if (sector >= getSectors()) {
+ track++;
+ sector = 0;
+ }
+ }
+ fileEntry.setTrack(track);
+ fileEntry.setSector(sector);
+ }
+ setSectorUsed(track, sector, vtoc);
+ byte[] trackSectorList = new byte[SECTOR_SIZE];
+ int offset = 0;
+ int trackSectorOffset = 0x0c;
+ int totalSectors = 0;
+ int t=1; // initial search for space
+ int s=0;
+ while (offset < data.length) {
+ // locate next free sector
+ while (true) {
+ if (isSectorFree(t,s,vtoc)) {
+ break;
+ }
+ s++;
+ if (s >= getSectors()) {
+ t++;
+ s = 0;
+ }
+ }
+ setSectorUsed(t,s,vtoc);
+ if (trackSectorOffset >= 0x100) {
+ // filled up the first track/sector list - save it
+ trackSectorList[0x01] = (byte) t;
+ trackSectorList[0x02] = (byte) s;
+ writeSector(track, sector, trackSectorList);
+ trackSectorList = new byte[SECTOR_SIZE];
+ trackSectorOffset = 0x0c;
+ track = t;
+ sector = s;
+ } else {
+ // write out a data sector
+ trackSectorList[trackSectorOffset] = (byte) t;
+ trackSectorList[trackSectorOffset+1] = (byte) s;
+ trackSectorOffset+= 2;
+ byte[] sectorData = new byte[SECTOR_SIZE];
+ int length = Math.min(SECTOR_SIZE, data.length - offset);
+ System.arraycopy(data, offset, sectorData, 0, length);
+ writeSector(t,s,sectorData);
+ offset+= SECTOR_SIZE;
+ }
+ totalSectors++;
+ }
+ writeSector(track, sector, trackSectorList); // last T/S list
+ totalSectors++;
+ fileEntry.setSectorsUsed(totalSectors);
+ writeVtoc(vtoc);
+ }
+
+ /**
+ * Free sectors used by a GutenbergFileEntry.
+ */
+/*
+ protected void freeSectors(GutenbergFileEntry GutenbergFileEntry) {
+ byte[] vtoc = readVtoc();
+ int track = GutenbergFileEntry.getTrack();
+ if (track == 255) return;
+ int sector = GutenbergFileEntry.getSector();
+ while (track != 0) {
+ setSectorFree(track,sector,vtoc);
+ byte[] trackSectorList = readSector(track, sector);
+ track = AppleUtil.getUnsignedByte(trackSectorList[0x01]);
+ sector = AppleUtil.getUnsignedByte(trackSectorList[0x02]);
+ for (int i=0x0c; i<0x100; i+=2) {
+ int t = AppleUtil.getUnsignedByte(trackSectorList[i]);
+ if (t == 0) break;
+ int s = AppleUtil.getUnsignedByte(trackSectorList[i+1]);
+ setSectorFree(t,s,vtoc);
+ }
+ }
+ writeVtoc(vtoc);
+ }
+*/
+ /**
+ * Format the disk as DOS 3.3.
+ * @see com.webcodepro.applecommander.storage.FormattedDisk#format()
+ */
+ public void format() {
+ getImageOrder().format();
+ format(15, 35, 16);
+ }
+
+ /**
+ * Format the disk as DOS 3.3 given the dymanic parameters.
+ * (Used for UniDOS and OzDOS.)
+ */
+ protected void format(int firstCatalogSector, int tracksPerDisk,
+ int sectorsPerTrack) {
+
+ writeBootCode();
+ // create catalog sectors
+ byte[] data = new byte[SECTOR_SIZE];
+ for (int sector=firstCatalogSector; sector > 0; sector--) {
+ if (sector > 1) {
+ data[0x01] = CATALOG_TRACK;
+ data[0x02] = (byte)(sector-1);
+ } else {
+ data[0x01] = 0;
+ data[0x02] = 0;
+ }
+ writeSector(CATALOG_TRACK, sector, data);
+ }
+ // create VTOC
+ data[0x01] = CATALOG_TRACK; // track# of first catalog sector
+ data[0x02] = (byte)firstCatalogSector; // sector# of first catalog sector
+ data[0x03] = 3; // DOS 3.3 formatted
+ data[0x06] = (byte)254; // DISK VOLUME#
+ data[0x27] = TRACK_SECTOR_PAIRS;// maximum # of T/S pairs in a sector
+ data[0x30] = CATALOG_TRACK+1; // last track where sectors allocated
+ data[0x31] = 1; // direction of allocation
+ data[0x34] = (byte)tracksPerDisk; // tracks per disk
+ data[0x35] = (byte)sectorsPerTrack;// sectors per track
+ data[0x37] = 1; // 36/37 are # of bytes per sector
+ for (int track=0; track$1
"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h1 heading
+ cooked=cooked.replaceAll("<[h|H]2>(.*)", "$1
"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h2 heading
+ cooked=cooked.replaceAll("<[h|H]3>(.*)", "$1
"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h3 heading
+ cooked=cooked.replaceAll("<[h|H]4>(.*)", "$1
"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h4 heading
+ cooked=cooked.replaceAll("<[n|N]1>(.*)", "$1
"); //$NON-NLS-1$ $NON-NLS-2$ Another kind of heading? Give it boundaries
+ cooked=cooked.replaceAll("<[n|N]2>(.*)", "$1
"); //$NON-NLS-1$ $NON-NLS-2$ Another kind of heading? Give it boundaries
+ cooked=cooked.replaceAll("<[n|N]3>(.*)", "$1
"); //$NON-NLS-1$ $NON-NLS-2$ Another kind of heading? Give it boundaries
+ cooked=cooked.replaceAll("<[t|T]1>", "$1
"); //$NON-NLS-1$ $NON-NLS-2$ Tab level 2
+ cooked=cooked.replaceAll("<[t|T]3>(.*)", "
"); //$NON-NLS-1$ $NON-NLS-2$ Tab level 3
+ cooked=cooked.replaceAll("\\x0f", "$1
"); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("h2")) //$NON-NLS-1$
+ output.print("
"); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("/h2")) //$NON-NLS-1$
+ output.print(""); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("/h3")) //$NON-NLS-1$
+ output.print(""); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("/h4")) //$NON-NLS-1$
+ output.print(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.print("\\b0\\par\\fs24 "); //$NON-NLS-1$
+ break;
+ default:
+ output.println();
+ break;
+ }
+ }
+ else if (t.startsWith("blockquote")) // Indent $NON-NLS-1$
+ {
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print(""); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("h3")) //$NON-NLS-1$
+ output.print("
"); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("h4")) //$NON-NLS-1$
+ output.print("
"); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ if (t.equalsIgnoreCase("h1")) //$NON-NLS-1$
+ output.print("\\pard\\s1\\b\\fs48 "); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("h2")) //$NON-NLS-1$
+ output.print("\\pard\\s2\\b\\fs36 "); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("h3")) //$NON-NLS-1$
+ output.print("\\pard\\s3\\b\\fs27 "); //$NON-NLS-1$
+ else if (t.equalsIgnoreCase("h4")) //$NON-NLS-1$
+ output.print("\\pard\\s4\\b\\fs24 "); //$NON-NLS-1$
+ break;
+ default:
+ output.println();
+ break;
+ }
+ }
+ else if ((t.startsWith("/h")) && (inHeader))
+ {
+ ignoreBr = true;
+ inHeader = false;
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ if (t.equalsIgnoreCase("/h1")) //$NON-NLS-1$
+ output.print("
"); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println(""); //$NON-NLS-1$
+ break;
+ default:
+ output.print(" "); //$NON-NLS-1$
+ break;
+ }
+ ignoreBr = true;
+ }
+ else if (t.startsWith("/blockquote")) // Outdent $NON-NLS-1$
+ {
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.print("
"); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println(""); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ ignoreBr = true;
+ }
+// else
+// System.err.println("Ignored command: <"+t+">");
+ }
+ else
+ {
+ // System.out.println("Data: ["+t+"]");
+ output.print(t);
+ }
+ }
+ if (!ignoreBr)
+ handleReturn(output);
+ ignoreBr = false;
+
+ switch (rendering)
+ {
+ // turn off many types of formatting stuff at the end of lines
+ case RENDER_AS_HTML:
+ if (inItalics)
+ output.print(""); //$NON-NLS-1$
+ if (inBold)
+ output.print(""); //$NON-NLS-1$
+ if (inUnderline)
+ output.print(""); //$NON-NLS-1$
+ if (inSuperscript)
+ output.print(""); //$NON-NLS-1$
+ case RENDER_AS_RTF:
+ if (inItalics)
+ output.print("\\i0 "); //$NON-NLS-1$
+ if (inBold)
+ output.print("\\b0 "); //$NON-NLS-1$
+ if (inUnderline)
+ output.print("\\ulnone"); //$NON-NLS-1$
+ if (inSuperscript)
+ output.print("\\nosupersub"); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ inItalics = false;
+ inBold = false;
+ inUnderline = false;
+ inSuperscript = false;
+ inHeader = false;
+ }
+ // Put the finishing touches on the document
+ switch (rendering)
+ {
+ case RENDER_AS_HTML:
+ output.println(""); //$NON-NLS-1$
+ break;
+ case RENDER_AS_RTF:
+ output.println("}"); //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
+ /**
+ * Deal with carriage-return.
+ */
+ protected void handleReturn(PrintWriter printWriter) {
+ if (isHtmlRendering())
+ printWriter.println("
"); //$NON-NLS-1$
+ else if (isRtfRendering())
+ printWriter.println("\\par"); //$NON-NLS-1$
+ else
+ printWriter.println();
+ }
+
+
+ /**
+ * Give suggested file name.
+ *
+ * @see com.webcodepro.applecommander.storage.FileFilter#getSuggestedFileName(FileEntry)
+ */
+ public String getSuggestedFileName(FileEntry fileEntry) {
+ String fileName = fileEntry.getFilename().trim();
+ String extension = ".txt"; //$NON-NLS-1$
+ if (isHtmlRendering())
+ extension = ".html"; //$NON-NLS-1$
+ else if (isRtfRendering())
+ extension = ".rtf"; //$NON-NLS-1$
+
+ if (!fileName.toLowerCase().endsWith(extension)) {
+ fileName = fileName + extension;
+ }
+ return fileName;
+ }
+
+ /**
+ * Set the rendering method.
+ */
+ protected void setRendering(int rendering) {
+ this.rendering = rendering;
+ }
+
+ /**
+ * Indicates if this is a text rendering.
+ */
+ public boolean isTextRendering() {
+ return rendering == RENDER_AS_TEXT;
+ }
+
+ /**
+ * Indicates if this is an HTML rendering.
+ */
+ public boolean isHtmlRendering() {
+ return rendering == RENDER_AS_HTML;
+ }
+
+ /**
+ * Indicates if this is an RTF rendering.
+ */
+ public boolean isRtfRendering() {
+ return rendering == RENDER_AS_RTF;
+ }
+
+ /**
+ * Selects the text rendering engine.
+ */
+ public void selectTextRendering() {
+ rendering = RENDER_AS_TEXT;
+ }
+
+ /**
+ * Selects the HTML rendering engine.
+ */
+ public void selectHtmlRendering() {
+ rendering = RENDER_AS_HTML;
+ }
+
+ /**
+ * Selects the RTF rendering engine.
+ */
+ public void selectRtfRendering() {
+ rendering = RENDER_AS_RTF;
+ }
+}
\ No newline at end of file
diff --git a/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFileEntry.java b/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFileEntry.java
new file mode 100644
index 0000000..dfa929c
--- /dev/null
+++ b/src/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFileEntry.java
@@ -0,0 +1,343 @@
+/*
+ * 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.os.gutenberg;
+
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.webcodepro.applecommander.storage.Disk;
+import com.webcodepro.applecommander.storage.DiskFullException;
+import com.webcodepro.applecommander.storage.FileEntry;
+import com.webcodepro.applecommander.storage.FileFilter;
+import com.webcodepro.applecommander.storage.FormattedDisk;
+import com.webcodepro.applecommander.storage.StorageBundle;
+import com.webcodepro.applecommander.storage.filters.GutenbergFileFilter;
+import com.webcodepro.applecommander.util.AppleUtil;
+import com.webcodepro.applecommander.util.TextBundle;
+
+/**
+ * Represents a DOS file entry on disk.
+ *