Implement Gutenberg disk translation

This commit is contained in:
2008-12-26 20:36:47 +00:00
parent 1a46ae1126
commit 2eb1437326
8 changed files with 1714 additions and 6 deletions

View File

@ -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.

View File

@ -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

View File

@ -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.
* <p>
* To choose export format, use the appropriately named select method.
* <p>
* 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("<e9>", ""); //$NON-NLS-1$ $NON-NLS-2$ End of file
cooked=cooked.replaceAll("<d1>", ""); //$NON-NLS-1$ $NON-NLS-2$ File start
cooked=cooked.replaceAll("<s1>", ""); //$NON-NLS-1$ $NON-NLS-2$ File start
cooked=cooked.replaceAll("<2>", ""); //$NON-NLS-1$ $NON-NLS-2$ Dunno what this is
cooked=cooked.replaceAll("<f9>", ""); //$NON-NLS-1$ $NON-NLS-2$ Dunno what this is
cooked=cooked.replaceAll("<i>", ""); //$NON-NLS-1$ $NON-NLS-2$ Dunno what this is
cooked=cooked.replaceAll("<i1>", ""); //$NON-NLS-1$ $NON-NLS-2$ Dunno what this is
cooked=cooked.replaceAll("<[h|H]1>(.*)", "<h1>$1</h1>"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h1 heading
cooked=cooked.replaceAll("<[h|H]2>(.*)", "<h2>$1</h2>"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h2 heading
cooked=cooked.replaceAll("<[h|H]3>(.*)", "<h3>$1</h3>"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h3 heading
cooked=cooked.replaceAll("<[h|H]4>(.*)", "<h4>$1</h4>"); //$NON-NLS-1$ $NON-NLS-2$ Bound a h4 heading
cooked=cooked.replaceAll("<[n|N]1>(.*)", "<h1>$1</h1>"); //$NON-NLS-1$ $NON-NLS-2$ Another kind of heading? Give it boundaries
cooked=cooked.replaceAll("<[n|N]2>(.*)", "<h2>$1</h2>"); //$NON-NLS-1$ $NON-NLS-2$ Another kind of heading? Give it boundaries
cooked=cooked.replaceAll("<[n|N]3>(.*)", "<h3>$1</h3>"); //$NON-NLS-1$ $NON-NLS-2$ Another kind of heading? Give it boundaries
cooked=cooked.replaceAll("<[t|T]1>", "<p>"); //$NON-NLS-1$ $NON-NLS-2$ Tab level 1
cooked=cooked.replaceAll("<[t|T]2>(.*)", "<blockquote>$1</blockquote>"); //$NON-NLS-1$ $NON-NLS-2$ Tab level 2
cooked=cooked.replaceAll("<[t|T]3>(.*)", "<blockquote><blockquote>$1</blockquote></blockquote>"); //$NON-NLS-1$ $NON-NLS-2$ Tab level 3
cooked=cooked.replaceAll("\\x0f", "</i>"); //$NON-NLS-1$ $NON-NLS-2$ Italics off
cooked=cooked.replaceAll("\\x01(.*)", " <i>$1</i>"); //$NON-NLS-1$ $NON-NLS-2$ Italics on
cooked=cooked.replaceAll("\\x02(.*)", " <i>$1</i>"); //$NON-NLS-1$ $NON-NLS-2$ Italics on
cooked=cooked.replaceAll("~", "\""); //$NON-NLS-1$ $NON-NLS-2$ Leading quote
cooked=cooked.replaceAll("_", "-"); //$NON-NLS-1$ $NON-NLS-2$
StringTokenizer newlines = new StringTokenizer(cooked, "\r", false); //$NON-NLS-1$
switch (rendering)
{
case RENDER_AS_HTML:
output.println("<body><html>");
break;
case RENDER_AS_RTF:
output.print("{\\rtf1"); //$NON-NLS-1$
output.print("{\\fonttbl{\\f0\\fmodern\\fprq1;}}"); //$NON-NLS-1$
output.print("{\\*\\generator AppleCommander "); //$NON-NLS-1$
output.print(AppleCommander.VERSION);
output.println(";}"); //$NON-NLS-1$
output.print("\\f0 "); //$NON-NLS-1$
break;
default:
break;
}
while (newlines.hasMoreTokens())
{
int mode = 0;
String line = newlines.nextToken();
StringTokenizer commands = new StringTokenizer(line,"<>",true);
while (commands.hasMoreTokens())
{
String t = commands.nextToken();
if (t.equals("<")) //$NON-NLS-1$
mode = 1;
else if (t.equals(">")) //$NON-NLS-1$
mode = 0;
else if (mode == 1) {
if (t.startsWith("NF")) //$NON-NLS-1$
{
// do nothing... consume it
}
else if (t.equalsIgnoreCase("i") && (!inItalics)) //$NON-NLS-1$
{
// Italics on
switch (rendering)
{
case RENDER_AS_HTML:
output.print("<i>"); //$NON-NLS-1$
break;
case RENDER_AS_RTF:
output.println("\\i "); //$NON-NLS-1$
break;
default:
break;
}
inItalics = true;
}
else if (t.equalsIgnoreCase("/i")) //$NON-NLS-1$
{
// Italics off
switch (rendering)
{
case RENDER_AS_HTML:
output.print("</i>"); //$NON-NLS-1$
break;
case RENDER_AS_RTF:
output.println("\\i0 "); //$NON-NLS-1$
break;
default:
break;
}
inItalics = false;
}
else if (t.equalsIgnoreCase("p") || t.startsWith("j") || t.startsWith("J")) //$NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
{
switch (rendering)
{
case RENDER_AS_HTML:
output.print("<p>"); //$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("<u>"); //$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("</u>"); //$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("<b>"); //$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("</b>"); //$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("<sup>"); //$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("</sup>"); //$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("<center>"); //$NON-NLS-1$
break;
case RENDER_AS_RTF:
output.println("\\pard\\qc "); //$NON-NLS-1$
break;
default:
break;
}
inCenter = true;
ignoreBr = true;
}
else if (t.equalsIgnoreCase("h8") && (inCenter == true)) //$NON-NLS-1$
{
// Center off
switch (rendering)
{
case RENDER_AS_HTML:
output.print("</center>"); //$NON-NLS-1$
break;
case RENDER_AS_RTF:
output.println("\\par \\pard "); //$NON-NLS-1$
break;
default:
break;
}
inCenter = false;
ignoreBr = true;
}
else if (t.startsWith("h") && (!inHeader)) //$NON-NLS-1$
{
ignoreBr = true;
inHeader = true;
switch (rendering)
{
case RENDER_AS_HTML:
if (t.equalsIgnoreCase("h1")) //$NON-NLS-1$
output.print("<h1>"); //$NON-NLS-1$
else if (t.equalsIgnoreCase("h2")) //$NON-NLS-1$
output.print("<h2>"); //$NON-NLS-1$
else if (t.equalsIgnoreCase("h3")) //$NON-NLS-1$
output.print("<h3>"); //$NON-NLS-1$
else if (t.equalsIgnoreCase("h4")) //$NON-NLS-1$
output.print("<h4>"); //$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("</h1>"); //$NON-NLS-1$
else if (t.equalsIgnoreCase("/h2")) //$NON-NLS-1$
output.print("</h2>"); //$NON-NLS-1$
else if (t.equalsIgnoreCase("/h3")) //$NON-NLS-1$
output.print("</h3>"); //$NON-NLS-1$
else if (t.equalsIgnoreCase("/h4")) //$NON-NLS-1$
output.print("</h4>"); //$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("<blockquote>"); //$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("</blockquote>"); //$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("</i>"); //$NON-NLS-1$
if (inBold)
output.print("</b>"); //$NON-NLS-1$
if (inUnderline)
output.print("</u>"); //$NON-NLS-1$
if (inSuperscript)
output.print("</sup>"); //$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("</body></html>"); //$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("<br>"); //$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;
}
}

View File

@ -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.
* <p>
* 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;
}
}

View File

@ -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.
* <p>
* 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<tracksPerDisk; track++) {
for (int sector=0; sector<sectorsPerTrack; sector++) {
if (track == 0 || track == CATALOG_TRACK) {
setSectorUsed(track, sector, data);
} else {
setSectorFree(track, sector, data);
}
}
}
writeVtoc(data);
}
/**
* Indicates if a specific track/sector is free.
*/
public boolean isSectorFree(int track, int sector, byte[] vtoc) {
checkRange(track, sector);
byte byt = vtoc[getFreeMapByte(track, sector)];
return AppleUtil.isBitSet(byt, getFreeMapBit(sector));
}
/**
* Indicates if a specific track/sector is used.
*/
public boolean isSectorUsed(int track, int sector, byte[] vtoc) {
return !isSectorFree(track, sector, vtoc);
}
/**
* Sets the track/sector indicator to free.
*/
public void setSectorFree(int track, int sector, byte[] vtoc) {
checkRange(track, sector);
int offset = getFreeMapByte(track, sector);
byte byt = vtoc[offset];
byt = AppleUtil.setBit(byt, getFreeMapBit(sector));
vtoc[offset] = byt;
}
/**
* Sets the track/sector indicator to used.
*/
public void setSectorUsed(int track, int sector, byte[] vtoc) {
checkRange(track, sector);
int offset = getFreeMapByte(track, sector);
byte byt = vtoc[offset];
byt = AppleUtil.clearBit(byt, getFreeMapBit(sector));
vtoc[offset] = byt;
}
/**
* Compute the VTOC byte for the T/S map.
*/
protected int getFreeMapByte(int track, int sector) {
int trackOffset = track * 4;
int sectorOffset = 1 - ((sector & 0x8) >> 3);
return 0x38 + trackOffset + sectorOffset;
}
/**
* Compute the VTOC bit for the T/S map.
*/
protected int getFreeMapBit(int sector) {
int bit = sector & 0x7;
return bit;
}
/**
* Validate track/sector range. This just validates the
* maximum values allowable for track and sector.
*/
protected void checkRange(int track, int sector) {
if (track > 50 || sector > 32) {
throw new IllegalArgumentException(
textBundle.format("DosFormatDisk.InvalidTrackAndSectorCombinationError", //$NON-NLS-1$
track, sector));
}
}
/**
* Returns the logical disk number. Returns a 0 to indicate no numbering.
*/
public int getLogicalDiskNumber() {
return 0;
}
/**
* Returns a valid filename for the given filename.
*/
public String getSuggestedFilename(String filename) {
int len = Math.min(filename.length(), 12);
return filename.toUpperCase().substring(0, len).trim();
}
/**
* Returns a valid filetype for the given filename. The most simple
* format will just assume a filetype of binary. This method is
* available for the interface to make an intelligent first guess
* as to the filetype.
*/
public String getSuggestedFiletype(String filename) {
return "T"; //$NON-NLS-1$
}
/**
* Returns a list of possible file types. Since the filetype is
* specific to each operating system, a simple String is used.
*/
public String[] getFiletypes() {
return filetypes;
}
/**
* Indicates if this filetype requires an address component.
* For DOS, only the Binary type needs an address.
*/
public boolean needsAddress(String filetype) {
return "B".equals(filetype); //$NON-NLS-1$
}
/**
* Indicates if this FormattedDisk supports a disk map.
*/
public boolean supportsDiskMap() {
return true;
}
/**
* Change to a different ImageOrder. Remains in DOS 3.3 format but
* the underlying order can chage.
* @see ImageOrder
*/
public void changeImageOrder(ImageOrder imageOrder) {
AppleUtil.changeImageOrderByTrackAndSector(getImageOrder(), imageOrder);
setImageOrder(imageOrder);
}
/**
* Create a new DirectoryEntry.
* @see com.webcodepro.applecommander.storage.DirectoryEntry#createDirectory()
*/
public DirectoryEntry createDirectory() throws DiskFullException {
throw new UnsupportedOperationException(textBundle.get("DirectoryCreationNotSupported")); //$NON-NLS-1$
}
}

View File

@ -49,6 +49,7 @@ WordProcessorRenderingMenuItem=Rendering
WordProcessorRenderAsTextMenuItem=Text
WordProcessorRenderAsHtmlMenuItem=HTML
WordProcessorRenderAsRtfMenuItem=RTF
GutenbergRenderingMenuItem=Gutenberg File Rendered as...
ExportAsGraphicsMenuItem=Graphics...
ExportGraphicsModeMenuItem=Mode
ExportGraphicsAsHiresBlackAndWhiteMenuItem=Hi-Res B&W

View File

@ -87,6 +87,7 @@ import com.webcodepro.applecommander.storage.filters.GraphicsFileFilter;
import com.webcodepro.applecommander.storage.filters.IntegerBasicFileFilter;
import com.webcodepro.applecommander.storage.filters.PascalTextFileFilter;
import com.webcodepro.applecommander.storage.filters.TextFileFilter;
import com.webcodepro.applecommander.storage.filters.GutenbergFileFilter;
import com.webcodepro.applecommander.storage.os.prodos.ProdosDiskSizeDoesNotMatchException;
import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout;
@ -153,6 +154,7 @@ public class DiskExplorerTab {
private FileFilter fileFilter;
private GraphicsFileFilter graphicsFilter = new GraphicsFileFilter();
private AppleWorksWordProcessorFileFilter awpFilter = new AppleWorksWordProcessorFileFilter();
private GutenbergFileFilter wpFilter = new GutenbergFileFilter();
private int currentFormat = FormattedDisk.FILE_DISPLAY_STANDARD;
private boolean formatChanged;
@ -612,6 +614,62 @@ public class DiskExplorerTab {
item = new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.CASCADE);
item.setText(textBundle.get("GutenbergRenderingMenuItem")); //$NON-NLS-1$
Menu subMenu2 = new Menu(shell, SWT.DROP_DOWN);
item.setMenu(subMenu2);
subMenu.addMenuListener(new MenuAdapter() {
/**
* Toggle all sub-menu MenuItems to the proper state to reflect
* the current file extension chosen.
*/
public void menuShown(MenuEvent event) {
Menu theMenu = (Menu) event.getSource();
MenuItem[] subItems = theMenu.getItems();
subItems[0].setSelection(getWPFilter().isTextRendering());
subItems[1].setSelection(getWPFilter().isHtmlRendering());
subItems[2].setSelection(getWPFilter().isRtfRendering());
}
});
item = new MenuItem(subMenu2, SWT.RADIO);
item.setText(textBundle.get("WordProcessorRenderAsTextMenuItem")); //$NON-NLS-1$
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate rendering style.
*/
public void widgetSelected(SelectionEvent event) {
getWPFilter().selectTextRendering();
setFileFilter(getWPFilter());
exportFile(null);
}
});
item = new MenuItem(subMenu2, SWT.RADIO);
item.setText(textBundle.get("WordProcessorRenderAsHtmlMenuItem")); //$NON-NLS-1$
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate rendering style.
*/
public void widgetSelected(SelectionEvent event) {
getWPFilter().selectHtmlRendering();
setFileFilter(getWPFilter());
exportFile(null);
}
});
item = new MenuItem(subMenu2, SWT.RADIO);
item.setText(textBundle.get("WordProcessorRenderAsRtfMenuItem")); //$NON-NLS-1$
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate rendering style.
*/
public void widgetSelected(SelectionEvent event) {
getWPFilter().selectRtfRendering();
setFileFilter(getWPFilter());
exportFile(null);
}
});
item = new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.NONE);
item.setText(textBundle.get("ExportAsGraphicsMenuItem")); //$NON-NLS-1$
item.setEnabled(GraphicsFileFilter.isCodecAvailable());
@ -953,6 +1011,7 @@ public class DiskExplorerTab {
outputStream.write(data);
outputStream.close();
} catch (Exception ex) {
ex.printStackTrace();
String errorMessage = ex.getMessage();
if (errorMessage == null) {
errorMessage = ex.getClass().getName();
@ -1596,6 +1655,7 @@ public class DiskExplorerTab {
}
}
} else { // No CTRL key
if ((event.stateMask & SWT.ALT) != SWT.ALT) { // Ignore ALT key combinations like alt-F4!
switch (event.keyCode) {
case SWT.F2: // Standard file display
changeCurrentFormat(FormattedDisk.FILE_DISPLAY_STANDARD);
@ -1611,6 +1671,7 @@ public class DiskExplorerTab {
getShowDeletedFilesToolItem().setSelection(isShowDeletedFiles());
fillFileTable(getCurrentFileList());
break;
}
}
}
}
@ -1939,6 +2000,10 @@ public class DiskExplorerTab {
return awpFilter;
}
protected GutenbergFileFilter getWPFilter() {
return wpFilter;
}
protected GraphicsFileFilter getGraphicsFilter() {
return graphicsFilter;
}

View File

@ -52,6 +52,7 @@ import com.webcodepro.applecommander.storage.filters.GraphicsFileFilter;
import com.webcodepro.applecommander.storage.filters.IntegerBasicFileFilter;
import com.webcodepro.applecommander.storage.filters.PascalTextFileFilter;
import com.webcodepro.applecommander.storage.filters.TextFileFilter;
import com.webcodepro.applecommander.storage.filters.GutenbergFileFilter;
import com.webcodepro.applecommander.ui.UiBundle;
import com.webcodepro.applecommander.ui.swt.filteradapter.ApplesoftFilterAdapter;
import com.webcodepro.applecommander.ui.swt.filteradapter.BusinessBASICFilterAdapter;
@ -187,10 +188,15 @@ public class FileViewerWindow {
imageManager.get(ImageManager.ICON_VIEW_AS_SPREADSHEET)
));
nativeFilterAdapterMap.put(AppleWorksWordProcessorFileFilter.class,
new TextFilterAdapter(this, textBundle.get("FileViewerWindow.WordprocessorButton"), //$NON-NLS-1$
textBundle.get("FileViewerWindow.WordprocessorTooltip"), //$NON-NLS-1$
imageManager.get(ImageManager.ICON_VIEW_AS_WORDPROCESSOR)
));
new TextFilterAdapter(this, textBundle.get("FileViewerWindow.WordprocessorButton"), //$NON-NLS-1$
textBundle.get("FileViewerWindow.WordprocessorTooltip"), //$NON-NLS-1$
imageManager.get(ImageManager.ICON_VIEW_AS_WORDPROCESSOR)
));
nativeFilterAdapterMap.put(GutenbergFileFilter.class,
new TextFilterAdapter(this, textBundle.get("FileViewerWindow.WordprocessorButton"), //$NON-NLS-1$
textBundle.get("FileViewerWindow.WordprocessorTooltip"), //$NON-NLS-1$
imageManager.get(ImageManager.ICON_VIEW_AS_WORDPROCESSOR)
));
nativeFilterAdapterMap.put(AssemblySourceFileFilter.class,
new TextFilterAdapter(this, textBundle.get("FileViewerWindow.AssemblyButton"), //$NON-NLS-1$
textBundle.get("FileViewerWindow.AssemblyTooltip"), //$NON-NLS-1$