Version 1.1.1 is the initial GPL release of AppleCommander.

This commit is contained in:
Robert Greene 2002-12-01 02:21:00 +00:00
commit ebf72c103e
65 changed files with 8568 additions and 0 deletions

8
.classpath Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
<classpathentry kind="src" path="/JUnit"/>
<classpathentry kind="lib" path="C:/Program Files/Eclipse 2.0/plugins/org.eclipse.swt.win32_2.0.2/ws/win32/swt.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

2
.cvsignore Normal file
View File

@ -0,0 +1,2 @@
bin
AppleCommander.preferences

18
.project Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>AppleCommander</name>
<comment></comment>
<projects>
<project>JUnit</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

21
HEADER Normal file
View File

@ -0,0 +1,21 @@
Attach the following code to the header of all source files.
/*
* 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
*/

270
LICENSE Normal file
View File

@ -0,0 +1,270 @@
The GNU General Public License (GPL)
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to
share and change it. By contrast, the GNU General Public License is
intended to guarantee your freedom to share and change free software--to
make sure the software is free for all its users. This General Public
License applies to most of the Free Software Foundation's software and to
any other program whose authors commit to using it. (Some other Free
Software Foundation software is covered by the GNU Library General Public
License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price.
Our General Public Licenses are designed to make sure that you have the
freedom to distribute copies of free software (and charge for this service
if you wish), that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free programs;
and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to
deny you these rights or to ask you to surrender the rights. These
restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or
for a fee, you must give the recipients all the rights that you have. You
must make sure that they, too, receive or can get the source code. And you
must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2)
offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that
everyone understands that there is no warranty for this free software. If
the software is modified by someone else and passed on, we want its
recipients to know that what they have is not the original, so that any
problems introduced by others will not reflect on the original authors'
reputations.
Finally, any free program is threatened constantly by software patents. We
wish to avoid the danger that redistributors of a free program will
individually obtain patent licenses, in effect making the program
proprietary. To prevent this, we have made it clear that any patent must
be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a
notice placed by the copyright holder saying it may be distributed under
the terms of this General Public License. The "Program", below, refers to
any such program or work, and a "work based on the Program" means either
the Program or any derivative work under copyright law: that is to say, a
work containing the Program or a portion of it, either verbatim or with
modifications and/or translated into another language. (Hereinafter,
translation is included without limitation in the term "modification".)
Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of running
the Program is not restricted, and the output from the Program is covered
only if its contents constitute a work based on the Program (independent
of having been made by running the Program). Whether that is true depends
on what theProgram does.
1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this
License and to the absence of any warranty; and give any other recipients
of the Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you
may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it,
thus forming a work based on the Program, and copy and distribute such
modifications or work under the terms of Section 1 above, provided that
you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices stating
that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole
or in part contains or is derived from the Program or any part thereof, to
be licensed as a whole at no charge to all third parties under the terms
of this License.
c) If the modified program normally reads commands interactively when run,
you must cause it, when started running for such interactive use in the
most ordinary way, to print or display an announcement including an
appropriate copyright notice and a notice that there is no warranty (or
else, saying that you provide a warranty) and that users may redistribute
the program under these conditions, and telling the user how to view a
copy of this License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on the
Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be
reasonably considered independent and separate works in themselves, then
this License, and its terms, do not apply to those sections when you
distribute them as separate works. But when you distribute the same
sections as part of a whole which is a work based on the Program, the
distribution of the whole must be on the terms of this License, whose
permissions for other licensees extend to the entire whole, and thus to
each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your
rights to work written entirely by you; rather, the intent is to exercise
the right to control the distribution of derivative or collective works
based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of a
storage or distribution medium does not bring the other work under the
scope of this License.
3. You may copy and distribute the Program (or a work based on it, under
Section 2) in object code or executable form under the terms of Sections 1
and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source
code, which must be distributed under the terms of Sections 1 and 2 above
on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to
give any third party, for a charge no more than your cost of physically
performing source distribution, a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to
distribute corresponding source code. (This alternative is allowed only
for noncommercial distribution and only if you received the program in
object code or executable form with such an offer, in accord with
Subsection b above.)
The source code for a work means the preferred form of the work for making
modifications to it. For an executable work, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the executable. However, as a special exception, the
source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major components
(compiler, kernel, and so on) of the operating system on which the
executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to
copy from a designated place, then offering equivalent access to copy the
source code from the same place counts as distribution of the source code,
even though third parties are not compelled to copy the source along with
the object code.
4. You may not copy, modify, sublicense, or distribute the Program except
as expressly provided under this License. Any attempt otherwise to copy,
modify, sublicense or distribute the Program is void, and will
automatically terminate your rights under this License. However, parties
who have received copies, or rights, from you under this License will not
have their licenses terminated so long as such parties remain in full
compliance.
5. You are not required to accept this License, since you have not signed
it. However, nothing else grants you permission to modify or distribute
the Program or its derivative works. These actions are prohibited by law
if you do not accept this License. Therefore, by modifying or distributing
the Program (or any work based on the Program), you indicate your
acceptance of this License to do so, and all its terms and conditions for
copying, distributing or modifying the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these terms
and conditions. You may not impose any further restrictions on the
recipients' exercise of the rights granted herein. You are not responsible
for enforcing compliance by third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot distribute
so as to satisfy simultaneously your obligations under this License and
any other pertinent obligations, then as a consequence you may not
distribute the Program at all. For example, if a patent license would not
permit royalty-free redistribution of the Program by all those who receive
copies directly or indirectly through you, then the only way you could
satisfy both it and this License would be to refrain entirely from
distribution of the Program.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any such
claims; this section has the sole purpose of protecting the integrity of
the free software distribution system, which is implemented by public
license practices. Many people have made generous contributions to the
wide range of software distributed through that system in reliance on
consistent application of that system; it is up to the author/donor to
decide if he or she is willing to distribute software through any other
system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain
countries either by patents or by copyrighted interfaces, the original
copyright holder who places the Program under this License may add an
explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions of
the General Public License from time to time. Such new versions will be
similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free
Software Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals of
preserving the free status of all derivatives of our free software and of
promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT
LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES
SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

3
build/manifest.mf Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Class-Path: swt.jar
Main-Class: com.webcodepro.applecommander.ui.AppleCommander

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,170 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* This class contains helper methods for dealing with Apple2 data.
* <p>
* Date created: Oct 5, 2002 4:16:16 PM
* @author: Rob Greene
*/
public class AppleUtil {
/**
* Compute the value of a word.
* Pulls value from buffer given the offset.
* A word is two bytes, in standard Apple LO/HI format.
*/
public static int getWordValue(byte[] buffer, int offset) {
return getWordValue(buffer[offset], buffer[offset+1]);
}
/**
* Compute the value of a word.
*/
public static int getWordValue(byte low, byte high) {
return getUnsignedByte(low) + getUnsignedByte(high)*256;
}
/**
* Compute the value of a 3 byte value. This may be ProDOS specific.
* Pulls value from buffer given the offset.
* Stored in standard Apple LO/HI format.
*/
public static int get3ByteValue(byte[] buffer, int offset) {
return getUnsignedByte(buffer[offset])
+ getUnsignedByte(buffer[offset+1])*256
+ getUnsignedByte(buffer[offset+2])*65536;
}
/**
* Extract out an unsigned byte as an int.
* All Java bytes are signed; need to convert to an int
* and remove the sign.
*/
public static int getUnsignedByte(byte value) {
return (int) value & 0xff;
}
/**
* Count the number of bits set in a byte.
*/
public static int getBitCount(byte byt) {
int count = 0;
for (int ix=0; ix<8; ix++) {
if (isBitSet(byt, ix)) count++;
}
return count;
}
/**
* Determine if a specific bit is set.
*/
public static boolean isBitSet(byte byt, int bit) {
byte[] masks = { (byte)0x01, (byte)0x02,(byte)0x04, (byte)0x08,
(byte)0x10, (byte)0x20, (byte)0x40, (byte)0x80 };
return (byt & masks[bit]) != 0;
}
/**
* Extract a string from the buffer.
*/
public static String getString(byte[] buffer, int offset, int length) {
byte[] value = new byte[length];
for (int i=0; i<length; i++) {
byte ch = buffer[offset+i];
ch &= 0x7f;
value[i] = ch;
}
return new String(value);
}
/**
* Extract a Pascal string from the buffer.
*/
public static String getPascalString(byte[] buffer, int offset) {
int length = getUnsignedByte(buffer[offset]);
return getString(buffer, offset+1, length);
}
/**
* Extract a Pascal date from the buffer.
*/
public static Date getPascalDate(byte[] buffer, int offset) {
int pascalDate = getWordValue(buffer, offset);
int month = pascalDate & 0x000f;
int day = (pascalDate & 0x00f0) >> 4;
int year = (pascalDate & 0xff00) >> 8;
if (year < 50) year+= 2000;
if (year < 100) year+= 1900;
GregorianCalendar gc = new GregorianCalendar(year, month, day);
return gc.getTime();
}
/**
* Extract a ProDOS string from the buffer.
*/
public static String getProdosString(byte[] buffer, int offset) {
int length = getUnsignedByte(buffer[offset]) & 0x0f;
return getString(buffer, offset+1, length);
}
/**
* Format a byte value as hexidecimal.
*/
public static String getFormattedByte(int byt) {
String[] values = { "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "A", "B", "C", "D", "E", "F" };
int byt1 = byt & 0x0f;
int byt2 = (byt & 0xf0) >> 4;
return values[byt2] + values[byt1];
}
/**
* Format a word value as hexidecimal.
*/
public static String getFormattedWord(int word) {
return getFormattedByte((word & 0xff00) >> 8)
+ getFormattedByte(word & 0x00ff);
}
/**
* Extract a ProDOS date from the buffer.
*/
public static Date getProdosDate(byte[] buffer, int offset) {
int ymd = getWordValue(buffer, offset);
if (ymd == 0) return null;
int hm = getWordValue(buffer, offset+2);
int day = ymd & 0x001f; // bits 0-4
int month = (ymd & 0x01e0) >> 5; // bits 5-8
int year = (ymd & 0xfe00) >> 9; // bits 9-15
int minute = hm & 0x003f; // bits 0-5
int hour = (hm & 0x1f00) >> 8; // bits 8-12
if (year < 50) year+= 2000;
if (year < 100) year+= 1900;
GregorianCalendar gc = new GregorianCalendar(year, month, day, hour, minute);
return gc.getTime();
}
}

View File

@ -0,0 +1,212 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Extract the contents of an AWP (AppleWorks word processor) document and
* convert to a text format.
* See format documentation at:
* http://www.gno.org/pub/apple2/doc/apple/filetypes/ftn.1a.xxxx
* <p>
* Date created: Nov 15, 2002 3:55:21 PM
* @author: Rob Greene
*/
public class AppleWorksWordProcessorFileFilter implements FileFilter {
public static final int RENDER_AS_TEXT = 0;
public static final int RENDER_AS_HTML = 1;
private int rendering = RENDER_AS_TEXT;
/**
* Constructor for AppleWorksWordProcessorFileFilter.
*/
public AppleWorksWordProcessorFileFilter() {
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();
if (fileData[4] != 0x4f) return null; // not an AWP file!
ByteArrayOutputStream byteArray = new ByteArrayOutputStream(fileData.length);
PrintWriter printWriter = new PrintWriter(byteArray, true);
if (isHtmlRendering()) {
printWriter.println("<html><style>BODY { font-family: monospace; }</style><body>");
}
boolean version3 = (fileData[183] != 0);
int offset = 300 + (version3 ? 2 : 0); // version 3.0's first line record is invalid
while (offset < fileData.length) {
int byte0 = AppleUtil.getUnsignedByte(fileData[offset++]);
int byte1 = AppleUtil.getUnsignedByte(fileData[offset++]);
if (byte0 == 0xff && byte1 == 0xff) { // end of file
break;
} else if (byte1 == 0xd0) { // Carriage return line records
handleReturn(printWriter);
} else if (byte1 > 0xd0) { // Command line records
if (isHtmlRendering()) {
offset = handleCommandRecordAsHtml(byte0, byte1, printWriter, offset);
}
} else { // Text records (assumed)
offset = handleTextRecord(fileData, printWriter, offset);
}
}
if (isHtmlRendering()) {
printWriter.println("</body></html>");
}
return byteArray.toByteArray();
}
/**
* Deal with an individual text record.
*/
protected int handleTextRecord(byte[] fileData, PrintWriter printWriter, int offset) {
int byte2 = AppleUtil.getUnsignedByte(fileData[offset++]);
int byte3 = AppleUtil.getUnsignedByte(fileData[offset++]);
boolean addReturn = (byte3 >= 0x80);
int length = (byte3 & 0x7f);
while (length > 0) {
byte ch = fileData[offset++];
length--;
if (ch < 0x20) { // special formatting character
if (isHtmlRendering()) handleSpecialCodesAsHtml(printWriter, ch);
} else {
if (isHtmlRendering() && ch == ' ') {
int extraSpaces = 0;
while (fileData[offset+extraSpaces] == ' ') {
extraSpaces++;
}
if (extraSpaces > 0) {
printWriter.print("&nbsp;");
while (fileData[offset] == ' ') {
offset++;
length--;
printWriter.print("&nbsp;");
}
} else {
printWriter.print((char)ch);
}
} else {
printWriter.print((char)ch);
}
}
}
if (addReturn) handleReturn(printWriter);
return offset;
}
/**
* Deal with carriage-return.
*/
protected void handleReturn(PrintWriter printWriter) {
if (isHtmlRendering()) printWriter.println("<br>");
else printWriter.println();
}
/**
* Process special coding of a text record.
*/
protected void handleSpecialCodesAsHtml(PrintWriter printWriter, byte ch) {
switch (ch) {
case 0x01: printWriter.print("<b>");
break;
case 0x02: printWriter.print("</b>");
break;
case 0x03: printWriter.print("<sup>");
break;
case 0x04: printWriter.print("</sup>");
break;
case 0x05: printWriter.print("<sub>");
break;
case 0x06: printWriter.print("</sub>");
break;
case 0x07: printWriter.print("<u>");
break;
case 0x08: printWriter.print("</u>");
break;
case 0x09: printWriter.print("[Page#]");
break;
case 0x0b: printWriter.print("&nbsp;");
break;
case 0x0e: SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy");
printWriter.print(dateFormat.format(new Date()));
break;
case 0x0f: SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
printWriter.print(timeFormat.format(new Date()));
break;
}
}
/**
* Deal with an individual command line record.
*/
protected int handleCommandRecordAsHtml(int byte0, int byte1,
PrintWriter printWriter, int offset) {
switch (byte1) {
case 0xd7: printWriter.println("<style>BODY: text-align: right;</style>");
break;
case 0xdf: printWriter.println("<style>BODY: text-align: justify;</style>");
break;
case 0xe0: printWriter.println("<style>BODY: text-align: left;</style>");
break;
case 0xe1: printWriter.println("<style>BODY: text-align: center;</style>");
break;
case 0xee: for (int i=0; i<byte0; i++) {
printWriter.println("<br>");
}
break;
}
return offset;
}
/**
* 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";
if (isHtmlRendering()) extension = ".html";
if (!fileName.toLowerCase().endsWith(extension)) {
fileName = fileName + extension;
}
return fileName;
}
/**
* Set the rendering method.
*/
public 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;
}
}

View File

@ -0,0 +1,124 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
/**
* Filter the given file as an Applesoft file.
* <p>
* Applesoft memory format:<br>
* [Line]<br>
* ...
* [Line]<br>
* <br>
* where <Line> is:<br>
* [Next addr - $0000 is end of program] (word)<br>
* [Line no] (word)<br>
* [Tokens and/or characters]<br>
* [End-of-line marker: $00 bytes]
* <p>
* Date created: Nov 2, 2002 10:04:10 PM
* @author: Rob Greene
*/
public class ApplesoftFileFilter implements FileFilter {
private static String tokens[] = { // starts at $80
" END ", " FOR ", " NEXT ", " DATA ", " INPUT ", " DEL ",
" DIM ", " READ ", " GR ", " TEXT ", " PR# ", " IN# ",
" CALL ", " PLOT ", " HLIN ", " VLIN ", " HGR2 ", " HGR ",
" HCOLOR= ", " HPLOT ", " DRAW ", " XDRAW ", " HTAB ", " HOME ",
" ROT= ", " SCALE= ", " SHLOAD ", " TRACE ", " NOTRACE ", " NORMAL ",
" INVERSE ", " FLASH ", " COLOR= ", " POP ", " VTAB ", " HIMEM: ",
" LOMEM: ", " ONERR ", " RESUME ", " RECALL ", " STORE ", " SPEED= ",
" LET ", " GOTO ", " RUN ", " IF ", " RESTORE ", " & ",
" GOSUB ", " RETURN ", " REM ", " STOP ", " ON ", " WAIT ",
" LOAD ", " SAVE ", " DEF ", " POKE ", " PRINT ", " CONT ",
" LIST ", " CLEAR ", " GET ", " NEW ", " TAB( ", " TO ",
" FN ", " SPC( ", " THEN ", " AT ", " NOT ", " STEP ",
" +", " -", " *", "/", " ^", " AND ",
" OR ", " >", " =", " <", " SGN", " INT",
" ABS", " USR", " FRE", " SCRN( ", " PDL", " POS",
" SQR", " RND", " LOG", " EXP", " COS", " SIN",
" TAN", " ATN", " PEEK", " LEN", " STR$", " VAL",
" ASC", " CHR$", " LEFT$", " RIGHT$", " MID$ " };
/**
* Constructor for ApplesoftFileFilter.
*/
public ApplesoftFileFilter() {
super();
}
/**
* Process the given FileEntry and return a text image of the Applesoft file.
* @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 * 2);
PrintWriter printWriter = new PrintWriter(byteArray, true);
while (offset < fileData.length) {
int nextAddress = AppleUtil.getWordValue(fileData, offset);
if (nextAddress == 0) break; // next address of 0 indicates end of program
offset+= 2;
int lineNumber = AppleUtil.getWordValue(fileData, offset);
offset+= 2;
printWriter.print(lineNumber);
printWriter.print(' ');
while (fileData[offset] != 0) {
byte byt = fileData[offset++];
if ((byt & 0x80) != 0) {
int token = AppleUtil.getUnsignedByte(byt) - 0x80;
if (token >= tokens.length) {
printWriter.print("<UNKNOWN TOKEN>");
} else {
String tokenString = tokens[token];
printWriter.print(tokenString);
}
} else {
char ch = (char)byt;
if (ch < 0x20) {
printWriter.print("<CTRL-");
printWriter.print((char)('@' + ch));
printWriter.print(">");
} else {
printWriter.print(ch);
}
}
}
printWriter.println();
offset++; // skip to next line
}
return byteArray.toByteArray();
}
/**
* Give suggested file name.
*/
public String getSuggestedFileName(FileEntry fileEntry) {
String fileName = fileEntry.getFilename().trim();
if (!fileName.toLowerCase().endsWith(".bas")) {
fileName = fileName + ".bas";
}
return fileName;
}
}

View File

@ -0,0 +1,54 @@
/*
* 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;
/**
* Filter the given file data to be the appropriate length.
* <p>
* Date created: Nov 2, 2002 9:07:52 PM
* @author: Rob Greene
*/
public class BinaryFileFilter implements FileFilter {
/**
* Constructor for BinaryFileFilter.
*/
public BinaryFileFilter() {
super();
}
/**
* Process the given FileEntry and return a byte array with filtered data.
* @see com.webcodepro.applecommander.storage.FileFilter#filter(byte[])
*/
public byte[] filter(FileEntry fileEntry) {
return fileEntry.getFileData(); // should be nothing to do
}
/**
* Give suggested file name.
*/
public String getSuggestedFileName(FileEntry fileEntry) {
String fileName = fileEntry.getFilename().trim();
if (!fileName.toLowerCase().endsWith(".dump")) {
fileName = fileName + ".dump";
}
return fileName;
}
}

View File

@ -0,0 +1,316 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
/**
* Abstract representation of an Apple2 disk (floppy, 800k, hard disk).
* <p>
* Date created: Oct 3, 2002 10:59:47 PM
* @author: Rob Greene
*/
public class Disk {
/**
* Specifies a filter to be used in determining filetypes which are supported.
* This works from a file extension, so it may or may not apply to the Macintosh.
*/
public class FilenameFilter {
private String names;
private String extensions;
public FilenameFilter(String names, String extensions) {
this.names = names;
this.extensions = extensions;
}
public String getExtensions() {
return extensions;
}
public String getNames() {
return names;
}
}
public static final int BLOCK_SIZE = 512;
public static final int SECTOR_SIZE = 256;
public static final int APPLE_140KB_DISK = 143360;
public static final int APPLE_800KB_DISK = 819200;
public static final int APPLE_800KB_2IMG_DISK = APPLE_800KB_DISK + 0x40;
private static FilenameFilter[] filenameFilters;
private byte[] diskImage;
private String filename;
/**
* Get the supported file filters supported by the Disk interface.
* This is due to the fact that FilenameFilter is an innerclass of Disk -
* without an instance of the class, the filters cannot be created.
*/
public static FilenameFilter[] getFilenameFilters() {
if (filenameFilters == null) {
new Disk();
}
return filenameFilters;
}
/**
* Constructor for a Disk - used only to generate FilenameFilter objects.
*/
private Disk() {
filenameFilters = new FilenameFilter[] {
new FilenameFilter("All Emulator Images",
"*.do; *.dsk; *.po; *.2mg; *.2img; *.hdv; *.do.gz; *.dsk.gz; *.po.gz; *.2mg.gz; *.2img.gz"),
new FilenameFilter("140K DOS 3.3 Ordered Images (*.do, *.dsk)",
"*.do; *.dsk; *.do.gz; *.dsk.gz"),
new FilenameFilter("140K ProDOS Ordered Images (*.po)",
"*.po; *.po.gz"),
new FilenameFilter("800K ProDOS Ordered Images (*.2mg, *.2img)",
"*.2mg; *.2img; *.2mg.gz, *.2img.gz"),
new FilenameFilter("ApplePC Hard Disk Images (*.hdv)",
"*.hdv"),
new FilenameFilter("All Compressed Images",
"*.do.gz; *.dsk.gz; *.po.gz; *.2mg.gz; *.2img.gz"),
new FilenameFilter("All Files",
"*.*")
};
}
/**
* Construct a Disk with the given byte array.
*/
protected Disk(String filename, byte[] diskImage) {
this.diskImage = diskImage;
this.filename = filename;
}
/**
* Construct a Disk and load the specified file.
* Read in the entire contents of the file.
*/
public Disk(String filename) throws IOException {
this.filename = filename;
InputStream input = new FileInputStream(filename);
if (isCompressed()) {
input = new GZIPInputStream(input);
}
int diskSize = APPLE_140KB_DISK;
if (is2ImgOrder()) {
diskSize = APPLE_800KB_2IMG_DISK;
}
ByteArrayOutputStream diskImageByteArray =
new ByteArrayOutputStream(diskSize);
byte[] data = new byte[1024];
int bytes;
while ((bytes = input.read(data)) > 0) {
diskImageByteArray.write(data, 0, bytes);
}
input.close();
this.diskImage = diskImageByteArray.toByteArray();
}
/**
* Determine type of disk, and return the appropriate
* FormattedDisk object. Returns null if none are
* recognized.
*/
public FormattedDisk getFormattedDisk() {
if (isProdosFormat()) {
return new ProdosFormatDisk(filename, diskImage);
} else if (isDosFormat()) {
return new DosFormatDisk(filename, diskImage);
} else if (isPascalFormat()) {
return new PascalFormatDisk(filename, diskImage);
} else if (isRdosFormat()) {
return new RdosFormatDisk(filename, diskImage);
}
return null;
}
/**
* Returns the diskImage.
* @return byte[]
*/
public byte[] getDiskImage() {
return diskImage;
}
/**
* Extract a portion of the disk image.
*/
public byte[] readBytes(int start, int length) {
byte[] buffer = new byte[length];
System.arraycopy(diskImage, start + (is2ImgOrder() ? 0x40 : 0), buffer, 0, length);
return buffer;
}
/**
* Returns the filename.
* @return String
*/
public String getFilename() {
return filename;
}
/**
* Indicate if this disk is GZIP compressed.
*/
public boolean isCompressed() {
return filename.toLowerCase().endsWith(".gz");
}
/**
* Indicate if this disk is ProDOS ordered (beginning with block 0).
*/
public boolean isProdosOrder() {
return filename.toLowerCase().endsWith(".po")
|| filename.toLowerCase().endsWith(".po.gz")
|| is2ImgOrder()
|| filename.toLowerCase().endsWith(".hdv");
}
/**
* Indicate if this disk is DOS ordered (T0,S0 - T35,S15).
*/
public boolean isDosOrder() {
return filename.toLowerCase().endsWith(".do")
|| filename.toLowerCase().endsWith(".do.gz")
|| filename.toLowerCase().endsWith(".dsk")
|| filename.toLowerCase().endsWith(".dsk.gz");
}
/**
* Indicate if this disk is a 2IMG disk.
* This is ProDOS ordered, but with a header on the disk.
*/
public boolean is2ImgOrder() {
return filename.toLowerCase().endsWith(".2img")
|| filename.toLowerCase().endsWith(".2img.gz")
|| filename.toLowerCase().endsWith(".2mg")
|| filename.toLowerCase().endsWith(".2mg.gz");
}
/**
* Identify the size of this disk.
*/
public int getPhysicalSize() {
return diskImage.length;
}
/**
* Get the block from the disk image.
*/
public byte[] readBlock(int block) {
if (block * BLOCK_SIZE > getPhysicalSize()) {
return null;
} else {
if (isProdosOrder()) {
return readBytes(block*BLOCK_SIZE, BLOCK_SIZE);
} else if (isDosOrder()) {
int[] sectorMapping1 = { 0, 13, 11, 9, 7, 5, 3, 1 };
int[] sectorMapping2 = { 14, 12, 10, 8, 6, 4, 2, 15 };
int track = block / 8;
int sectorOffset = block % 8;
int sector1 = sectorMapping1[sectorOffset];
int sector2 = sectorMapping2[sectorOffset];
int physicalLocation1 = (track * 16 + sector1) * SECTOR_SIZE;
int physicalLocation2 = (track * 16 + sector2) * SECTOR_SIZE;
byte[] data = new byte[BLOCK_SIZE];
System.arraycopy(readBytes(physicalLocation1, SECTOR_SIZE),
0, data, 0, SECTOR_SIZE);
System.arraycopy(readBytes(physicalLocation2, SECTOR_SIZE),
0, data, SECTOR_SIZE, SECTOR_SIZE);
return data;
} else {
return null;
}
}
}
/**
* Retrieve the specified sector.
*/
public byte[] readSector(int track, int sector) {
if ((track * 16 + sector) * SECTOR_SIZE > getPhysicalSize()) {
return null;
} else if (isProdosOrder()) {
// what block a sector belongs to:
int[] blockInterleave = { 0, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 7 };
// where in that block a sector resides:
int[] blockOffsets = { 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 };
return readBytes(
((track * 8) + blockInterleave[sector]) * BLOCK_SIZE
+ blockOffsets[sector] * SECTOR_SIZE, SECTOR_SIZE);
} else if (isDosOrder()) {
return readBytes((track * 16 + sector) * SECTOR_SIZE, SECTOR_SIZE);
}
return null;
}
/**
* Test the disk format to see if this is a ProDOS formatted
* disk.
*/
public boolean isProdosFormat() {
byte[] prodosVolumeDirectory = readBlock(2);
return prodosVolumeDirectory[0] == 0 &&
prodosVolumeDirectory[1] == 0 &&
(prodosVolumeDirectory[4]&0xf0) == 0xf0;
}
/**
* Test the disk format to see if this is a DOS 3.3 formatted
* disk.
*/
public boolean isDosFormat() {
byte[] vtoc = readSector(17, 0);
return vtoc[0x01] == 17 // expect catalog to start on track 17
&& vtoc[0x02] == 15 // expect catalog to start on sector 15
&& vtoc[0x03] == 3 // expect DOS release number of 3
&& vtoc[0x27] == 122 // expect 122 tract/sector pairs per sector
&& vtoc[0x34] == 35 // expect 35 tracks per disk (140KB disk only!)
&& vtoc[0x35] == 16 // expect 16 sectors per disk (140KB disk only!)
&& vtoc[0x36] == 0 // bytes per sector (low byte)
&& vtoc[0x37] == 1; // bytes per sector (high byte)
}
/**
* Test the disk format to see if this is a Pascal formatted
* disk.
*/
public boolean isPascalFormat() {
byte[] directory = readBlock(2);
return directory[0] == 0 && directory[1] == 0
&& directory[2] == 6 && directory[3] == 0
&& directory[4] == 0 && directory[5] == 0;
}
/**
* Test the disk format to see if this is a RDOS formatted
* disk.
*/
public boolean isRdosFormat() {
byte[] block = readSector(0, 0x0d);
String id = AppleUtil.getString(block, 0xe0, 4);
return "RDOS".equals(id);
}
}

View File

@ -0,0 +1,89 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
/**
* Helper class to load Apple2 disk images.
* This class identifies the format, and the type of disk, loading the
* appropriate disk management class.
* <p>
* Date created: Oct 3, 2002 10:54:34 PM
* @author: Rob Greene
* @deprecated
*/
public class DiskHelper {
public static final int APPLE_140KB_DISK = 143360;
public static final int APPLE_800KB_DISK = 819200;
public static final int APPLE_800KB_2IMG_DISK = APPLE_800KB_DISK + 0x40;
/**
* Load an Apple2 disk from a given filename.
* Determine type of disk, load approparitely, and
* construct the appropriate Disk object.
*/
public static FormattedDisk load(String filename) throws IOException {
if (filename == null) return null;
byte[] diskImage = loadDisk(filename);
Disk test = new Disk(filename, diskImage);
if (test.isProdosFormat()) {
return new ProdosFormatDisk(filename, diskImage);
} else if (test.isDosFormat()) {
return new DosFormatDisk(filename, diskImage);
} else if (test.isPascalFormat()) {
return new PascalFormatDisk(filename, diskImage);
} else if (test.isRdosFormat()) {
return new RdosFormatDisk(filename, diskImage);
}
// FIXME: Should return unknown disk Disk
return null;
}
/**
* Read in a disk in the same order as the image.
* Disk itself will handle location translation.
*/
private static byte[] loadDisk(String filename) throws IOException {
InputStream input = new FileInputStream(filename);
if (filename.toLowerCase().endsWith(".gz")) {
input = new GZIPInputStream(input);
}
int diskSize = APPLE_140KB_DISK;
if (filename.toLowerCase().endsWith(".2img") || filename.toLowerCase().endsWith(".2mg")) {
diskSize = APPLE_800KB_2IMG_DISK;
}
ByteArrayOutputStream diskImage =
new ByteArrayOutputStream(diskSize);
byte[] data = new byte[1024];
int bytes;
while ((bytes = input.read(data)) > 0) {
diskImage.write(data, 0, bytes);
}
input.close();
return diskImage.toByteArray();
}
}

View File

@ -0,0 +1,271 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a DOS file entry on disk.
* <p>
* Date created: Oct 4, 2002 5:15:25 PM
* @author: Rob Greene
*/
public class DosFileEntry implements FileEntry {
private byte[] fileEntry;
private DosFormatDisk disk;
/**
* Constructor for DosFileEntry.
*/
public DosFileEntry(byte[] fileEntry, DosFormatDisk disk) {
super();
this.fileEntry = fileEntry;
this.disk = disk;
}
/**
* Return the name of this file.
* @see com.webcodepro.applecommander.storage.FileEntry#getFilename()
*/
public String getFilename() {
byte[] filename = new byte[30];
System.arraycopy(fileEntry, 3, filename, 0, filename.length);
for (int i=0; i<filename.length; i++) {
filename[i] &= 0x7f;
}
return new String(filename).trim();
}
/**
* Return the filetype of this file.
* @see com.webcodepro.applecommander.storage.FileEntry#getFiletype()
*/
public String getFiletype() {
int filetype = (AppleUtil.getUnsignedByte(fileEntry[2]) & 0x7f);
if (filetype == 0x00) return "T";
// the "^" operator is exclusive or - used to ensure only that
// bit was turned on. if others are turned on, fall through and
// return a "?" as the file type
if ((filetype ^ 0x01) == 0) return "I";
if ((filetype ^ 0x02) == 0) return "A";
if ((filetype ^ 0x04) == 0) return "B";
if ((filetype ^ 0x08) == 0) return "S";
if ((filetype ^ 0x10) == 0) return "R";
if ((filetype ^ 0x20) == 0) return "a";
if ((filetype ^ 0x40) == 0) return "b";
return "?"; // should never occur (read the code!)
}
/**
* Identify if this file is locked.
* @see com.webcodepro.applecommander.storage.FileEntry#isLocked()
*/
public boolean isLocked() {
return (fileEntry[2] & 0x80) != 0;
}
/**
* Compute the size of this file (in bytes).
* @see com.webcodepro.applecommander.storage.FileEntry#getSize()
*/
public int getSize() {
byte[] rawdata = null;
if (!isDeleted()) {
rawdata = disk.getFileData(this);
}
// default to nothing special, just compute from number of sectors
int size = (getSectorsUsed()-1) * Disk.SECTOR_SIZE;
if (rawdata != null) {
if ("B".equals(getFiletype())) {
// binary
return AppleUtil.getWordValue(rawdata, 2);
} else if ("A".equals(getFiletype()) || "I".equals(getFiletype())) {
// applesoft, integer basic
return AppleUtil.getWordValue(rawdata, 0);
}
}
return size;
}
/**
* Compute the number of sectors used.
*/
public int getSectorsUsed() {
return AppleUtil.getUnsignedByte(fileEntry[0x21])
+ AppleUtil.getUnsignedByte(fileEntry[0x22])*16;
}
/**
* Identify if this is a directory file.
* @see com.webcodepro.applecommander.storage.FileEntry#isDirectory()
*/
public boolean isDirectory() {
return false;
}
/**
* Retrieve the list of files in this directory.
* Always returns null for DOS.
* @see com.webcodepro.applecommander.storage.FileEntry#getFiles()
*/
public List getFiles() {
return null;
}
/**
* Identify if this file has been deleted.
* @see com.webcodepro.applecommander.storage.FileEntry#isDeleted()
*/
public boolean isDeleted() {
return AppleUtil.getUnsignedByte(fileEntry[0]) == 0xff;
}
/**
* 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() ? "*" : " ");
list.add(getFiletype());
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getSectorsUsed()));
list.add(getFilename());
break;
case FormattedDisk.FILE_DISPLAY_DETAIL:
list.add(isLocked() ? "*" : " ");
list.add(getFiletype());
list.add(getFilename());
list.add(numberFormat.format(getSize()));
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getSectorsUsed()));
list.add(isDeleted() ? "Deleted" : "");
list.add("T" + getTrack() + " S" + getSector());
break;
default: // FILE_DISPLAY_STANDARD
list.add(getFilename());
list.add(getFiletype());
list.add(numberFormat.format(getSize()));
list.add(isLocked() ? "Locked" : "");
break;
}
return list;
}
/**
* Get the track of first track/sector list sector.
*/
public int getTrack() {
return AppleUtil.getUnsignedByte(fileEntry[0x00]);
}
/**
* Get the sector of first track/sector list sector.
*/
public int getSector() {
return AppleUtil.getUnsignedByte(fileEntry[0x01]);
}
/**
* Get file data. This handles any operating-system specific issues.
* Specifically, DOS 3.3 places address and length into binary files
* and length into Applesoft files.
*/
public byte[] getFileData() {
byte[] rawdata = disk.getFileData(this);
byte[] filedata;
if (isBinaryFile()) {
int length = AppleUtil.getWordValue(rawdata, 2);
filedata = new byte[length];
System.arraycopy(rawdata, 4, filedata, 0, length);
} else if (isApplesoftBasicFile() || isIntegerBasicFile()) {
filedata = new byte[getSize()];
System.arraycopy(rawdata, 2, filedata, 0, filedata.length);
} else {
filedata = rawdata;
}
return filedata;
}
/**
* 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() {
if (isApplesoftBasicFile()) {
return new ApplesoftFileFilter();
} else if (isIntegerBasicFile()) {
return new IntegerBasicFileFilter();
} else if (isTextFile()) {
return new TextFileFilter();
} else if (isBinaryFile()) {
int size = getSize();
// the minimum size is guessed a bit - I don't remember, but maybe there
// are 8 spare bytes at the end of the graphics screen
GraphicsFileFilter filter = new GraphicsFileFilter();
if (size >= 8185 && size <= 8192) {
filter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
return filter;
} else if (size >= 16377 && size <= 16384) {
filter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
return filter;
}
// fall through to BinaryFileFilter...
}
return new BinaryFileFilter();
}
/**
* Determine if this is a text file.
*/
public boolean isTextFile() {
return "T".equals(getFiletype());
}
/**
* Determine if this is an Applesoft BASIC file.
*/
public boolean isApplesoftBasicFile() {
return "A".equals(getFiletype());
}
/**
* Determine if this is an Integer BASIC file.
*/
public boolean isIntegerBasicFile() {
return "I".equals(getFiletype());
}
/**
* Determine if this is a binary file.
*/
public boolean isBinaryFile() {
return "B".equals(getFiletype());
}
}

View File

@ -0,0 +1,398 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
/**
* Manages a disk that is in Apple DOS 3.3 format.
* <p>
* Date created: Oct 4, 2002 12:29:23 AM
* @author: Rob Greene
*/
public class DosFormatDisk extends FormattedDisk {
/**
* Indicates the length in bytes of the DOS file entry field.
*/
public static final int FILE_DESCRIPTIVE_ENTRY_LENGTH = 35;
/**
* 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;
/**
* Use this inner interface for managing the disk usage data.
* This offloads format-specific implementation to the implementing class.
*/
private class DosDiskUsage 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.
*/
public boolean isFree() {
if (location == null || location.length != 2) {
throw new IllegalArgumentException("Invalid dimension for isFree! Did you call next first?");
}
byte[] vtoc = getVtoc();
byte byt = vtoc[0x38 + (location[TRACK_LOCATION_INDEX] * 4)
+ (location[SECTOR_LOCATION_INDEX] / 8)];
boolean free = AppleUtil.isBitSet(byt, 7 - (location[SECTOR_LOCATION_INDEX] % 8));
return free;
}
public boolean isUsed() {
return !isFree();
}
}
/**
* Constructor for DosFormatDisk.
* @param filename
* @param diskImage
* @param order
*/
public DosFormatDisk(String filename, byte[] diskImage) {
super(filename, diskImage);
}
/**
* Identify the operating system format of this disk as DOS 3.3.
* @see com.webcodepro.applecommander.storage.Disk#getFormat()
*/
public String getFormat() {
return "DOS 3.3";
}
/**
* Retrieve a list of files.
* @see com.webcodepro.applecommander.storage.Disk#getFiles()
*/
public List getFiles() {
List list = new ArrayList();
byte[] vtoc = getVtoc();
int track = AppleUtil.getUnsignedByte(vtoc[1]);
int sector = AppleUtil.getUnsignedByte(vtoc[2]);
while (track != 0) { // iterate through all catalog sectors
byte[] catalogSector = readSector(track, sector);
int offset = 0x0b;
while (offset < 0xff) { // iterate through all entries
byte[] entry = new byte[FILE_DESCRIPTIVE_ENTRY_LENGTH];
System.arraycopy(catalogSector, offset, entry, 0, entry.length);
if (entry[0] != 0) {
list.add(new DosFileEntry(entry, this));
}
offset+= entry.length;
}
track = catalogSector[1];
sector = catalogSector[2];
}
return list;
}
/**
* Identify if this disk format as not capable of having directories.
* @see com.webcodepro.applecommander.storage.Disk#hasDirectories()
*/
public boolean canHaveDirectories() {
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.Disk#getFreeSpace()
*/
public int getFreeSpace() {
return getFreeSectors() * SECTOR_SIZE;
}
/**
* Comput the number of free sectors available on the disk.
*/
public int getFreeSectors() {
byte[] vtoc = getVtoc();
int freeSectors = 0;
for (int offset=0x38; offset<0xff; offset++) {
byte bitmap = vtoc[offset];
freeSectors+= AppleUtil.getBitCount(bitmap);
}
return freeSectors;
}
/**
* Return the amount of used space in bytes.
* @see com.webcodepro.applecommander.storage.Disk#getUsedSpace()
*/
public int getUsedSpace() {
return getUsedSectors() * SECTOR_SIZE;
}
/**
* 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 DOS disk name. Basically, the DISK VOLUME #xxx
* that a CATALOG command would show. Note that Java bytes are
* signed, so a little mojo is in order.
* @see com.webcodepro.applecommander.storage.Disk#getDiskName()
*/
public String getDiskName() {
int volumeNumber = AppleUtil.getUnsignedByte(getVtoc()[0x06]);
return "DISK VOLUME #" + volumeNumber;
}
/**
* Return the VTOC (Volume Table Of Contents).
*/
protected byte[] getVtoc() {
return readSector(0x11, 0);
}
/**
* Get the disk usage bitmap. The size could vary and is stored in the
* VTOC.
* @see com.webcodepro.applecommander.storage.FormattedDisk#getBitmap()
* @deprecated DOS 3.3.po comes up with 448 entries in the bitmap?!
*/
public BitSet getBitmap() {
byte[] vtoc = getVtoc();
int tracks = getTracks();
int sectors = getSectors();
BitSet bitmap = new BitSet(tracks * sectors);
// individually test each track & sector - should handle 140K or 400K disks!
int count = 0;
for (int t=0; t<tracks; t++) {
for (int s=0; s<sectors; s++) {
byte byt = vtoc[0x38 + (t * 4) + (s / 8)];
boolean free = AppleUtil.isBitSet(byt, 7 - (s % 8));
bitmap.set(count, free);
count++;
}
}
return bitmap;
}
/**
* Get the free setting for the bitmap at a specific location.
* The location is specified by an int array to support block
* and track/sector formatted disks.
* @deprecated
*/
public boolean isLocationFree(int[] location) {
if (location == null || location.length != 2) {
throw new IllegalArgumentException("Invalid dimension for isLocationFree!");
}
byte[] vtoc = getVtoc();
byte byt = vtoc[0x38 + (location[TRACK_LOCATION_INDEX] * 4)
+ (location[SECTOR_LOCATION_INDEX] / 8)];
boolean free = AppleUtil.isBitSet(byt, 7 - (location[SECTOR_LOCATION_INDEX] % 8));
return free;
}
/**
* Get the disk usage iterator.
*/
public DiskUsage getDiskUsage() {
return new DosDiskUsage();
}
/**
* Get the number of tracks on this disk.
*/
public int getTracks() {
byte[] vtoc = getVtoc();
return AppleUtil.getUnsignedByte(vtoc[0x34]);
}
/**
* Get the number of sectors on this disk.
*/
public int getSectors() {
byte[] vtoc = getVtoc();
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[] { "Track", "Sector" };
}
/**
* Get DOS-specific disk information.
*/
public List getDiskInformation() {
List list = super.getDiskInformation();
list.add(new DiskInformation("Total Sectors", getTotalSectors()));
list.add(new DiskInformation("Free Sectors", getFreeSectors()));
list.add(new DiskInformation("Used Sectors", getUsedSectors()));
list.add(new DiskInformation("Tracks On Disk", getTracks()));
list.add(new DiskInformation("Sectors On Disk", getSectors()));
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));
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Size (sectors)", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
break;
case FILE_DISPLAY_DETAIL:
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Size (bytes)", 6, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Size (sectors)", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Deleted?", 7, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Track/Sector List", 7, FileColumnHeader.ALIGN_CENTER));
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; // FIXME - not implemented
}
/**
* Indicates if this disk image can create a file.
*/
public boolean canCreateFile() {
return false; // FIXME - not implemented
}
/**
* Indicates if this disk image can delete a file.
*/
public boolean canDeleteFile() {
return false; // FIXME - not implemented
}
/**
* Get the data associated with the specified FileEntry.
*/
public byte[] getFileData(FileEntry fileEntry) {
if ( !(fileEntry instanceof DosFileEntry)) {
throw new IllegalArgumentException("Most have a DOS 3.3 file entry!");
}
DosFileEntry dosEntry = (DosFileEntry) fileEntry;
// Size is calculated by sectors used - not actual size - as size varies
// on filetype, etc.
byte[] fileData = new byte[(dosEntry.getSectorsUsed()-1) * SECTOR_SIZE];
int track = dosEntry.getTrack();
int sector = dosEntry.getSector();
int offset = 0;
while (track != 0) {
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]);
byte[] sectorData = readSector(t,s);
System.arraycopy(sectorData, 0, fileData, offset, sectorData.length);
offset+= sectorData.length;
}
}
return fileData;
}
}

View File

@ -0,0 +1,91 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.List;
/**
* Represents a file entry on disk - not the data.
* <p>
* Date created: Oct 4, 2002 4:46:42 PM
* @author: Rob Greene
*/
public interface FileEntry {
/**
* Return the name of this file.
*/
public String getFilename();
/**
* Return the filetype of this file.
* This will be OS specific.
*/
public String getFiletype();
/**
* Identify if this file is locked.
*/
public boolean isLocked();
/**
* Compute the size of this file (in bytes).
*/
public int getSize();
/**
* Identify if this is a directory file.
*/
public boolean isDirectory();
/**
* Retrieve the list of files in this directory.
* Note that if this is not a directory, the return
* value should be null. If this a directory, the
* return value should always be a list - a directory
* with 0 entries returns an empty list.
*/
public List getFiles();
/**
* Identify if this file has been deleted.
*/
public boolean isDeleted();
/**
* 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);
/**
* Get file data. This handles any operating-system specific issues.
* Specifically, DOS 3.3 places address and length into binary files
* and length into Applesoft files.
*/
public byte[] getFileData();
/**
* 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.
*/
public FileFilter getSuggestedFilter();
}

View File

@ -0,0 +1,99 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.Comparator;
/**
* Sort FileEntry objects by the columnIndex. Mostly useful to the interface.
* The columns are tested to see if they are numerical - if so, an Integer compare
* is done (instead of a String).
* <p>
* Date created: Oct 27, 2002 8:24:39 PM
* @author: Rob Greene
*/
public class FileEntryComparator implements Comparator {
private int columnIndex;
private int displayMode;
/**
* Construct a FileEntryComparator for the given columnIndex.
*/
public FileEntryComparator(int columnIndex, int displayMode) {
this.columnIndex = columnIndex;
this.displayMode = displayMode;
}
/**
* Compare two FileEntry objects.
* @see java.util.Comparator#compare(Object, Object)
*/
public int compare(Object o1, Object o2) {
if (!(o1 instanceof FileEntry) || !(o2 instanceof FileEntry)) {
return 0;
}
if (o1 == null || o2 == null) {
return ((o1 == null) ? -1 : 0) + ((o2 == null) ? 1 : 0);
}
FileEntry entry1 = (FileEntry) o1;
FileEntry entry2 = (FileEntry) o2;
String column1 = (String) entry1.getFileColumnData(displayMode).get(columnIndex);
String column2 = (String) entry2.getFileColumnData(displayMode).get(columnIndex);
if (isAllDigits(column1) && isAllDigits(column2)) {
int int1 = toInt(column1);
int int2 = toInt(column2);
return int1 - int2;
} else {
return column1.compareTo(column2);
}
}
/**
* Test for digits in the screen.
*/
protected boolean isAllDigits(String string) {
if (string == null || string.length() == 0) return false;
for (int i=0; i<string.length(); i++) {
char ch = string.charAt(i);
if (!Character.isDigit(ch) && ch != ',') {
return false;
}
}
return true;
}
/**
* Convert String to int.
*/
protected int toInt(String string) {
StringBuffer buf = new StringBuffer(string.length());
for (int i=0; i<string.length(); i++) {
char ch = string.charAt(i);
if (Character.isDigit(ch)) {
buf.append(ch);
}
}
return Integer.parseInt(buf.toString());
}
}

View File

@ -0,0 +1,40 @@
/*
* 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;
/**
* A file filter taks a buffer of bytes and filters or converts the bytes
* into another format. An example would be to filter Apple text by
* removing the high bit from each byte and stripping out all $00 values,
* as that signified either the end of a file or filler space.
* <p>
* Date created: Nov 2, 2002 9:02:47 PM
* @author: Rob Greene
*/
public interface FileFilter {
/**
* Process the given FileEntry and return a byte array with filtered data.
*/
public byte[] filter(FileEntry fileEntry);
/**
* Give suggested file name.
*/
public String getSuggestedFileName(FileEntry fileEntry);
}

View File

@ -0,0 +1,299 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Abstract representation of a formatted Apple2 disk (floppy, 800k, hard disk).
* <p>
* Date created: Oct 5, 2002 3:51:44 PM
* @author: Rob Greene
*/
public abstract class FormattedDisk extends Disk {
/**
* Use this inner class for label/value mappings in the disk info page.
*/
public class DiskInformation {
private String label;
private String value;
public DiskInformation(String label, String value) {
this.label = label;
this.value = value;
}
public DiskInformation(String label, int value) {
this.label = label;
this.value = Integer.toString(value);
}
public DiskInformation(String label, Date value) {
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
this.label = label;
if (value != null) {
this.value = dateFormat.format(value);
} else {
this.value = "-None-";
}
}
public String getLabel() {
return this.label;
}
public String getValue() {
return this.value;
}
}
/**
* Use this inner interface for managing the disk usage data.
* This offloads format-specific implementation to the implementing class.
* The usage is very similar to a Java2 Iterator - next must be called to
* set the value and isFree/isUsed are available for that location.
*/
public interface DiskUsage {
public boolean hasNext();
public void next();
public boolean isFree();
public boolean isUsed();
}
/**
* This inner class represents the column header information used
* in the directory display. Note that this needs to be synchronized
* with the appropriate FileEntry objects.
*/
public static final int FILE_DISPLAY_STANDARD = 1;
public static final int FILE_DISPLAY_NATIVE = 2;
public static final int FILE_DISPLAY_DETAIL = 3;
public class FileColumnHeader {
public static final int ALIGN_LEFT = 1;
public static final int ALIGN_CENTER = 2;
public static final int ALIGN_RIGHT = 3;
private String title;
private int maximumWidth;
private int alignment;
public FileColumnHeader(String title, int maximumWidth, int alignment) {
this.title = title;
this.maximumWidth = maximumWidth;
this.alignment = alignment;
}
public String getTitle() {
return title;
}
public int getMaximumWidth() {
return maximumWidth;
}
public int getAlignment() {
return alignment;
}
public boolean isLeftAlign() {
return alignment == ALIGN_LEFT;
}
public boolean isCenterAlign() {
return alignment == ALIGN_CENTER;
}
public boolean isRightAlign() {
return alignment == ALIGN_RIGHT;
}
}
/**
* Constructor for FormattedDisk.
* @param filename
* @param diskImage
*/
public FormattedDisk(String filename, byte[] diskImage) {
super(filename, diskImage);
}
/**
* Identify if this disk format is capable of having directories.
*/
public abstract boolean canHaveDirectories();
/**
* Return the name of the disk. Not the physical file name,
* but "DISK VOLUME #xxx" (DOS 3.3) or "/MY.DISK" (ProDOS).
*/
public abstract String getDiskName();
/**
* Retrieve a list of files.
*/
public abstract List getFiles();
/**
* Identify the operating system format of this disk.
*/
public abstract String getFormat();
/**
* Return the amount of free space in bytes.
*/
public abstract int getFreeSpace();
/**
* Return the amount of used space in bytes.
*/
public abstract int getUsedSpace();
/**
* Get suggested dimensions for display of bitmap.
* Typically, this will be only used for 5.25" floppies.
* This can return null if there is no suggestion.
*/
public abstract int[] getBitmapDimensions();
/**
* Get the length of the bitmap.
*/
public abstract int getBitmapLength();
/**
* Get the disk usage iterator.
*/
public abstract DiskUsage getDiskUsage();
/**
* Get the labels to use in the bitmap.
* Note that this should, at a minimum, return an array of
* String[1] unless the bitmap has not been implemented.
*/
public abstract String[] getBitmapLabels();
/**
* Get disk information. This is intended to be pretty generic -
* each disk format can build this as appropriate. Each subclass should
* override this method and add its own detail.
*/
public List getDiskInformation() {
List list = new ArrayList();
list.add(new DiskInformation("File Name", getFilename()));
list.add(new DiskInformation("Disk Name", getDiskName()));
list.add(new DiskInformation("Physical Size (bytes)", getPhysicalSize()));
list.add(new DiskInformation("Free Space (bytes)", getFreeSpace()));
list.add(new DiskInformation("Used Space (bytes)", getUsedSpace()));
list.add(new DiskInformation("Physical Size (KB)", getPhysicalSize() / 1024));
list.add(new DiskInformation("Free Space (KB)", getFreeSpace() / 1024));
list.add(new DiskInformation("Used Space (KB)", getUsedSpace() / 1024));
list.add(new DiskInformation("Archive Order",
is2ImgOrder() ? "2IMG" :
isDosOrder() ? "DOS 3.3" :
isProdosOrder() ? "ProDOS" : "Unknown"));
list.add(new DiskInformation("Disk Format", getFormat()));
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();
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Type", 8, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Size (bytes)", 6, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Locked?", 6, FileColumnHeader.ALIGN_CENTER));
return list;
}
/**
* Indicates if this disk format supports "deleted" files.
* Not to be confused with being able to delete a file, this indicates that
* deleted entries remain in the filesystem after the file has been deleted.
* There are some filesystems that "compress" the file out of the structure
* by completely removing the entry instead of marking it deleted (like
* Apple Pascal).
*/
public abstract boolean supportsDeletedFiles();
/**
* Indicates if this disk image can read data from a file.
* If not, the reason may be as simple as it has not beem implemented
* to something specific about the disk.
*/
public abstract boolean canReadFileData();
/**
* Indicates if this disk image can write data to a file.
* If not, the reason may be as simple as it has not beem implemented
* to something specific about the disk (such as read-only image).
*/
public abstract boolean canWriteFileData();
/**
* 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 abstract boolean canCreateFile();
/**
* Indicates if this disk image can delete a file.
* If not, the reason may be as simple as it has not beem implemented
* to something specific about the disk.
*/
public abstract boolean canDeleteFile();
/**
* Get the data associated with the specified FileEntry.
* This is just the raw data. Use the FileEntry itself to read
* data appropriately! For instance, DOS "B" (binary) files store
* length and address as part of the file itself, but it is not treated
* as file data.
* @see FileEntry#getFileData()
*/
public abstract byte[] getFileData(FileEntry fileEntry);
/**
* Locate a specific file by filename.
* Returns a null if specific filename is not located.
*/
public FileEntry getFile(String filename) {
List files = getFiles();
return getFile(files, filename.trim());
}
/**
* Recursive routine to locate a specific file by filename.
* Note that in the instance of a system with directories (ie, ProDOS),
* this really returns the first file with the given filename.
*/
protected FileEntry getFile(List files, String filename) {
FileEntry theFileEntry = null;
if (files != null) {
for (int i=0; i<files.size(); i++) {
FileEntry entry = (FileEntry) files.get(i);
if (entry.isDirectory()) {
theFileEntry = getFile(entry.getFiles(), filename);
break;
}
String otherFilename = entry.getFilename();
if (otherFilename != null) otherFilename = otherFilename.trim();
if (filename.equalsIgnoreCase(otherFilename)) {
theFileEntry = entry;
break;
}
}
}
return theFileEntry;
}
}

View File

@ -0,0 +1,402 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.sun.image.codec.jpeg.JPEGCodec;
/**
* Filter the given file as if it were a graphics image.
* <p>
* Address for Apple2 HGR/DHR address is calculated from an observation of a pattern:<br>
* line number bits: 87654321<br>
* 87 are multipled by 0x0028<br>
* 65 are multipled by 0x0100<br>
* 4 is multiplied by 0x0080<br>
* 321 are multipled by 0x0400
* <p>
* HGR bit values ignore the high bit, as that switches the "palette", and for B&W mode,
* the bit does nothing. The other 7 bits simply toggle the pixel on or off. Double hires
* does not follow this - it uses a real 4 bit value, but the high bit is still ignored for
* graphics (hence, the 560 instead of 640 resolution).
* <p>
* Date created: Nov 3, 2002 12:06:36 PM
* @author: Rob Greene
*/
public class GraphicsFileFilter implements FileFilter {
public static final int MODE_HGR_BLACK_AND_WHITE = 1;
public static final int MODE_HGR_COLOR = 2;
public static final int MODE_DHR_BLACK_AND_WHITE = 3;
public static final int MODE_DHR_COLOR = 4;
private String extension;
private int mode = MODE_HGR_COLOR;
private static final int CODEC_NONE = 0; // disabled!
private static final int CODEC_IMAGEIO = 1; // JDK 1.4
private static final int CODEC_JPEGCODEC = 2; // SUN JDK's only
private int imageCodec;
/**
* Constructor for GraphicsFileFilter.
*/
public GraphicsFileFilter() {
super();
determineImageCodec();
}
/**
* Start guessing which codec is avilable for images.
*/
protected void determineImageCodec() {
try {
Class.forName("javax.imageio.ImageIO");
imageCodec = CODEC_IMAGEIO;
extension = "PNG";
return;
} catch (ClassNotFoundException ignored) {
try {
Class.forName("com.sun.image.codec.jpeg.JPEGCodec");
imageCodec = CODEC_JPEGCODEC;
extension = "JPEG";
} catch (ClassNotFoundException ignored2) {
imageCodec = CODEC_NONE;
}
}
}
/**
* Indicate if a codec is available (assist with interface requirements).
*/
public boolean isCodecAvailable() {
return imageCodec != CODEC_NONE;
}
/**
* Indicate if the ImageIO codec is avilable.
*/
protected boolean isCodecImageIo() {
return imageCodec == CODEC_IMAGEIO;
}
/**
* Indicate if the SUN JPEG Codec is avilable.
*/
protected boolean isCodecJpegCodec() {
return imageCodec == CODEC_JPEGCODEC;
}
/**
* Filter the file data and produce an image.
* @see com.webcodepro.applecommander.storage.FileFilter#filter(FileEntry)
*/
public byte[] filter(FileEntry fileEntry) {
byte[] fileData = fileEntry.getFileData();
BufferedImage image = null;
if (isHiresColorMode()) {
image = new BufferedImage(280, 192, BufferedImage.TYPE_INT_RGB);
} else if (isDoubleHiresMode()) {
image = new BufferedImage(560, 192*2, BufferedImage.TYPE_INT_RGB);
} else {
return new byte[0];
}
for (int y=0; y<192; y++) {
int base = ( // odd notation - bit value shifted right * hex value
((y & 0x7) << 10) // 00000111 * 0x0400
| (y & 0x8) << 4 // 00001000 * 0x0080
| (y & 0x30) << 4 // 00110000 * 0x0100
| ((y & 0xc0) >> 6) * 0x028 // 11000000 * 0x0028
) & 0x1fff;
byte[] lineData = new byte[40];
System.arraycopy(fileData, base, lineData, 0, 40);
if (isHiresBlackAndWhiteMode()) {
processHiresBlackAndWhiteLine(lineData, image, y);
} else if (isHiresColorMode()) {
processHiresColorLine(lineData, image, y);
} else if (isDoubleHiresMode()) {
byte[] lineData2 = new byte[40];
System.arraycopy(fileData, base + 0x2000, lineData2, 0, 40);
if (isDoubleHiresBlackAndWhiteMode()) {
processDoubleHiresBlackAndWhiteLine(lineData, lineData2, image, y);
} else if (isDoubleHiresColorMode()) {
processDoubleHiresColorLine(lineData, lineData2, image, y);
}
} else {
// oops...
}
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
if (isCodecImageIo()) {
ImageIO.write(image, getExtension(), outputStream);
} else if (isCodecJpegCodec()) {
JPEGCodec.createJPEGEncoder(outputStream).encode(image);
}
return outputStream.toByteArray();
} catch (IOException ex) {
return null;
}
}
/**
* Given a specific line in the image, process it in hires black and white
* mode.
*/
protected void processHiresBlackAndWhiteLine(byte[] lineData, BufferedImage image, int y) {
for (int x=0; x<280; x++) {
int offset = x / 7; // byte across row
int bit = x % 7; // bit to test
byte byt = lineData[offset];
if (AppleUtil.isBitSet(byt, bit)) {
image.setRGB(x, y, 0xffffff);
} else {
image.setRGB(x, y, 0x0);
}
}
}
/**
* Given a specific line in the image, process it in hires color mode.
* HGR color is two bits to determine color - essentially resolution is
* 140 horizontally, but it indicates the color for two pixels.
* <p>
* The names of pixles is a bit confusion - pixel0 is really the left-most
* pixel (not the low-value bit).
* To alleviate my bad naming, here is a color table to assist:<br>
* <pre>
* Color Bits RGB
* ======= ==== ========
* Black1 000 0x000000
* Green 001 0x00ff00
* Violet 010 0xff00ff
* White1 011 0xffffff
* Black2 100 0x000000
* Orange 101 0xff8000
* Blue 110 0x0000ff
* White2 111 0xffffff
* </pre>
* Remember: bits are listed as "highbit", "pixel0", "pixel1"!
*/
protected void processHiresColorLine(byte[] lineData, BufferedImage image, int y) {
for (int x=0; x<140; x++) {
int x0 = x*2;
int x1 = x0+1;
int offset0 = x0 / 7; // byte across row
int bit0 = x0 % 7; // bit to test
boolean pixel0 = AppleUtil.isBitSet(lineData[offset0], bit0);
int offset1 = x1 / 7; // byte across row
int bit1 = x1 % 7; // bit to test
boolean pixel1 = AppleUtil.isBitSet(lineData[offset1], bit1);
int color;
if (pixel0 && pixel1) {
color = 0xffffff; // white
} else if (!pixel0 && !pixel1) {
color = 0; // black
} else {
boolean highbit = pixel0 ? AppleUtil.isBitSet(lineData[offset0], 7) :
AppleUtil.isBitSet(lineData[offset1], 7);
if (pixel0 && highbit) {
color = 0x0000ff; // blue
} else if (pixel0 && !highbit) {
color = 0xff00ff; // voilet
} else if (pixel1 && !highbit) {
color = 0x00ff00; // green
} else { // pixel1 && highbit
color = 0xff8000; // orange
}
}
if (pixel0) image.setRGB(x0, y, color);
if (pixel1) image.setRGB(x1, y, color);
}
}
/**
* Given a specific line in the image, process it in double hires black and white
* mode.
*/
protected void processDoubleHiresBlackAndWhiteLine(byte[] lineData1, byte[] lineData2,
BufferedImage image, int y) {
for (int x=0; x<560; x++) {
// alternate bytes - switching memory banks
byte[] lineData = (x % 14 < 7) ? lineData1 : lineData2;
int rowOffset = x / 14; // byte across row
int bit = x % 7; // bit to test
byte byt = lineData[rowOffset];
if (AppleUtil.isBitSet(byt, bit)) {
image.setRGB(x, y*2, 0xffffff);
image.setRGB(x, y*2+1, 0xffffff);
} else {
image.setRGB(x, y*2, 0x0);
image.setRGB(x, y*2+1, 0x0);
}
}
}
/**
* Given a specific line in the image, process it in double hires color
* mode. Treat image as 140x192 mode.
* <p>
* From the <a href='http://web.pdx.edu/~heiss/technotes/aiie/tn.aiie.03.html'>Apple2
* technical note:
* <pre>
* Repeated<br>
* Binary<br>
* Color aux1 main1 aux2 main2 Pattern<br>
* Black 00 00 00 00 0000<br>
* Magenta 08 11 22 44 0001<br>
* Brown 44 08 11 22 0010<br>
* Orange 4C 19 33 66 0011<br>
* Dark Green 22 44 08 11 0100<br>
* Grey1 2A 55 2A 55 0101<br>
* Green 66 4C 19 33 0110<br>
* Yellow 6E 5D 3B 77 0111<br>
* Dark Blue 11 22 44 08 1000<br>
* Violet 19 33 66 4C 1001<br>
* Grey2 55 2A 55 2A 1010<br>
* Pink 5D 3B 77 6E 1011<br>
* Medium Blue 33 66 4C 19 1100<br>
* Light Blue 3B 77 6E 5D 1101<br>
* Aqua 77 6E 5D 3B 1110<br>
* White 7F 7F 7F 7F 1111
* </pre>
*/
protected void processDoubleHiresColorLine(byte[] lineData1, byte[] lineData2,
BufferedImage image, int y) {
int[] bitValues = { 8,4,2,1 };
int[] colorValues = {
0x000000, 0xff0000, 0x800000, 0xff8000, // black, magenta, brown, orange
0x008000, 0x808080, 0x00ff00, 0xffff00, // dark green, grey1, green, yellow
0x000080, 0xff00ff, 0x808080, 0xff80c0, // dark blue, voilet, grey2, pink
0x0000a0, 0x0000ff, 0x00c080, 0xffffff // medium blue, light blue, aqua, white
};
for (int x=0; x<560; x+=4) {
int colorValue = 0;
for (int b = 0; b < 4; b++) {
int xb = x+b;
// alternate bytes - switching memory banks
byte[] lineData = (xb % 14 < 7) ? lineData1 : lineData2;
int rowOffset = xb / 14; // byte across row
int bit = xb % 7; // bit to test
byte byt = lineData[rowOffset];
if (AppleUtil.isBitSet(byt, bit)) {
colorValue+= bitValues[b];
}
}
for (int b = 0; b < 4; b++) {
image.setRGB(x+b, y*2, colorValues[colorValue]);
image.setRGB(x+b, y*2+1, colorValues[colorValue]);
}
}
}
/**
* Give file extensions.
*/
public String[] getFileExtensions() {
if (isCodecImageIo()) {
return new String[] { "PNG", "JPEG" };
} else if (isCodecJpegCodec()) {
return new String[] { "JPEG" };
} else {
return new String[0];
}
}
/**
* Give suggested file name.
*/
public String getSuggestedFileName(FileEntry fileEntry) {
String fileName = fileEntry.getFilename().trim();
if (!fileName.toLowerCase().endsWith("." + getExtension())) {
fileName = fileName + "." + getExtension();
}
return fileName;
}
/**
* Set the format name.
*/
public void setExtension(String extension) {
this.extension = extension;
}
/**
* Get the format name.
*/
public String getExtension() {
return extension;
}
/**
* Set the color mode.
*/
public void setMode(int mode) {
this.mode = mode;
}
/**
* Indicates if this is configured for hires black & white mode.
*/
public boolean isHiresBlackAndWhiteMode() {
return mode == MODE_HGR_BLACK_AND_WHITE;
}
/**
* Indicates if this is configured for hires color mode.
*/
public boolean isHiresColorMode() {
return mode == MODE_HGR_COLOR;
}
/**
* Indicates if this is configured for double hires black & white mode.
*/
public boolean isDoubleHiresBlackAndWhiteMode() {
return mode == MODE_DHR_BLACK_AND_WHITE;
}
/**
* Indicates if this is configured for double hires color mode.
*/
public boolean isDoubleHiresColorMode() {
return mode == MODE_DHR_COLOR;
}
/**
* Indicates if this is a hires mode.
*/
protected boolean isHiresMode() {
return isHiresBlackAndWhiteMode() || isHiresColorMode();
}
/**
* Indicates if this is a double hires mode.
*/
protected boolean isDoubleHiresMode() {
return isDoubleHiresBlackAndWhiteMode() || isDoubleHiresColorMode();
}
}

View File

@ -0,0 +1,132 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
/**
* Filter the given file as an Integer BASIC file.
* <p>
* On disk, it looks similar to Applesoft - first two bytes are length; rest of
* image is raw data for Integer Basic.
* <p>
* [byte] length of line<br>
* [word] line number<br>
* [byte]* line data<br>
* $01 end of line<br>
* Repeat until end of program (line length of 0).
* <p>
* Tokens are $00 - $7F, some are duplicated.<br>
* $01 = end of line.<br>
* $B0 - $B9 = signifies a number stored in a word.<br>
* <p>
* Date created: Nov 3, 2002 1:14:47 AM
* @author: Rob Greene
*/
public class IntegerBasicFileFilter implements FileFilter {
private static String[] tokens = {
null, null, null, ": ", "LOAD ", "SAVE ", null, "RUN ", // $00-$07
null, "DEL ", ", ", "NEW ", "CLR ", "AUTO ", null, "MAN ", // $08-$0F
"HIMEM: ", "LOMEM: ", "+", "-", "*", "/", "=", "#", // $10-$17
">=", ">", "<=", "<>", "<", " AND ", " OR ", " MOD ",// $18-$1F
"^", null, "(", ",", " THEN ", " THEN ", ",", ",", // $20-$27
"\"", "\"", "(", null, null, "(", " PEEK ", "RND ", // $28-$2F
"SGN ", "ABS ", "PDL ", null, "(", "+", "-", "NOT ", // $30-$37
"(", "=", "#", " LEN(", " ASC(", " SCRN(", ",", " (", // $38-$3F
"$", null, "(", ",", ",", ";", ";", ";", // $40-$47
",", ",", ",", "TEXT ", "GR ", "CALL ", "DIM ", "DIM ", // $48-$4F
"TAB ", "END ", "INPUT ", "INPUT ", "INPUT ", "FOR ", "=", " TO ", // $50-$57
" STEP ", "NEXT ", ",", "RETURN ", "GOSUB ", "REM ", "LET ", "GOTO ",// $58-$5F
"IF ", "PRINT ", "PRINT ", "PRINT ", " POKE ", ",", "COLOR= ", "PLOT ",// $60-$67
",", "HLIN ", ",", " AT ", "VLIN ", ",", " AT ", "VTAB ",// $68-$6F
"=", "=", ")", null, "LIST ", ",", null, "POP ", // $70-$77
null, "NO DSP ", "NO TRACE ", "DSP ", "DSP ", "TRACE ", "PR # ", "IN # " // $78-$7F
};
/**
* Constructor for IntegerBasicFileFilter.
*/
public IntegerBasicFileFilter() {
super();
}
/**
* Process the given FileEntry and return a text image of the Integer BASIC file.
* @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 * 2);
PrintWriter printWriter = new PrintWriter(byteArray, true);
while (offset < fileData.length) {
int lineLength = AppleUtil.getUnsignedByte(fileData[offset]);
int lineNumber = AppleUtil.getWordValue(fileData, offset+1);
boolean inComment = false;
printWriter.print(lineNumber);
printWriter.print(' ');
for (int i=offset+3; i<(offset+lineLength); i++) {
byte byt = fileData[i];
if ((byt & 0x80) != 0) {
int value = AppleUtil.getUnsignedByte(byt);
// numbers follow a number digit
if (!inComment && value >= 0xb1 && value <= 0xb9) {
int integer = AppleUtil.getWordValue(fileData, i+1);
printWriter.print(integer);
i+= 2;
} else {
char ch = (char)(byt&0x7f);
if (ch < 0x20) { // handle control characters
printWriter.print("<CTRL-");
printWriter.print((char)('@' + ch));
printWriter.print(">");
} else {
printWriter.print(ch);
}
}
} else {
String token = tokens[(int)byt];
if (token != null) {
printWriter.print(token);
inComment = (byt == 0x5d); // REM statement
} else {
// ignoring unknown tokens
// $00 and $01 seem to be valid; the others may or may not be valid
}
}
}
offset+= lineLength;
printWriter.println();
}
return byteArray.toByteArray();
}
/**
* Give suggested file name.
*/
public String getSuggestedFileName(FileEntry fileEntry) {
String fileName = fileEntry.getFilename().trim();
if (!fileName.toLowerCase().endsWith(".int")) {
fileName = fileName + ".int";
}
return fileName;
}
}

View File

@ -0,0 +1,209 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Represents a Pascal file entry on disk.
* <p>
* Date created: Oct 5, 2002 12:22:34 AM
* @author: Rob Greene
*/
public class PascalFileEntry implements FileEntry {
private byte[] fileEntry;
private PascalFormatDisk disk;
/**
* Constructor for PascalFileEntry.
*/
public PascalFileEntry(byte[] fileEntry, PascalFormatDisk disk) {
super();
this.fileEntry = fileEntry;
this.disk = disk;
}
/**
* Get the block number of the files 1st block.
*/
public int getFirstBlock() {
return AppleUtil.getWordValue(fileEntry, 0);
}
/**
* Get the block number of the files last block +1.
*/
public int getLastBlock() {
return AppleUtil.getWordValue(fileEntry, 2);
}
/**
* Return the name of this file.
*/
public String getFilename() {
return AppleUtil.getPascalString(fileEntry, 6);
}
/**
* Return the filetype of this file.
*/
public String getFiletype() {
String filetypes[] = {
"xdskfile (for bad blocks)",
"codefile",
"textfile",
"infofile",
"datafile",
"graffile",
"fotofile",
"securedir" };
int filetype = fileEntry[4] & 0x0f;
if (filetype == 0 || filetype > filetypes.length) {
return "unknown (" + filetype + ")";
} else {
return filetypes[filetype-1];
}
}
/**
* Identify if this file is locked - not applicable in Pascal?
*/
public boolean isLocked() {
return false;
}
/**
* Get the number of bytes used in files last block.
*/
public int getBytesUsedInLastBlock() {
return AppleUtil.getWordValue(fileEntry, 22);
}
/**
* Compute the size of this file (in bytes).
*/
public int getSize() {
int blocks = getBlocksUsed() - 1;
return blocks*Disk.BLOCK_SIZE + getBytesUsedInLastBlock();
}
/**
* Compute the blocks used.
*/
public int getBlocksUsed() {
return AppleUtil.getWordValue(fileEntry, 2) - AppleUtil.getWordValue(fileEntry, 0);
}
/**
* Pascal does not support directories.
*/
public boolean isDirectory() {
return false;
}
/**
* Retrieve the list of files in this directory.
* Always returns null, as Pascal does not support directories.
*/
public List getFiles() {
return null;
}
/**
* Pascal file entries are removed upon deletion.
* Thus, a file entry cannot be marked as deleted.
*/
public boolean isDeleted() {
return false;
}
/**
* Get the file modification date.
*/
public Date getModificationDate() {
return AppleUtil.getPascalDate(fileEntry, 24);
}
/**
* 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();
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy");
List list = new ArrayList();
switch (displayMode) {
case FormattedDisk.FILE_DISPLAY_NATIVE:
list.add(dateFormat.format(getModificationDate()));
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getBlocksUsed()));
list.add(getFiletype());
list.add(getFilename());
break;
case FormattedDisk.FILE_DISPLAY_DETAIL:
list.add(dateFormat.format(getModificationDate()));
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getBlocksUsed()));
numberFormat.setMinimumIntegerDigits(1);
list.add(numberFormat.format(getBytesUsedInLastBlock()));
list.add(numberFormat.format(getSize()));
list.add(getFiletype());
list.add(getFilename());
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getFirstBlock()));
list.add(numberFormat.format(getLastBlock()-1));
break;
default: // FILE_DISPLAY_STANDARD
list.add(getFilename());
list.add(getFiletype());
list.add(numberFormat.format(getSize()));
list.add(isLocked() ? "Locked" : "");
break;
}
return list;
}
/**
* Get file data. This handles any operating-system specific issues.
* Currently, the disk itself handles this.
*/
public byte[] getFileData() {
return disk.getFileData(this);
}
/**
* 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.
*/
public FileFilter getSuggestedFilter() {
if ("textfile".equals(getFiletype())) {
return new TextFileFilter();
}
return new BinaryFileFilter();
}
}

View File

@ -0,0 +1,356 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/**
* Manages a disk that is in the Pascal format.
* <p>
* Date created: Oct 4, 2002 11:56:50 PM
* @author: Rob Greene
*/
public class PascalFormatDisk extends FormattedDisk {
/**
* The size of the Pascal file entry.
*/
public static final int ENTRY_SIZE = 26;
/**
* Use this inner interface for managing the disk usage data.
* This offloads format-specific implementation to the implementing class.
* A BitSet is used to track all blocks, as Pascal disks do not have a
* bitmap stored on the disk. This is safe since we know the number of blocks
* that exist. (BitSet length is of last set bit - unset bits at the end are
* "lost".)
*/
private class PascalDiskUsage implements DiskUsage {
private int location = -1;
private BitSet bitmap = null;
public boolean hasNext() {
return location == -1 || location < getBlocksOnDisk() - 1;
}
public void next() {
if (bitmap == null) {
bitmap = new BitSet(getBlocksOnDisk());
// assume all blocks are unused
for (int block=6; block<getBlocksOnDisk(); block++) {
bitmap.set(block);
}
// process through all files and mark those blocks as used
Iterator files = getFiles().iterator();
while (files.hasNext()) {
PascalFileEntry entry = (PascalFileEntry) files.next();
for (int block=entry.getFirstBlock(); block<entry.getLastBlock(); block++) {
bitmap.clear(block);
}
}
location = 0;
} else {
location++;
}
}
public boolean isFree() {
return bitmap.get(location); // true = free
}
public boolean isUsed() {
return !bitmap.get(location); // false = used
}
}
/**
* Constructor for PascalFormatDisk.
* @param filename
* @param diskImage
* @param order
*/
public PascalFormatDisk(String filename, byte[] diskImage) {
super(filename, diskImage);
}
/**
* Identify the operating system format of this disk.
* @see com.webcodepro.applecommander.storage.Disk#getFormat()
*/
public String getFormat() {
return "Pascal";
}
/**
* Retrieve a list of files.
* @see com.webcodepro.applecommander.storage.Disk#getFiles()
*/
public List getFiles() {
List list = new ArrayList();
// read directory blocks (block 2-5):
byte[] directory = new byte[4 * BLOCK_SIZE];
for (int i=0; i<4; i++) {
System.arraycopy(readBlock(2+i), 0, directory, i*BLOCK_SIZE, BLOCK_SIZE);
}
// process directory blocks:
int entrySize = ENTRY_SIZE;
int offset = entrySize;
while (offset < directory.length) {
byte[] entry = new byte[entrySize];
System.arraycopy(directory, offset, entry, 0, entry.length);
if (entry[6] == 0) break; // at end
list.add(new PascalFileEntry(entry, this));
offset+= entrySize;
}
return list;
}
/**
* Identify if this disk format is capable of having directories.
* @see com.webcodepro.applecommander.storage.Disk#canHaveDirectories()
*/
public boolean canHaveDirectories() {
return false;
}
/**
* Return the amount of free space in bytes.
* @see com.webcodepro.applecommander.storage.Disk#getFreeSpace()
*/
public int getFreeSpace() {
return getFreeBlocks() * BLOCK_SIZE;
}
/**
* Return the number of free blocks.
*/
public int getFreeBlocks() {
List files = getFiles();
int blocksFree = getBlocksOnDisk() - 6;
if (files != null) {
for (int i=0; i<files.size(); i++) {
PascalFileEntry entry = (PascalFileEntry) files.get(i);
blocksFree-= entry.getBlocksUsed();
}
}
return blocksFree;
}
/**
* Return the volume entry.
*/
protected byte[] getVolumeEntry() {
byte[] block = readBlock(2);
byte[] entry = new byte[ENTRY_SIZE];
System.arraycopy(block, 0, entry, 0, entry.length);
return entry;
}
/**
* Return the number of blocks on disk.
*/
public int getBlocksOnDisk() {
return AppleUtil.getWordValue(getVolumeEntry(), 14);
}
/**
* Return the number of files on disk.
*/
public int getFilesOnDisk() {
return AppleUtil.getWordValue(getVolumeEntry(), 16);
}
/**
* Return the last access date.
*/
public Date getLastAccessDate() {
return AppleUtil.getPascalDate(getVolumeEntry(), 18);
}
/**
* Return the most recent date setting. Huh?
*/
public Date getMostRecentDateSetting() {
return AppleUtil.getPascalDate(getVolumeEntry(), 20);
}
/**
* Return the amount of used space in bytes.
* @see com.webcodepro.applecommander.storage.Disk#getUsedSpace()
*/
public int getUsedSpace() {
return getUsedBlocks() * BLOCK_SIZE;
}
/**
* Return the number of used blocks.
*/
public int getUsedBlocks() {
List files = getFiles();
int blocksUsed = 6;
if (files != null) {
for (int i=0; i<files.size(); i++) {
PascalFileEntry entry = (PascalFileEntry) files.get(i);
blocksUsed+= entry.getBlocksUsed();
}
}
return blocksUsed;
}
/**
* Return the name of the disk. This is stored on block #2
* offset +6 (string[7]).
* @see com.webcodepro.applecommander.storage.Disk#getDiskName()
*/
public String getDiskName() {
return AppleUtil.getPascalString(readBlock(2), 6);
}
/**
* Get suggested dimensions for display of bitmap. Since Pascal disks are
* a block device, no suggestion is given.
*/
public int[] getBitmapDimensions() {
return null;
}
/**
* Get the length of the bitmap.
*/
public int getBitmapLength() {
return getBlocksOnDisk();
}
/**
* Get the disk usage iterator.
*/
public DiskUsage getDiskUsage() {
return new PascalDiskUsage();
}
/**
* Get the labels to use in the bitmap.
*/
public String[] getBitmapLabels() {
return new String[] { "Block" };
}
/**
* Get Pascal-specific disk information.
*/
public List getDiskInformation() {
List list = super.getDiskInformation();
list.add(new DiskInformation("Total Blocks", getBlocksOnDisk()));
list.add(new DiskInformation("Free Blocks", getFreeBlocks()));
list.add(new DiskInformation("Used Blocks", getUsedBlocks()));
list.add(new DiskInformation("Files On Disk", getFilesOnDisk()));
list.add(new DiskInformation("Last Access Date", getLastAccessDate()));
list.add(new DiskInformation("Most Recent Date Setting", getMostRecentDateSetting()));
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("Modified", 8, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
break;
case FILE_DISPLAY_DETAIL:
list.add(new FileColumnHeader("Modified", 8, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Bytes in last block", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Size (bytes)", 6, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("First Block", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Last Block", 3, FileColumnHeader.ALIGN_RIGHT));
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 false;
}
/**
* 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; // FIXME - not implemented
}
/**
* Indicates if this disk image can create a file.
*/
public boolean canCreateFile() {
return false; // FIXME - not implemented
}
/**
* Indicates if this disk image can delete a file.
*/
public boolean canDeleteFile() {
return false; // FIXME - not implemented
}
/**
* Get the data associated with the specified FileEntry.
*/
public byte[] getFileData(FileEntry fileEntry) {
if ( !(fileEntry instanceof PascalFileEntry)) {
throw new IllegalArgumentException("Most have a Pascal file entry!");
}
PascalFileEntry pascalEntry = (PascalFileEntry) fileEntry;
int firstBlock = pascalEntry.getFirstBlock();
int lastBlock = pascalEntry.getLastBlock();
byte[] fileData = new byte[pascalEntry.getSize()];
int offset = 0;
for (int block = firstBlock; block < lastBlock; block++) {
byte[] blockData = readBlock(block);
if (block == lastBlock-1) {
System.arraycopy(blockData, 0, fileData, offset, pascalEntry.getBytesUsedInLastBlock());
} else {
System.arraycopy(blockData, 0, fileData, offset, blockData.length);
}
offset+= blockData.length;
}
return fileData;
}
}

View File

@ -0,0 +1,72 @@
/*
* 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;
/**
* Provide common directory attributes.
* <p>
* Date created: Oct 5, 2002 11:17:00 PM
* @author: Rob Greene
*/
public class ProdosCommonDirectoryHeader extends ProdosCommonEntry {
/**
* Constructor for ProdosCommonDirectoryHeader.
* @param fileEntry
*/
public ProdosCommonDirectoryHeader(byte[] fileEntry) {
super(fileEntry);
}
/**
* Get the length of each entry. Expected to be 0x27.
*/
public int getEntryLength() {
return AppleUtil.getUnsignedByte(getFileEntry()[0x1f]);
}
/**
* Get the number of entries per block. Expected to be 0x0d.
*/
public int getEntriesPerBlock() {
return AppleUtil.getUnsignedByte(getFileEntry()[0x20]);
}
/**
* Get the number of active entries in the volume directory.
*/
public int getFileCount() {
return AppleUtil.getWordValue(getFileEntry(), 0x21);
}
/**
* Get the block number of the bit map.
*/
public int getBitMapPointer() {
return AppleUtil.getWordValue(getFileEntry(), 0x23);
}
/**
* Get the total number of blocks on this volume.
*/
public int getTotalBlocks() {
return AppleUtil.getWordValue(getFileEntry(), 0x25);
}
}

View File

@ -0,0 +1,153 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.Date;
/**
* Represents the common Prodos entry behavior.
* <p>
* Date created: Oct 5, 2002 10:55:41 PM
* @author: Rob Greene
*/
public class ProdosCommonEntry {
private byte[] fileEntry;
/**
* Constructor for ProdosCommonEntry.
*/
public ProdosCommonEntry(byte[] fileEntry) {
super();
this.fileEntry = fileEntry;
}
/**
* Get the fileEntry bytes.
*/
protected byte[] getFileEntry() {
return fileEntry;
}
/**
* Get storage type.
*/
protected int getStorageType() {
return AppleUtil.getUnsignedByte(getFileEntry()[0]) >> 4;
}
/**
* Indicates if this is a "seedling" file (only one data block).
*/
public boolean isSeedlingFile() {
return getStorageType() == 0x01;
}
/**
* Indicates if this is a "sapling" file (2 to 256 data blocks).
*/
public boolean isSaplingFile() {
return getStorageType() == 0x02;
}
/**
* Indicates if this is a "tree" file (257 to 32768 data blocks).
*/
public boolean isTreeFile() {
return getStorageType() == 0x03;
}
/**
* Indicates if this is a subdirectory header entry.
*/
public boolean isSubdirectoryHeader() {
return getStorageType() == 0x0e;
}
/**
* Indicates if this is a volume header entry.
*/
public boolean isVolumeHeader() {
return getStorageType() == 0x0f;
}
/**
* Get the creation date.
*/
public Date getCreationDate() {
return AppleUtil.getProdosDate(getFileEntry(), 0x18);
}
/**
* Get the version of ProDOS that created this file.
*/
public int getProdosVersion() {
return AppleUtil.getUnsignedByte(getFileEntry()[0x1c]);
}
/**
* Get the minimum version of ProDOS which can access this file.
*/
public int getMinimumProdosVersion() {
return AppleUtil.getUnsignedByte(getFileEntry()[0x1d]);
}
/**
* Get the access byte.
*/
protected byte getAccess() {
return getFileEntry()[0x1e];
}
/**
* Indicates if this file may be destroyed.
*/
public boolean canDestroy() {
return AppleUtil.isBitSet(getAccess(), 7);
}
/**
* Indicates if this file may be renamed.
*/
public boolean canRename() {
return AppleUtil.isBitSet(getAccess(), 6);
}
/**
* Indicates if this file has changed since last backup.
*/
public boolean hasChanged() {
return AppleUtil.isBitSet(getAccess(), 5);
}
/**
* Indicates if this file may be written.
*/
public boolean canWrite() {
return AppleUtil.isBitSet(getAccess(), 1);
}
/**
* Indicates if this file may be read.
*/
public boolean canRead() {
return AppleUtil.isBitSet(getAccess(), 0);
}
}

View File

@ -0,0 +1,457 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Represents a ProDOS file entry on disk.
* <p>
* Date created: Oct 5, 2002 6:01:15 PM
* @author: Rob Greene
*/
public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
private List files;
private ProdosSubdirectoryHeader subdirectoryHeader;
private ProdosFormatDisk disk;
/**
* Constructor for ProdosFileEntry.
*/
public ProdosFileEntry(byte[] fileEntry, ProdosFormatDisk disk) {
super(fileEntry);
this.disk = disk;
}
/**
* Return the name of this file.
* This handles normal files, deleted files, and AppleWorks files - which use
* the AUXTYPE attribute to indicate upper/lower-case in the filename.
*/
public String getFilename() {
String fileName;
if (isDeleted()) {
fileName = AppleUtil.getString(getFileEntry(), 1, 15);
StringBuffer buf = new StringBuffer();
for (int i=0; i<fileName.length(); i++) {
char ch = fileName.charAt(i);
if (ch != 0) {
buf.append(ch);
} else {
break;
}
}
fileName = buf.toString();
} else {
fileName = AppleUtil.getProdosString(getFileEntry(), 0);
}
if (isAppleWorksFile()) {
int auxtype = getAuxiliaryType();
StringBuffer mixedCase = new StringBuffer(fileName);
// the highest bit of the least significant byte is the first
// character through the 2nd bit of the most significant byte
// being the 15th character. Bit is on indicates lowercase or
// a space if a "." is present.
for (int i=0; i<16 && i<fileName.length(); i++) {
boolean lowerCase;
if (i < 8) {
lowerCase = AppleUtil.isBitSet((byte)auxtype, 7-i);
} else {
lowerCase = AppleUtil.isBitSet((byte)(auxtype >> 8), 7-(i%8));
}
if (lowerCase) {
char ch = mixedCase.charAt(i);
if (ch == '.') {
mixedCase.setCharAt(i, ' ');
} else {
mixedCase.setCharAt(i, Character.toLowerCase(ch));
}
}
}
fileName = mixedCase.toString();
}
return fileName;
}
/**
* Return the filetype of this file. This will be three characters,
* according to ProDOS - a "$xx" if unknown.
* <p>
* This could be improved should specific information regarding file types
* be needed; the file type could become a separate object which works with
* the file in some manner.
* <p>
* Note: Source of information is the following url -
* http://www.apple2.org.za/gswv/gsezine/GS.WorldView/ProDOS.File.Types.v2.0.txt
*/
public String getFiletype() {
int filetype = AppleUtil.getUnsignedByte(getFileEntry()[0x10]);
switch (filetype) {
case 0x00: return "UNK";
case 0x01: return "BAD";
case 0x02: return "PCD";
case 0x03: return "PTX";
case 0x04: return "TXT";
case 0x05: return "PDA";
case 0x06: return "BIN";
case 0x07: return "FNT";
case 0x08: return "FOT";
case 0x09: return "BA3";
case 0x0a: return "DA3";
case 0x0b: return "WPF";
case 0x0c: return "SOS";
case 0x0f: return "DIR";
case 0x10: return "RPD";
case 0x11: return "RPI";
case 0x12: return "AFD";
case 0x13: return "AFM";
case 0x14: return "AFR";
case 0x15: return "SCL";
case 0x16: return "PFS";
case 0x19: return "ADB"; // AppleWorks: AUX TYPE indicates UPPER/lower case
case 0x1a: return "AWP"; // AppleWorks: AUX TYPE indicates UPPER/lower case
case 0x1b: return "ASP"; // AppleWorks: AUX TYPE indicates UPPER/lower case
case 0x20: return "TDM";
case 0x21: return "IPS";
case 0x22: return "UPV";
case 0x29: return "3SD";
case 0x2a: return "8SC";
case 0x2b: return "8OB";
case 0x2c: return "8IC";
case 0x2d: return "8LD";
case 0x2e: return "P8C"; // P8C or PTP, depending on AUX TYPE
case 0x41: return "OCR";
case 0x42: return "FTD";
case 0x50: return "GWP";
case 0x51: return "GSS";
case 0x52: return "GDB";
case 0x53: return "DRW";
case 0x54: return "GDP";
case 0x55: return "HMD";
case 0x56: return "EDU";
case 0x57: return "STN";
case 0x58: return "HLP";
case 0x59: return "COM";
case 0x5a: return "CFG"; // CFG or PTP, depending on AUX TYPE
case 0x5b: return "ANM";
case 0x5c: return "MUM";
case 0x5d: return "ENT";
case 0x5e: return "DVU";
case 0x60: return "PRE";
case 0x6b: return "BIO";
case 0x6d: return "DVR"; // DVR/TDR
case 0x6e: return "PRE";
case 0x6f: return "HDV"; // PC Volume
case 0xa0: return "WP_";
case 0xab: return "GSB";
case 0xac: return "TDF";
case 0xad: return "BDF";
case 0xb0: return "SRC";
case 0xb1: return "OBJ";
case 0xb2: return "LIB";
case 0xb3: return "S16";
case 0xb4: return "RTL";
case 0xb5: return "EXE";
case 0xb6: return "STR"; // STR/PIF
case 0xb7: return "TSF"; // TSF/TIF
case 0xb8: return "NDA";
case 0xb9: return "CDA";
case 0xba: return "TOL";
case 0xbb: return "DRV"; // DRV/DVR
case 0xbc: return "LDF";
case 0xbd: return "FST";
case 0xbf: return "DOC";
case 0xc0: return "PNT";
case 0xc1: return "PIC";
case 0xc2: return "ANI";
case 0xc3: return "PAL";
case 0xc5: return "OOG";
case 0xc6: return "SCR";
case 0xc7: return "CDV";
case 0xc8: return "FON";
case 0xc9: return "FND";
case 0xca: return "ICN";
case 0xd5: return "MUS";
case 0xd6: return "INS";
case 0xd7: return "MDI";
case 0xd8: return "SND";
case 0xdb: return "DBM";
case 0xe0: return "SHK";
case 0xe2: return "DTS"; // DTS/ATK
case 0xee: return "R16";
case 0xef: return "PAS";
case 0xf0: return "CMD";
// Left $F1 - $F8 alone as these are user-defined types
case 0xf9: return "P16";
case 0xfa: return "INT";
case 0xfb: return "IVR";
case 0xfc: return "BAS";
case 0xfd: return "VAR";
case 0xfe: return "REL";
case 0xff: return "SYS";
default :
return "$" + AppleUtil.getFormattedByte(filetype);
}
}
/**
* Indicate if this is an AppleWorks file.
* Intended to force upper/lowercase into the filename.
*/
public boolean isAppleWorksFile() {
int filetype = AppleUtil.getUnsignedByte(getFileEntry()[0x10]);
return (filetype == 0x19 || filetype == 0x1a || filetype == 0x1b);
}
/**
* Get the key pointer. This is either the data block (seedling),
* index block (sapling), or master index block (tree).
*/
public int getKeyPointer() {
return AppleUtil.getWordValue(getFileEntry(), 0x11);
}
/**
* Get the number of blocks used.
*/
public int getBlocksUsed() {
return AppleUtil.getWordValue(getFileEntry(), 0x13);
}
/**
* Get the EOF position. This can indicate the length of a file.
*/
public int getEofPosition() {
return AppleUtil.get3ByteValue(getFileEntry(), 0x15);
}
/**
* Get the auxiliary type for this file.
* TXT - random access record length.
* BIN - load address for binary image.
* BAS - load address for program image.
* VAR - address of compressed variables image.
* SYS - load address for system program (usually 0x2000).
*/
public int getAuxiliaryType() {
return AppleUtil.getWordValue(getFileEntry(), 0x1f);
}
/**
* Get the last modification date.
*/
public Date getLastModificationDate() {
return AppleUtil.getProdosDate(getFileEntry(), 0x21);
}
/**
* Get the block number of the key block for the directory which describes this file.
*/
public int getHeaderPointer() {
return AppleUtil.getWordValue(getFileEntry(), 0x25);
}
/**
* Identify if this file is locked.
*/
public boolean isLocked() {
return !canDestroy() && !canRename() && !canWrite();
}
/**
* Compute the size of this file (in bytes).
*/
public int getSize() {
return getEofPosition();
}
/**
* Identify if this is a directory file.
*/
public boolean isDirectory() {
return getStorageType() == 0x0d;
}
/**
* Retrieve the list of files in this directory.
* Note that if this is not a directory, the return
* value should be null. If this a directory, the
* return value should always be a list - a directory
* with 0 entries returns an empty list.
*/
public List getFiles() {
return files;
}
/**
* Set the list of files.
*/
public void setFiles(List files) {
this.files = files;
}
/**
* Identify if this file has been deleted.
*/
public boolean isDeleted() {
return getStorageType() == 0;
}
/**
* Set the subdirectory header.
*/
public void setSubdirectoryHeader(ProdosSubdirectoryHeader subdirectoryHeader) {
this.subdirectoryHeader = subdirectoryHeader;
}
/**
* Get the subdirectory header.
*/
public ProdosSubdirectoryHeader getSubdirectoryHeader() {
return this.subdirectoryHeader;
}
/**
* 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();
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
List list = new ArrayList();
switch (displayMode) {
case FormattedDisk.FILE_DISPLAY_NATIVE:
list.add(isLocked() ? "*" : " ");
list.add(getFilename());
list.add(getFiletype());
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getBlocksUsed()));
list.add(getLastModificationDate() == null ? "<NO DATE> " :
dateFormat.format(getLastModificationDate()));
list.add(getCreationDate() == null ? "<NO DATE> " :
dateFormat.format(getCreationDate()));
numberFormat.setMinimumIntegerDigits(1);
list.add(numberFormat.format(getEofPosition()));
if ("TXT".equals(getFiletype()) && getAuxiliaryType() > 0) {
numberFormat.setMinimumIntegerDigits(1);
list.add("L=" + numberFormat.format(getAuxiliaryType()).trim());
} else if (("BIN".equals(getFiletype()) || "BAS".equals(getFiletype())
|| "VAR".equals(getFiletype()) || "SYS".equals(getFiletype()))
&& getAuxiliaryType() > 0) {
list.add("A=$" + AppleUtil.getFormattedWord(getAuxiliaryType()));
} else {
list.add("");
}
break;
case FormattedDisk.FILE_DISPLAY_DETAIL:
list.add(isLocked() ? "*" : " ");
list.add(getFilename());
list.add(isDeleted() ? "Deleted" : "");
String permissions = "";
if (canDestroy()) permissions+= "Destroy ";
if (canRead()) permissions+= "Read ";
if (canRename()) permissions+= "Rename ";
if (canWrite()) permissions+= "Write ";
list.add(permissions);
list.add(getFiletype());
list.add(isDirectory() ? "Directory" : "");
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getBlocksUsed()));
list.add(getLastModificationDate() == null ? "<NO DATE> " :
dateFormat.format(getLastModificationDate()));
list.add(getCreationDate() == null ? "<NO DATE> " :
dateFormat.format(getCreationDate()));
numberFormat.setMinimumIntegerDigits(1);
list.add(numberFormat.format(getEofPosition()));
if ("TXT".equals(getFiletype()) && getAuxiliaryType() > 0) {
numberFormat.setMinimumIntegerDigits(1);
list.add("L=" + numberFormat.format(getAuxiliaryType()).trim());
} else if (("BIN".equals(getFiletype()) || "BAS".equals(getFiletype())
|| "VAR".equals(getFiletype()) || "SYS".equals(getFiletype()))
&& getAuxiliaryType() > 0) {
list.add("A=$" + AppleUtil.getFormattedWord(getAuxiliaryType()));
} else {
list.add("");
}
list.add(AppleUtil.getFormattedWord(getHeaderPointer()));
list.add(AppleUtil.getFormattedWord(getKeyPointer()));
list.add(isSaplingFile() ? "Sapling" : isSeedlingFile() ? "Seedling" :
isTreeFile() ? "Tree" : "Unknown");
list.add(hasChanged() ? "Changed" : "");
numberFormat.setMinimumIntegerDigits(1);
list.add(numberFormat.format(getMinimumProdosVersion()));
list.add(numberFormat.format(getProdosVersion()));
break;
default: // FILE_DISPLAY_STANDARD
list.add(getFilename());
list.add(getFiletype());
list.add(numberFormat.format(getSize()));
list.add(isLocked() ? "Locked" : "");
break;
}
return list;
}
/**
* Get file data. This handles any operating-system specific issues.
* Currently, the disk itself handles this.
*/
public byte[] getFileData() {
return disk.getFileData(this);
}
/**
* 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.
*/
public FileFilter getSuggestedFilter() {
if ("TXT".equals(getFiletype()) || "SRC".equals(getFiletype())) {
return new TextFileFilter();
} else if ("AWP".equals(getFiletype())) {
return new AppleWorksWordProcessorFileFilter();
} else if ("BAS".equals(getFiletype())) {
return new ApplesoftFileFilter();
} else if ("INT".equals(getFiletype())) { // supposedly not available in ProDOS, however
return new IntegerBasicFileFilter();
} else if ("BIN".equals(getFiletype())) {
int size = getSize();
// the minimum size is guessed a bit - I don't remember, but maybe there
// are 8 spare bytes at the end of the graphics screen
GraphicsFileFilter filter = new GraphicsFileFilter();
if (size >= 8185 && size <= 8192) {
filter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
return filter;
} else if (size >= 16377 && size <= 16384) {
filter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
return filter;
}
// fall through to BinaryFileFilter...
}
return new BinaryFileFilter();
}
}

View File

@ -0,0 +1,392 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.ArrayList;
import java.util.List;
/**
* Manages a disk that is in the ProDOS format.
* <p>
* Date created: Oct 3, 2002 11:45:25 PM
* @author: Rob Greene
*/
public class ProdosFormatDisk extends FormattedDisk {
/**
* The standard ProDOS file entry length.
*/
public static final int ENTRY_LENGTH = 0x27;
/**
* Hold on to the volume directory header.
*/
private ProdosVolumeDirectoryHeader volumeHeader;
/**
* Use this inner interface for managing the disk usage data.
* This offloads format-specific implementation to the implementing class.
*/
private class ProdosDiskUsage implements DiskUsage {
private int location = -1;
private transient byte[] data = null;
public boolean hasNext() {
return location == -1 || location < volumeHeader.getTotalBlocks() - 1;
}
public void next() {
location++;
}
/**
* Get the free setting for the bitmap at the current location.
*/
public boolean isFree() {
if (location == -1) {
throw new IllegalArgumentException("Invalid dimension for isFree! Did you call next first?");
}
if (data == null) {
int volumeBitmapBlock = volumeHeader.getBitMapPointer();
int volumeBitmapBlocks = volumeHeader.getTotalBlocks();
int blocksToRead = (volumeBitmapBlocks / 4096) + 1;
// Read in the entire volume bitmap:
data = new byte[blocksToRead * BLOCK_SIZE];
for (int i=0; i<blocksToRead; i++) {
System.arraycopy(readBlock(volumeBitmapBlock+i), 0, data, i*BLOCK_SIZE, BLOCK_SIZE);
}
}
// Locate appropriate bit and check it:
int byt = location / 8;
int bit = 7 - (location % 8);
boolean free = AppleUtil.isBitSet(data[byt], bit);
return free;
}
public boolean isUsed() {
return !isFree();
}
}
/**
* Constructor for ProdosFormatDisk.
* @param filename
* @param diskImage
*/
public ProdosFormatDisk(String filename, byte[] diskImage) {
super(filename, diskImage);
// read volume header:
byte[] block = readBlock(2);
byte[] entry = new byte[ENTRY_LENGTH];
System.arraycopy(block, 4, entry, 0, ENTRY_LENGTH);
volumeHeader = new ProdosVolumeDirectoryHeader(entry);
}
/**
* Identify the operating system format of this disk.
* @see com.webcodepro.applecommander.storage.Disk#getFormat()
*/
public String getFormat() {
return "ProDOS";
}
/**
* Retrieve a list of files.
* @see com.webcodepro.applecommander.storage.Disk#getFiles()
*/
public List getFiles() {
return getFiles(2);
}
/**
* Build a list of files, starting in the given block number.
* This works for the master as well as the subdirectories.
*/
protected List getFiles(int blockNumber) {
List files = new ArrayList();
while (blockNumber != 0) {
byte[] block = readBlock(blockNumber);
int offset = 4;
while (offset+ENTRY_LENGTH < BLOCK_SIZE) {
byte[] entry = new byte[ENTRY_LENGTH];
System.arraycopy(block, offset, entry, 0, ENTRY_LENGTH);
int checksum = 0;
for (int i=0; i<entry.length; i++) {
checksum |= entry[i];
}
if (checksum != 0) {
ProdosCommonEntry tester = new ProdosCommonEntry(entry);
if (tester.isVolumeHeader() || tester.isSubdirectoryHeader()) {
// ignore it, we've already got it
} else {
ProdosFileEntry fileEntry = new ProdosFileEntry(entry, this);
files.add(fileEntry);
if (fileEntry.isDirectory()) {
int keyPointer = fileEntry.getKeyPointer();
byte[] subdirBlock = readBlock(keyPointer);
byte[] subdirEntry = new byte[ENTRY_LENGTH];
System.arraycopy(subdirBlock, 4, subdirEntry, 0, ENTRY_LENGTH);
fileEntry.setSubdirectoryHeader(new ProdosSubdirectoryHeader(subdirEntry));
fileEntry.setFiles(getFiles(keyPointer));
}
}
}
offset+= entry.length;
}
blockNumber = AppleUtil.getWordValue(block, 2);
}
return files;
}
/**
* Return the amount of free space in bytes.
* @see com.webcodepro.applecommander.storage.Disk#getFreeSpace()
*/
public int getFreeSpace() {
return getFreeBlocks() * BLOCK_SIZE;
}
/**
* Return the number of free blocks on the disk.
*/
public int getFreeBlocks() {
int freeBlocks = 0;
int blocksToProcess = (volumeHeader.getTotalBlocks() + 4095) / 4096;
int blockNumber = volumeHeader.getBitMapPointer();
for (int ix=0; ix<blocksToProcess; ix++) {
byte[] block = readBlock(blockNumber+ix);
for (int byt=0; byt<block.length; byt++) {
freeBlocks+= AppleUtil.getBitCount(block[byt]);
}
}
return freeBlocks;
}
/**
* Return the amount of used space in bytes.
* @see com.webcodepro.applecommander.storage.Disk#getUsedSpace()
*/
public int getUsedSpace() {
return getUsedBlocks() * BLOCK_SIZE;
}
/**
* Return the number of used blocks on the disk.
*/
public int getUsedBlocks() {
return volumeHeader.getTotalBlocks() - getFreeBlocks();
}
/**
* Identify if this disk format is capable of having directories.
* @see com.webcodepro.applecommander.storage.Disk#hasDirectories()
*/
public boolean canHaveDirectories() {
return true;
}
/**
* Return the name of the disk.
* @see com.webcodepro.applecommander.storage.Disk#getDiskName()
*/
public String getDiskName() {
return "/" + volumeHeader.getVolumeName() + "/";
}
/**
* Get suggested dimensions for display of bitmap. There is no suggestion
* for a ProDOS volume - it is just a series of blocks.
*/
public int[] getBitmapDimensions() {
return null;
}
/**
* Get the length of the bitmap.
*/
public int getBitmapLength() {
return volumeHeader.getTotalBlocks();
}
/**
* Get the disk usage iterator.
*/
public DiskUsage getDiskUsage() {
return new ProdosDiskUsage();
}
/**
* Get the labels to use in the bitmap.
*/
public String[] getBitmapLabels() {
return new String[] { "Block" };
}
/**
* Get Pascal-specific disk information.
*/
public List getDiskInformation() {
List list = super.getDiskInformation();
list.add(new DiskInformation("Total Blocks", volumeHeader.getTotalBlocks()));
list.add(new DiskInformation("Free Blocks", getFreeBlocks()));
list.add(new DiskInformation("Used Blocks", getUsedBlocks()));
list.add(new DiskInformation("Volume Access",
(volumeHeader.canDestroy() ? "Destroy " : "") +
(volumeHeader.canRead() ? "Read " : "") +
(volumeHeader.canRename() ? "Rename " : "") +
(volumeHeader.canWrite() ? "Write" : "")));
list.add(new DiskInformation("Block Number of Bitmap", volumeHeader.getBitMapPointer()));
list.add(new DiskInformation("Creation Date", volumeHeader.getCreationDate()));
list.add(new DiskInformation("File Entries Per Block", volumeHeader.getEntriesPerBlock()));
list.add(new DiskInformation("File Entry Length (bytes)", volumeHeader.getEntryLength()));
list.add(new DiskInformation("Active Files in Root Directory", volumeHeader.getFileCount()));
list.add(new DiskInformation("Minimum ProDOS Version Required",
volumeHeader.getMinimumProdosVersion()));
list.add(new DiskInformation("Volume Created By ProDOS Version", volumeHeader.getProdosVersion()));
list.add(new DiskInformation("Volume Name", volumeHeader.getVolumeName()));
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));
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Modified", 10, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Created", 10, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Length", 10, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Aux. Type", 8, FileColumnHeader.ALIGN_LEFT));
break;
case FILE_DISPLAY_DETAIL:
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Deleted?", 7, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Permissions", 8, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Directory?", 9, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Modified", 10, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Created", 10, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Length", 10, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Aux. Type", 8, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Dir. Header", 5, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Key Block", 5, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Key Type", 8, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Changed", 5, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Min. ProDOS Ver.", 2, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("ProDOS Ver.", 2, FileColumnHeader.ALIGN_CENTER));
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; // FIXME - not implemented
}
/**
* Indicates if this disk image can create a file.
*/
public boolean canCreateFile() {
return false; // FIXME - not implemented
}
/**
* Indicates if this disk image can delete a file.
*/
public boolean canDeleteFile() {
return false; // FIXME - not implemented
}
/**
* Get the data associated with the specified FileEntry.
* Note that this could return a 16MB file! Sparse files are not treated specially.
*/
public byte[] getFileData(FileEntry fileEntry) {
if ( !(fileEntry instanceof ProdosFileEntry)) {
throw new IllegalArgumentException("Most have a ProDOS file entry!");
}
ProdosFileEntry prodosEntry = (ProdosFileEntry) fileEntry;
byte[] fileData = new byte[prodosEntry.getEofPosition()];
int indexBlocks = 0;
if (prodosEntry.isSeedlingFile()) {
byte[] blockData = readBlock(prodosEntry.getKeyPointer());
System.arraycopy(blockData, 0, fileData, 0, prodosEntry.getEofPosition());
} else if (prodosEntry.isSaplingFile()) {
byte[] indexBlock = readBlock(prodosEntry.getKeyPointer());
getIndexBlockData(fileData, indexBlock, 0);
} else if (prodosEntry.isTreeFile()) {
byte[] masterIndexBlock = readBlock(prodosEntry.getKeyPointer());
int offset = 0;
for (int i=0; i<0x100; i++) {
int blockNumber = AppleUtil.getWordValue(masterIndexBlock[i], masterIndexBlock[i+0x100]);
byte[] indexBlock = readBlock(blockNumber);
offset+= getIndexBlockData(fileData, indexBlock, offset);
}
} else {
throw new IllegalArgumentException("Unknown ProDOS filetype!");
}
return fileData;
}
/**
* Read file data from the given index block.
* Note that block number 0 is an unused block.
* @see #getFileData()
*/
protected int getIndexBlockData(byte[] fileData, byte[] indexBlock, int offset) {
for (int i=0; i<0x100; i++) {
int blockNumber = AppleUtil.getWordValue(indexBlock[i], indexBlock[i+0x100]);
byte[] blockData = readBlock(blockNumber);
if (offset + blockData.length > fileData.length) { // end of file
int bytesToCopy = fileData.length - offset;
if (blockNumber != 0) System.arraycopy(blockData, 0, fileData, offset, bytesToCopy);
offset+= bytesToCopy;
} else {
if (blockNumber != 0) System.arraycopy(blockData, 0, fileData, offset, blockData.length);
offset+= blockData.length;
}
}
return offset;
}
}

View File

@ -0,0 +1,66 @@
/*
* 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;
/**
* Provides commone subdirectory attributes.
* <p>
* Date created: Oct 5, 2002 11:17:57 PM
* @author: Rob Greene
*/
public class ProdosSubdirectoryHeader extends ProdosCommonDirectoryHeader {
/**
* Constructor for ProdosSubdirectoryHeader.
* @param fileEntry
*/
public ProdosSubdirectoryHeader(byte[] fileEntry) {
super(fileEntry);
}
/**
* Return the name of this subdirectory.
*/
public String getSubdirectoryName() {
return AppleUtil.getProdosString(getFileEntry(), 0);
}
/**
* Return the block number of the parent directory which contains the
* file entry for this subdirectory.
*/
public int getParentPointer() {
return AppleUtil.getWordValue(getFileEntry(), 0x23);
}
/**
* Return the number of the file entry within the parent block.
*/
public int getParentEntry() {
return AppleUtil.getUnsignedByte(getFileEntry()[0x25]);
}
/**
* Return the length of the parent entry.
*/
public int getParentEntryLength() {
return AppleUtil.getWordValue(getFileEntry(), 0x26);
}
}

View File

@ -0,0 +1,44 @@
/*
* 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;
/**
* Represents the ProDOS volume directory header.
* <p>
* Date created: Oct 5, 2002 10:58:24 PM
* @author: Rob Greene
*/
public class ProdosVolumeDirectoryHeader extends ProdosCommonDirectoryHeader {
/**
* Constructor for ProdosVolumeDirectoryHeaderEntry.
* @param fileEntry
*/
public ProdosVolumeDirectoryHeader(byte[] fileEntry) {
super(fileEntry);
}
/**
* Return the name of this volume.
*/
public String getVolumeName() {
return AppleUtil.getProdosString(getFileEntry(), 0);
}
}

View File

@ -0,0 +1,239 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
/**
* Handle RDOS file entry format.
* <p>
* Since I was unable to locate the file entries on the internet, it is documented here:
* <tt>
* Offset Description<br>
* ====== ====================================================<br>
* $00-$17 File name; space-filled. If the first byte is $00, that is the end of the<br>
* directory. If the first byte is $80, the file is deleted.<br>
* $18 File type. Appears to be actual letter ('A'=Applesoft, etc)<br>
* $19 File length in blocks (block = sector = 256 bytes)<br>
* $1A-$1B Address of application. For Applesoft and binary; others may vary.<br>
* $1C-$1D Length in bytes of file.<br>
* $1E-$1F Starting block of application.<br>
* </tt>
* <p>
* Date created: Oct 7, 2002 1:36:56 PM
* @author: Rob Greene
*/
public class RdosFileEntry implements FileEntry {
private byte[] fileEntry;
private RdosFormatDisk disk;
/**
* Constructor for RdosFileEntry.
*/
public RdosFileEntry(byte[] fileEntry, RdosFormatDisk disk) {
super();
this.fileEntry = fileEntry;
this.disk = disk;
}
/**
* Return the number of blocks this file uses.
*/
public int getSizeInBlocks() {
return AppleUtil.getUnsignedByte(fileEntry[0x19]);
}
/**
* Return the starting block of this application.
*/
public int getStartingBlock() {
return AppleUtil.getWordValue(fileEntry, 0x1e);
}
/**
* Return the address of application.
*/
public int getAddress() {
return AppleUtil.getWordValue(fileEntry, 0x1a);
}
/**
* Return the name of this file.
*/
public String getFilename() {
return isDeleted() ? "<NOT IN USE> " : AppleUtil.getString(fileEntry, 0, 24);
}
/**
* Return the filetype of this file.
*/
public String getFiletype() {
return isDeleted() ? " " : AppleUtil.getString(fileEntry, 0x18, 1);
}
/**
* Locked doesn't appear to be a concept under RDOS.
*/
public boolean isLocked() {
return false;
}
/**
* Compute the size of this file (in bytes).
*/
public int getSize() {
return AppleUtil.getWordValue(fileEntry, 0x1c);
}
/**
* RDOS does not support directories.
*/
public boolean isDirectory() {
return false;
}
/**
* Retrieve the list of files in this directory.
* Since RDOS does not support directories, this will always return null.
*/
public List getFiles() {
return null;
}
/**
* Identify if this file has been deleted.
*/
public boolean isDeleted() {
return AppleUtil.getUnsignedByte(fileEntry[0]) == 0x80;
}
/**
* 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(getFiletype());
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getSizeInBlocks()));
list.add(getFilename());
numberFormat.setMinimumIntegerDigits(1);
list.add(numberFormat.format(getSize()));
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getStartingBlock()));
break;
case FormattedDisk.FILE_DISPLAY_DETAIL:
list.add(getFiletype());
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getSizeInBlocks()));
list.add(getFilename());
numberFormat.setMinimumIntegerDigits(1);
list.add(numberFormat.format(getSize()));
numberFormat.setMinimumIntegerDigits(3);
list.add(numberFormat.format(getStartingBlock()));
list.add("$" + AppleUtil.getFormattedWord(getAddress()));
list.add(isDeleted() ? "Deleted" : "");
break;
default: // FILE_DISPLAY_STANDARD
list.add(getFilename());
list.add(getFiletype());
list.add(numberFormat.format(getSize()));
list.add(isLocked() ? "Locked" : "");
break;
}
return list;
}
/**
* Get file data. This handles any operating-system specific issues.
* Currently, the disk itself handles this.
*/
public byte[] getFileData() {
byte[] rawdata = disk.getFileData(this);
byte[] filedata = new byte[getSize()];
System.arraycopy(rawdata, 0, filedata, 0, filedata.length);
return filedata;
}
/**
* 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() {
if (isApplesoftBasicFile()) {
return new ApplesoftFileFilter();
} else if (isIntegerBasicFile()) {
return new IntegerBasicFileFilter();
} else if (isTextFile()) {
return new TextFileFilter();
} else if (isBinaryFile()) {
int size = getSize();
// the minimum size is guessed a bit - I don't remember, but maybe there
// are 8 spare bytes at the end of the graphics screen
GraphicsFileFilter filter = new GraphicsFileFilter();
if (size >= 8185 && size <= 8192) {
filter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
return filter;
} else if (size >= 16377 && size <= 16384) {
filter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
return filter;
}
// fall through to BinaryFileFilter...
}
return new BinaryFileFilter();
}
/**
* Determine if this is a text file.
*/
public boolean isTextFile() {
return "T".equals(getFiletype());
}
/**
* Determine if this is an Applesoft BASIC file.
*/
public boolean isApplesoftBasicFile() {
return "A".equals(getFiletype());
}
/**
* Determine if this is an Integer BASIC file.
*/
public boolean isIntegerBasicFile() {
return "I".equals(getFiletype());
}
/**
* Determine if this is a binary file.
*/
public boolean isBinaryFile() {
return "B".equals(getFiletype());
}
}

View File

@ -0,0 +1,328 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2002 by Robert Greene
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.storage;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
/**
* Manages a disk that is in the RDOS format.
* <p>
* Note that the RDOS block interleave is different than the standard DOS 3.3 format.
* Thus, when the image is made, the sectors are skewed differently - use readRdosBlock
* to read the appropriate block number.
* <p>
* Also note that the operating system is itself the first file. Block #0 is really
* track 0, sector 0 - meaning that the first file should not (cannot) be deleted.
* <p>
* RDOS appears to have been placed on 13 sector disks. This limits the number of blocks
* to 455. It also may also cause incompatibilities with other formats and other cracks.
* <p>
* Date created: Oct 7, 2002 2:03:58 PM
* @author: Rob Greene
*/
public class RdosFormatDisk extends FormattedDisk {
/**
* Specifies the length of a file entry.
*/
public static final int ENTRY_LENGTH = 0x20;
/**
* Specifies the number of blocks on the disk.
* RDOS apparantly only worked on 5.25" disks.
*/
public static final int BLOCKS_ON_DISK = 455;
/**
* Use this inner interface for managing the disk usage data.
* This offloads format-specific implementation to the implementing class.
* A BitSet is used to track all blocks, as RDOS disks do not have a
* bitmap stored on the disk. This is safe since we know the number of blocks
* that exist. (BitSet length is of last set bit - unset bits at the end are
* "lost".)
* <p>
* Note one really unique point about RDOS - the entire disk is mapped out
* by the file entries. There are no blocks marked off, by default, by the
* operating system. However, the first file (RDOS itself) starts on block
* 0 (track 0, sector 0) and runs for 26 blocks - which covers all of track 0
* (the operating system) and the 10 sectors used for file entries.
*/
private class RdosDiskUsage implements DiskUsage {
private int location = -1;
private BitSet bitmap = null;
public boolean hasNext() {
return location == -1 || location < BLOCKS_ON_DISK - 1;
}
public void next() {
if (bitmap == null) {
bitmap = new BitSet(BLOCKS_ON_DISK);
// mark all blocks as unused
for (int b=0; b<BLOCKS_ON_DISK; b++) {
bitmap.set(b);
}
// for each file, mark the blocks used
Iterator files = getFiles().iterator();
while (files.hasNext()) {
RdosFileEntry fileEntry = (RdosFileEntry) files.next();
if (!fileEntry.isDeleted()) {
for (int b=0; b<fileEntry.getSizeInBlocks(); b++) {
bitmap.clear(fileEntry.getStartingBlock()+b);
}
}
}
location = 0;
} else {
location++;
}
}
public boolean isFree() {
return bitmap.get(location); // true = free
}
public boolean isUsed() {
return !bitmap.get(location); // false = used
}
}
/**
* Constructor for RdosFormatDisk.
* @param filename
* @param diskImage
*/
public RdosFormatDisk(String filename, byte[] diskImage) {
super(filename, diskImage);
}
/**
* Read an RDOS block. The sector skewing for RDOS seems to be different.
* This routine will convert the block number to a DOS track and sector,
* handling the sector change-over. The readSector method then should
* take care of various image formats.
* <p>
* Note that sectorSkew has the full 16 sectors, even though RDOS
* itself is a 13 sector format.
*/
public byte[] readRdosBlock(int block) {
int sectorSkew[] = { 0, 7, 0x0e, 6, 0x0d, 5, 0x0c, 4,
0x0b, 3, 0x0a, 2, 9, 1, 8, 0x0f };
int track = block / 13;
int sector = sectorSkew[block % 13];
return readSector(track, sector);
}
/**
* RDOS dos not support directories.
*/
public boolean canHaveDirectories() {
return false;
}
/**
* RDOS really does not have a disk name. Fake one.
*/
public String getDiskName() {
byte[] block = readRdosBlock(4);
return AppleUtil.getString(block, 0xe0, 0x20);
}
/**
* Retrieve a list of files.
*/
public List getFiles() {
List files = new ArrayList();
for (int b=13; b<23; b++) {
byte[] data = readRdosBlock(b);
for (int i=0; i<data.length; i+= ENTRY_LENGTH) {
byte[] entry = new byte[ENTRY_LENGTH];
System.arraycopy(data, i, entry, 0, entry.length);
if (AppleUtil.getUnsignedByte(entry[0]) != 0) {
RdosFileEntry fileEntry = new RdosFileEntry(entry, this);
files.add(fileEntry);
}
}
}
return files;
}
/**
* Identify the operating system format of this disk.
*/
public String getFormat() {
return "RDOS 2.1";
}
/**
* Return the number of free blocks.
*/
public int getFreeBlocks() {
return BLOCKS_ON_DISK - getUsedBlocks();
}
/**
* Return the number of used blocks.
*/
public int getUsedBlocks() {
int used = 0;
Iterator files = getFiles().iterator();
while (files.hasNext()) {
RdosFileEntry fileEntry = (RdosFileEntry) files.next();
if (!fileEntry.isDeleted()) used+= fileEntry.getSizeInBlocks();
}
return used;
}
/**
* Return the amount of free space in bytes.
*/
public int getFreeSpace() {
return getFreeBlocks() * SECTOR_SIZE;
}
/**
* Return the amount of used space in bytes.
*/
public int getUsedSpace() {
return getUsedBlocks() * SECTOR_SIZE;
}
/**
* Get suggested dimensions for display of bitmap.
* Since RDOS uses blocks, a null is returned indicating no suggsetions.
*/
public int[] getBitmapDimensions() {
return null;
}
/**
* Get the length of the bitmap.
*/
public int getBitmapLength() {
return BLOCKS_ON_DISK;
}
/**
* Get the disk usage iterator.
*/
public DiskUsage getDiskUsage() {
return new RdosDiskUsage();
}
/**
* Get the labels to use in the bitmap.
*/
public String[] getBitmapLabels() {
return new String[] { "Block" };
}
/**
* Get Pascal-specific disk information.
*/
public List getDiskInformation() {
List list = super.getDiskInformation();
list.add(new DiskInformation("Total Blocks", BLOCKS_ON_DISK));
list.add(new DiskInformation("Free Blocks", getFreeBlocks()));
list.add(new DiskInformation("Used Blocks", getUsedBlocks()));
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("Type", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Name", 24, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Size", 6, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Starting Block", 3, FileColumnHeader.ALIGN_RIGHT));
break;
case FILE_DISPLAY_DETAIL:
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Name", 24, FileColumnHeader.ALIGN_LEFT));
list.add(new FileColumnHeader("Size", 6, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Starting Block", 3, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Address", 5, FileColumnHeader.ALIGN_RIGHT));
list.add(new FileColumnHeader("Deleted?", 7, FileColumnHeader.ALIGN_CENTER));
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; // FIXME - not implemented
}
/**
* Indicates if this disk image can create a file.
*/
public boolean canCreateFile() {
return false; // FIXME - not implemented
}
/**
* Indicates if this disk image can delete a file.
*/
public boolean canDeleteFile() {
return false; // FIXME - not implemented
}
/**
* Get the data associated with the specified FileEntry.
*/
public byte[] getFileData(FileEntry fileEntry) {
if ( !(fileEntry instanceof RdosFileEntry)) {
throw new IllegalArgumentException("Most have a RDOS file entry!");
}
RdosFileEntry rdosEntry = (RdosFileEntry) fileEntry;
int startingBlock = rdosEntry.getStartingBlock();
byte[] fileData = new byte[rdosEntry.getSizeInBlocks() * SECTOR_SIZE];
int offset = 0;
for (int blockOffset = 0; blockOffset < rdosEntry.getSizeInBlocks(); blockOffset++) {
byte[] blockData = readRdosBlock(startingBlock + blockOffset);
System.arraycopy(blockData, 0, fileData, offset, blockData.length);
offset+= blockData.length;
}
return fileData;
}
}

View File

@ -0,0 +1,69 @@
/*
* 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;
/**
* Filter the given file data for text.
* <p>
* Date created: Nov 2, 2002 9:11:27 PM
* @author: Rob Greene
*/
public class TextFileFilter implements FileFilter {
/**
* Constructor for TextFileFilter.
*/
public TextFileFilter() {
super();
}
/**
* Process the given FileEntry and return a byte array with filtered data.
* @see com.webcodepro.applecommander.storage.FileFilter#filter(byte[])
*/
public byte[] filter(FileEntry fileEntry) {
byte[] fileData = fileEntry.getFileData();
byte[] workingData = new byte[fileData.length];
int position = 0;
for (int i=0; i<fileData.length; i++) {
byte byt = fileData[i];
if (byt != 0) {
if ((byt & 0x80) != 0) { // high bit set
workingData[position++] = (byte)(byt & 0x7f);
} else {
workingData[position++] = byt;
}
}
}
byte[] filteredData = new byte[position];
System.arraycopy(workingData, 0, filteredData, 0, filteredData.length);
return filteredData;
}
/**
* Give suggested file name.
*/
public String getSuggestedFileName(FileEntry fileEntry) {
String fileName = fileEntry.getFilename().trim();
if (!fileName.toLowerCase().endsWith(".txt")) {
fileName = fileName + ".txt";
}
return fileName;
}
}

View File

@ -0,0 +1,223 @@
/*
* 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.test;
import java.io.IOException;
import java.util.List;
import junit.framework.TestCase;
import com.webcodepro.applecommander.storage.ApplesoftFileFilter;
import com.webcodepro.applecommander.storage.BinaryFileFilter;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
import com.webcodepro.applecommander.storage.IntegerBasicFileFilter;
import com.webcodepro.applecommander.storage.TextFileFilter;
import com.webcodepro.applecommander.storage.FormattedDisk.DiskUsage;
/**
* Test DiskHelper and its related Apple2 classes.
* <p>
* Date created: Oct 3, 2002 11:35:26 PM
* @author: Rob Greene
*/
public class DiskHelperTest extends TestCase {
public DiskHelperTest(String name) {
super(name);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(DiskHelperTest.class);
}
public void testLoadDos33() throws IOException {
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/DOS 3.3.po");
assertApplesoftFile(disk, "HELLO");
assertIntegerFile(disk, "ANIMALS");
assertTextFile(disk, "APPLE PROMS");
assertBinaryFile(disk, "BOOT13");
}
public void testLoadMaster() throws IOException {
showDirectory("C:/My Apple2/Disks/MASTER.DSK");
}
public void testLoadGalacticAttack1() throws IOException {
showDirectory("C:/My Apple2/Disks/galatt.dsk");
}
public void testLoadProdos() throws IOException {
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/Prodos.dsk");
assertApplesoftFile(disk, "COPY.ME");
assertBinaryFile(disk, "SETTINGS");
assertBinaryFile(disk, "PRODOS");
}
public void testLoadMarbleMadness() throws IOException {
showDirectory("C:/My Apple2/Disks/Marble Madness (1985)(Electronic Arts).2mg");
}
public void testLoadHd1() throws IOException {
showDirectory("C:/My Apple2/ApplePC/hd1.hdv");
}
public void testRdosBoot() throws IOException {
showDirectory("C:/My Apple2/Disks/RDOSboot.dsk");
}
public void testSsiSave() throws IOException {
showDirectory("C:/My Apple2/Disks/SSIsave.dsk");
}
public void testPhan2d1() throws IOException {
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/phan2d1.dsk");
assertApplesoftFile(disk, "PHANTASIE II");
assertBinaryFile(disk, "TWN21");
assertTextFile(disk, "ITEM");
assertGraphicsFile(disk, "ICE DRAGON");
}
public void testPhan2d2() throws IOException {
showDirectory("C:/My Apple2/Disks/phan2d2.dsk");
}
public void testPhantasie1() throws IOException {
showDirectory("C:/My Apple2/Disks/Phantasie1.dsk");
}
public void testPhantasie2() throws IOException {
showDirectory("C:/My Apple2/Disks/Phantasie2.dsk");
}
public void testCavernsOfFreitag() throws IOException {
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/CavernsOfFreitag.dsk");
assertGraphicsFile(disk, "TITLE.PIC");
}
protected FormattedDisk showDirectory(String imageName) throws IOException {
Disk disk = new Disk(imageName);
FormattedDisk formattedDisk = disk.getFormattedDisk();
System.out.println();
System.out.println(formattedDisk.getDiskName());
List files = formattedDisk.getFiles();
if (files != null) {
showFiles(files, "");
}
System.out.println(formattedDisk.getFreeSpace() + " bytes free.");
System.out.println(formattedDisk.getUsedSpace() + " bytes used.");
System.out.println("This disk " + (formattedDisk.canHaveDirectories() ? "does" : "does not") +
" support directories.");
System.out.println("This disk is formatted in the " + formattedDisk.getFormat() + " format.");
System.out.println();
showDiskUsage(formattedDisk);
return formattedDisk;
}
protected void showFiles(List files, String indent) {
for (int i=0; i<files.size(); i++) {
FileEntry entry = (FileEntry) files.get(i);
if (!entry.isDeleted()) {
List data = entry.getFileColumnData(FormattedDisk.FILE_DISPLAY_NATIVE);
System.out.print(indent);
for (int d=0; d<data.size(); d++) {
System.out.print(data.get(d));
System.out.print(" ");
}
System.out.println();
}
if (entry.isDirectory()) {
showFiles(entry.getFiles(), indent + " ");
}
}
}
protected void showDiskUsage(FormattedDisk disk) {
int[] dimensions = disk.getBitmapDimensions();
DiskUsage usage = disk.getDiskUsage();
if (usage == null) {
System.out.println("A bitmap is not available.");
return;
}
if (dimensions == null) {
int i=0;
while (usage.hasNext()) {
if (i > 0 && i % 80 == 0) System.out.println();
usage.next();
System.out.print(usage.isFree() ? "." : "U");
i++;
}
System.out.println();
} else {
for (int y=dimensions[0]-1; y>=0; y--) {
for (int x=0; x<dimensions[1]; x++) {
usage.next();
System.out.print(usage.isFree() ? "." : "U");
}
System.out.println();
}
}
System.out.println("U = used, . = free");
}
protected void assertApplesoftFile(FormattedDisk disk, String filename) {
assertNotNull(filename + " test: Disk should not be null", disk);
FileEntry fileEntry = disk.getFile(filename);
assertNotNull(filename + " test: File not found", disk);
assertTrue("ApplesoftFileFilter was not chosen",
fileEntry.getSuggestedFilter() instanceof ApplesoftFileFilter);
}
protected void assertIntegerFile(FormattedDisk disk, String filename) {
assertNotNull(filename + " test: Disk should not be null", disk);
FileEntry fileEntry = disk.getFile(filename);
assertNotNull(filename + " test: File not found", disk);
assertTrue("IntegerBasicFileFilter was not chosen",
fileEntry.getSuggestedFilter() instanceof IntegerBasicFileFilter);
}
protected void assertTextFile(FormattedDisk disk, String filename) {
assertNotNull(filename + " test: Disk should not be null", disk);
FileEntry fileEntry = disk.getFile(filename);
assertNotNull(filename + " test: File not found", disk);
assertTrue("TextFileFilter was not chosen",
fileEntry.getSuggestedFilter() instanceof TextFileFilter);
}
protected void assertBinaryFile(FormattedDisk disk, String filename) {
assertNotNull(filename + " test: Disk should not be null", disk);
FileEntry fileEntry = disk.getFile(filename);
assertNotNull(filename + " test: File not found", disk);
assertTrue("BinaryFileFilter was not chosen",
fileEntry.getSuggestedFilter() instanceof BinaryFileFilter);
}
protected void assertGraphicsFile(FormattedDisk disk, String filename) {
assertNotNull(filename + " test: Disk should not be null", disk);
FileEntry fileEntry = disk.getFile(filename);
assertNotNull(filename + " test: File not found", disk);
assertTrue("GraphicsFileFilter was not chosen",
fileEntry.getSuggestedFilter() instanceof GraphicsFileFilter);
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.ui;
import java.util.Arrays;
import com.webcodepro.applecommander.ui.swt.SwtAppleCommander;
/**
* Launch AppleCommander.
* This application attempts to identify which type of user-interface to
* launch. Additionally, there are some command-line interface switches
* available - see the about method.
* <p>
* Date created: Nov 16, 2002 9:13:25 PM
* @author: Rob Greene
*/
public class AppleCommander {
public static final String VERSION = "1.1.1";
/**
* Launch AppleCommander.
*/
public static void main(String[] args) {
if (args.length == 0) {
try {
Class.forName("org.eclipse.swt.SWT");
SwtAppleCommander.main(args);
} catch (ClassNotFoundException ex) {
System.err.println("Sorry, the SWT libraries do not appear to be available (yet).");
//SwingAppleCommander.main(args);
}
} else {
String[] extraArgs = new String[args.length - 1];
System.arraycopy(args, 1, extraArgs, 0, extraArgs.length);
if ("-swt".equalsIgnoreCase(args[0])) {
SwtAppleCommander.main(extraArgs);
} else if ("-swing".equalsIgnoreCase(args[0])) {
System.err.println("Sorry, the Swing GUI is not available (yet).");
//SwingAppleCommander.main(extraArgs);
} else if ("-command".equalsIgnoreCase(args[0])) {
System.err.println("Sorry, the command line user interface is not available (yet).");
//CommandLineAppleCommander.main(extraArgs);
} else {
System.err.println("Unknown user interface specified!");
System.err.println("Use -swt, -swing, or -command.");
}
}
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.ui;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;
/**
* Provide a generalized and common mechanism to handle user preferences throughout
* all AppleCommander user interfaces.
* <p>
* Date created: Nov 18, 2002 10:08:34 PM
* @author: Rob Greene
*/
public class UserPreferences {
private static final String FILENAME = "AppleCommander.preferences";
private static final String IMAGE_DIRECTORY = "imageDirectory";
private static final String EXPORT_DIRECTORY = "exportDirectory";
private static UserPreferences instance;
private Properties properties = new Properties();
/**
* Hide constructor from other classes.
*/
private UserPreferences() {
}
/**
* Get the singleton UserPreferences.
*/
public static UserPreferences getInstance() {
if (instance == null) {
instance = new UserPreferences();
instance.load();
}
return instance;
}
/**
* Initialize the user preferences from disk.
*/
private void load() {
try {
FileInputStream inputStream = new FileInputStream(FILENAME);
properties.load(inputStream);
inputStream.close();
} catch (Exception ignored) {
}
}
/**
* Save the user preferences to disk.
*/
public void save() {
try {
FileOutputStream outputStream = new FileOutputStream(FILENAME);
properties.store(outputStream, "AppleCommander user preferences");
outputStream.close();
} catch (Exception ignored) {
}
}
/**
* Get the disk image directory.
*/
public String getDiskImageDirectory() {
return properties.getProperty(IMAGE_DIRECTORY);
}
/**
* Get the export directory.
*/
public String getExportDirectory() {
return properties.getProperty(EXPORT_DIRECTORY);
}
/**
* Set the disk image directory.
*/
public void setDiskImageDirectory(String diskImageDirectory) {
properties.setProperty(IMAGE_DIRECTORY, diskImageDirectory);
}
/**
* Set the export directory.
*/
public void setExportDirectory(String exportDirectory) {
properties.setProperty(EXPORT_DIRECTORY, exportDirectory);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

View File

@ -0,0 +1,112 @@
/*
* 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.ui.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import com.webcodepro.applecommander.storage.AppleWorksWordProcessorFileFilter;
/**
* Choose format for AppleWorks Word Processor export.
* <p>
* Date created: Nov 15, 2002 11:31:15 PM
* @author: Rob Greene
*/
public class AppleWorksWordProcessorPane extends WizardPane {
private Composite parent;
private Object layoutData;
private Composite control;
private ExportWizard wizard;
/**
* Constructor for AppleWorksWordProcessorPane.
*/
public AppleWorksWordProcessorPane(Composite parent, ExportWizard exportWizard, Object layoutData) {
super();
this.parent = parent;
this.wizard = exportWizard;
this.layoutData = layoutData;
}
/**
* Get the next WizardPane.
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
*/
public WizardPane getNextPane() {
return new ExportFileDestinationPane(parent, wizard, layoutData);
}
/**
* Create and display the wizard pane.
* @see com.webcodepro.applecommander.gui.WizardPane#open()
*/
public void open() {
wizard.enableFinishButton(false);
wizard.enableNextButton(true);
control = new Composite(parent, SWT.NULL);
control.setLayoutData(layoutData);
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.justify = true;
layout.marginBottom = 5;
layout.marginLeft = 5;
layout.marginRight = 5;
layout.marginTop = 5;
layout.spacing = 3;
control.setLayout(layout);
Label label = new Label(control, SWT.WRAP);
label.setText("Please choose the appropriate format:");
RowLayout subpanelLayout = new RowLayout(SWT.VERTICAL);
subpanelLayout.justify = true;
subpanelLayout.spacing = 3;
Button button = new Button(control, SWT.RADIO);
button.setText("Text");
button.setSelection(getFilter().isTextRendering());
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getFilter().setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_TEXT);
}
});
button = new Button(control, SWT.RADIO);
button.setText("HTML");
button.setSelection(getFilter().isHtmlRendering());
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getFilter().setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_HTML);
}
});
}
/**
* Dispose of any resources.
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
*/
public void dispose() {
control.dispose();
control = null;
}
/**
* Get the AppleWorks word processor filter.
*/
protected AppleWorksWordProcessorFileFilter getFilter() {
return (AppleWorksWordProcessorFileFilter) wizard.getFileFilter();
}
}

View File

@ -0,0 +1,836 @@
/*
* 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.ui.swt;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.webcodepro.applecommander.storage.AppleWorksWordProcessorFileFilter;
import com.webcodepro.applecommander.storage.ApplesoftFileFilter;
import com.webcodepro.applecommander.storage.BinaryFileFilter;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FileEntryComparator;
import com.webcodepro.applecommander.storage.FileFilter;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
import com.webcodepro.applecommander.storage.IntegerBasicFileFilter;
import com.webcodepro.applecommander.storage.TextFileFilter;
import com.webcodepro.applecommander.storage.FormattedDisk.FileColumnHeader;
import com.webcodepro.applecommander.ui.UserPreferences;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
/**
* Build the Disk File tab for the Disk Window.
* <p>
* Date created: Nov 17, 2002 9:46:53 PM
* @author: Rob Greene
*/
public class DiskExplorerTab {
private Shell shell;
private SashForm sashForm;
private Tree directoryTree;
private Table fileTable;
private ToolBar toolBar;
private ToolItem exportToolItem;
private ToolItem importToolItem;
private ToolItem deleteToolItem;
private ImageManager imageManager;
private UserPreferences userPreferences = UserPreferences.getInstance();
private FormattedDisk disk;
private FileFilter fileFilter;
private GraphicsFileFilter graphicsFilter = new GraphicsFileFilter();
private AppleWorksWordProcessorFileFilter awpFilter = new AppleWorksWordProcessorFileFilter();
private int currentFormat = FormattedDisk.FILE_DISPLAY_STANDARD;
private boolean formatChanged;
private List currentFileList;
private Map columnWidths = new HashMap();
private boolean showDeletedFiles;
/**
* Create the DISK INFO tab.
*/
public DiskExplorerTab(CTabFolder tabFolder, FormattedDisk disk, ImageManager imageManager) {
this.disk = disk;
this.shell = tabFolder.getShell();
this.imageManager = imageManager;
createFilesTab(tabFolder);
}
/**
* Dispose of resources.
*/
public void dispose() {
sashForm.dispose();
directoryTree.dispose();
fileTable.dispose();
exportToolItem.dispose();
importToolItem.dispose();
deleteToolItem.dispose();
toolBar.dispose();
directoryTree = null;
fileTable = null;
currentFileList = null;
}
/**
* Create the FILES tab.
*/
protected void createFilesTab(CTabFolder tabFolder) {
CTabItem ctabitem = new CTabItem(tabFolder, SWT.NULL);
ctabitem.setText("Files");
Composite composite = new Composite(tabFolder, SWT.NULL);
ctabitem.setControl(composite);
GridLayout gridLayout = new GridLayout(1, false);
composite.setLayout(gridLayout);
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
createFileToolBar(composite, gridData);
sashForm = new SashForm(composite, SWT.NONE);
sashForm.setOrientation(SWT.HORIZONTAL);
gridData = new GridData(GridData.FILL_BOTH);
gridData.horizontalSpan = 2;
sashForm.setLayoutData(gridData);
directoryTree = new Tree(sashForm, SWT.SINGLE | SWT.BORDER);
directoryTree.setMenu(createDirectoryPopupMenu());
directoryTree.addSelectionListener(new SelectionListener() {
/**
* Single-click handler.
*/
public void widgetSelected(SelectionEvent event) {
changeCurrentFormat(currentFormat); // minor hack
}
/**
* Double-click handler.
*/
public void widgetDefaultSelected(SelectionEvent event) {
Tree item = (Tree) event.getSource();
TreeItem[] treeItem = item.getSelection();
treeItem[0].setExpanded(!treeItem[0].getExpanded());
}
});
fileTable = new Table(sashForm, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
fileTable.setHeaderVisible(true);
sashForm.setWeights(new int[] {1,2});
TreeItem diskItem = new TreeItem(directoryTree, SWT.BORDER);
diskItem.setText(disk.getDiskName());
diskItem.setData(disk);
directoryTree.setSelection(new TreeItem[] { diskItem });
if (disk.canHaveDirectories()) {
Iterator files = disk.getFiles().iterator();
while (files.hasNext()) {
FileEntry entry = (FileEntry) files.next();
if (entry.isDirectory()) {
TreeItem item = new TreeItem(diskItem, SWT.BORDER);
item.setText(entry.getFilename());
item.setData(entry);
addDirectoriesToTree(item, entry);
}
}
}
computeColumnWidths(FormattedDisk.FILE_DISPLAY_STANDARD);
computeColumnWidths(FormattedDisk.FILE_DISPLAY_NATIVE);
computeColumnWidths(FormattedDisk.FILE_DISPLAY_DETAIL);
formatChanged = true;
fillFileTable(disk.getFiles());
}
/**
* Construct the popup menu for the directory table on the File tab.
*/
protected Menu createDirectoryPopupMenu() {
Menu menu = new Menu(shell, SWT.POP_UP);
MenuItem item = new MenuItem(menu, SWT.CASCADE);
item.setText("Expand");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
TreeItem[] treeItem = directoryTree.getSelection();
treeItem[0].setExpanded(true);
}
});
item.setEnabled(disk.canHaveDirectories());
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Collapse");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
TreeItem[] treeItem = directoryTree.getSelection();
treeItem[0].setExpanded(false);
}
});
item.setEnabled(disk.canHaveDirectories());
item = new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Expand All");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
TreeItem[] treeItem = directoryTree.getSelection();
setDirectoryExpandedStates(treeItem[0], true);
}
});
item.setEnabled(disk.canHaveDirectories());
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Collapse All");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
TreeItem[] treeItem = directoryTree.getSelection();
setDirectoryExpandedStates(treeItem[0], false);
}
});
item.setEnabled(disk.canHaveDirectories());
return menu;
}
/**
* Construct the popup menu for the file table on the File tab.
*/
protected Menu createFilePopupMenu() {
Menu menu = new Menu(shell, SWT.POP_UP);
MenuItem item = new MenuItem(menu, SWT.CASCADE);
item.setText("Import...");
item.setEnabled(disk.canCreateFile() && disk.canWriteFileData());
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
// FIXME
}
});
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Export");
item.setEnabled(disk.canReadFileData());
item.setMenu(createFileExportMenu(SWT.DROP_DOWN));
item = new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Delete...");
item.setEnabled(disk.canDeleteFile());
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
// FIXME
}
});
return menu;
}
/**
* Construct the popup menu for the export button on the toolbar.
*/
protected Menu createFileExportMenu(int style) {
Menu menu = new Menu(shell, style);
MenuItem item = new MenuItem(menu, SWT.NONE);
item.setText("Raw disk data...");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
fileFilter = null;
exportFile(null);
}
});
item = new MenuItem(menu, SWT.NONE);
item.setText("Binary...");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
fileFilter = new BinaryFileFilter();
exportFile(null);
}
});
item = new MenuItem(menu, SWT.NONE);
item.setText("Applesoft Basic...");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
fileFilter = new ApplesoftFileFilter();
exportFile(null);
}
});
item = new MenuItem(menu, SWT.NONE);
item.setText("Integer Basic...");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
fileFilter = new IntegerBasicFileFilter();
exportFile(null);
}
});
item = new MenuItem(menu, SWT.NONE);
item.setText("ASCII Text...");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
fileFilter = new TextFileFilter();
exportFile(null);
}
});
item = new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.NONE);
item.setText("AppleWorks WordProcessor File...");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
fileFilter = awpFilter;
exportFile(null);
}
});
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Rendering");
Menu subMenu = new Menu(shell, SWT.DROP_DOWN);
item.setMenu(subMenu);
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(awpFilter.isTextRendering());
subItems[1].setSelection(awpFilter.isHtmlRendering());
}
});
item = new MenuItem(subMenu, SWT.RADIO);
item.setText("Text");
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate rendering style.
*/
public void widgetSelected(SelectionEvent event) {
awpFilter.setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_TEXT);
}
});
item = new MenuItem(subMenu, SWT.RADIO);
item.setText("HTML");
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate rendering style.
*/
public void widgetSelected(SelectionEvent event) {
awpFilter.setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_HTML);
}
});
item = new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.NONE);
item.setText("Graphics...");
item.setEnabled(graphicsFilter.isCodecAvailable());
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
fileFilter = graphicsFilter;
exportFile(null);
}
});
// Add graphics mode
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Mode");
item.setEnabled(graphicsFilter.isCodecAvailable());
subMenu = new Menu(shell, SWT.DROP_DOWN);
item.setMenu(subMenu);
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(graphicsFilter.isHiresBlackAndWhiteMode());
subItems[1].setSelection(graphicsFilter.isHiresColorMode());
subItems[2].setSelection(graphicsFilter.isDoubleHiresBlackAndWhiteMode());
subItems[3].setSelection(graphicsFilter.isDoubleHiresColorMode());
}
});
item = new MenuItem(subMenu, SWT.RADIO);
item.setText("Hi-Res B&W");
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate graphics mode.
*/
public void widgetSelected(SelectionEvent event) {
graphicsFilter.setMode(GraphicsFileFilter.MODE_HGR_BLACK_AND_WHITE);
}
});
item = new MenuItem(subMenu, SWT.RADIO);
item.setText("Hi-Res Color");
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate graphics mode.
*/
public void widgetSelected(SelectionEvent event) {
graphicsFilter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
}
});
item = new MenuItem(subMenu, SWT.RADIO);
item.setText("Double Hi-Res B&W");
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate graphics mode.
*/
public void widgetSelected(SelectionEvent event) {
graphicsFilter.setMode(GraphicsFileFilter.MODE_DHR_BLACK_AND_WHITE);
}
});
item = new MenuItem(subMenu, SWT.RADIO);
item.setText("Double Hi-Res COLOR");
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the appropriate graphics mode.
*/
public void widgetSelected(SelectionEvent event) {
graphicsFilter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
}
});
// Add graphics formats, if any are defined.
String[] formats = graphicsFilter.getFileExtensions();
if (formats != null && formats.length > 0) {
item = new MenuItem(menu, SWT.CASCADE);
item.setText("Format");
item.setEnabled(graphicsFilter.isCodecAvailable());
subMenu = new Menu(shell, SWT.DROP_DOWN);
item.setMenu(subMenu);
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();
for (int i=0; i<subItems.length; i++) {
subItems[i].setSelection(subItems[i].getText().
equals(graphicsFilter.getExtension()));
}
}
});
// Add all graphics formats...
for (int i=0; i<formats.length; i++) {
item = new MenuItem(subMenu, SWT.RADIO);
item.setText(formats[i]);
item.addSelectionListener(new SelectionAdapter() {
/**
* Set the file extension to use.
*/
public void widgetSelected(SelectionEvent event) {
MenuItem menuItem = (MenuItem) event.getSource();
graphicsFilter.setExtension(menuItem.getText());
}
});
}
}
return menu;
}
/**
* Change the "expanded" state of the node.
*/
protected void setDirectoryExpandedStates(TreeItem treeItem, boolean expand) {
treeItem.setExpanded(expand);
TreeItem[] treeItems = treeItem.getItems();
for (int i=0; i<treeItems.length; i++) {
setDirectoryExpandedStates(treeItems[i], expand);
}
}
/**
* Pre-compute column widths for the file tab.
* These can and are over-ridden by user sizing.
*/
protected void computeColumnWidths(int format) {
List headers = disk.getFileColumnHeaders(format);
int[] headerWidths = new int[headers.size()];
GC gc = new GC(shell);
for (int i=0; i<headers.size(); i++) {
FileColumnHeader header = (FileColumnHeader) headers.get(i);
if (header.getTitle().length() >= header.getMaximumWidth()) {
headerWidths[i] = gc.stringExtent(header.getTitle()).x + gc.stringExtent("WW").x;
} else {
headerWidths[i] = gc.stringExtent("W").x * header.getMaximumWidth();
}
}
gc.dispose();
gc = null;
columnWidths.put(new Integer(format), headerWidths);
}
/**
* Preserve the column widths.
*/
protected void preserveColumnWidths() {
TableColumn[] columns = fileTable.getColumns();
int[] widths = new int[columns.length];
for (int i=0; i<columns.length; i++) {
widths[i] = columns[i].getWidth();
}
columnWidths.put(new Integer(currentFormat), widths);
}
/**
* Display files in the fileTable.
*/
protected void fillFileTable(List fileList) {
int[] weights = sashForm.getWeights();
if (formatChanged) {
fileTable.dispose();
fileTable = new Table(sashForm, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
fileTable.setHeaderVisible(true);
fileTable.setMenu(createFilePopupMenu());
fileTable.addSelectionListener(new SelectionListener() {
/**
* Single-click handler.
*/
public void widgetSelected(SelectionEvent event) {
if (fileTable.getSelectionCount() > 0) {
exportToolItem.setEnabled(disk.canReadFileData());
importToolItem.setEnabled(disk.canCreateFile() && disk.canWriteFileData());
deleteToolItem.setEnabled(disk.canDeleteFile());
} else {
exportToolItem.setEnabled(false);
importToolItem.setEnabled(false);
deleteToolItem.setEnabled(false);
}
}
/**
* Double-click handler.
*/
public void widgetDefaultSelected(SelectionEvent event) {
// No action defined at this time
}
});
TableColumn column = null;
List headers = disk.getFileColumnHeaders(currentFormat);
int[] widths = (int[])columnWidths.get(new Integer(currentFormat));
for (int i=0; i<headers.size(); i++) {
FileColumnHeader header = (FileColumnHeader) headers.get(i);
int align = header.isCenterAlign() ? SWT.CENTER :
header.isLeftAlign() ? SWT.LEFT : SWT.RIGHT;
column = new TableColumn(fileTable, align);
column.setText(header.getTitle());
column.setWidth(widths[i]);
final int columnIndex = i;
column.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
sortFileTable(columnIndex);
}
});
}
} else {
fileTable.removeAll();
}
Iterator files = fileList.iterator();
while (files.hasNext()) {
FileEntry entry = (FileEntry) files.next();
if (showDeletedFiles || !entry.isDeleted()) {
TableItem item = new TableItem(fileTable, 0);
List data = entry.getFileColumnData(currentFormat);
for (int i=0; i<data.size(); i++) {
item.setText(i, (String)data.get(i));
}
item.setData(entry);
}
}
sashForm.setWeights(weights);
formatChanged = false;
currentFileList = fileList;
// disable all file-leve operations:
exportToolItem.setEnabled(false);
importToolItem.setEnabled(false);
deleteToolItem.setEnabled(false);
}
/**
* Export all selected files.
*/
private void exportFile(String directory) {
boolean promptForIndividualFiles = (directory == null);
TableItem[] selection = fileTable.getSelection();
for (int i=0; i<selection.length; i++) {
TableItem tableItem = selection[i];
FileEntry fileEntry = (FileEntry) tableItem.getData();
String filename = null;
if (promptForIndividualFiles) {
FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
fileDialog.setFilterPath(userPreferences.getExportDirectory());
if (fileFilter != null) {
fileDialog.setFileName(fileFilter.getSuggestedFileName(fileEntry));
} else {
fileDialog.setFileName(fileEntry.getFilename());
}
filename = fileDialog.open();
directory = fileDialog.getFilterPath();
} else {
filename = directory + File.separator + fileFilter.getSuggestedFileName(fileEntry);
}
if (filename != null) {
userPreferences.setExportDirectory(directory);
try {
File file = new File(filename);
if (file.exists()) {
Shell finalShell = shell;
MessageBox box = new MessageBox(finalShell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
box.setText("File already exists!");
box.setMessage(
"The file '" + filename + "' already exists. "
+ "Do you want to over-write it?");
if (box.open() == SWT.NO) {
return; // do not overwrite file
}
}
byte[] data = null;
if (fileFilter != null) {
data = fileFilter.filter(fileEntry);
} else {
data = disk.getFileData(fileEntry);
}
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(data);
outputStream.close();
} catch (Exception ex) {
Shell finalShell = shell;
String errorMessage = ex.getMessage();
if (errorMessage == null) {
errorMessage = ex.getClass().getName();
}
MessageBox box = new MessageBox(finalShell,
SWT.ICON_ERROR | SWT.OK | SWT.CANCEL);
box.setText("Unable to export file data!");
box.setMessage(
"Unable to export '" + filename + "'.\n\n"
+ "AppleCommander was unable to save the disk\n"
+ "data. The system error given was '"
+ errorMessage + "'\n\n"
+ "Sorry!\n\n"
+ "Press OK to continue export or CANCEL to cancel export.");
int button = box.open();
if (button == SWT.CANCEL) break; // break out of loop
}
}
}
}
/**
* Sort the file table by the specified columnIndex.
*/
protected void sortFileTable(int columnIndex) {
Collections.sort(currentFileList, new FileEntryComparator(columnIndex, currentFormat));
fillFileTable(currentFileList);
}
/**
* Helper function for building fileTree.
*/
protected void addDirectoriesToTree(TreeItem directoryItem, FileEntry directoryEntry) {
Iterator files = directoryEntry.getFiles().iterator();
while (files.hasNext()) {
final FileEntry entry = (FileEntry) files.next();
if (entry.isDirectory()) {
TreeItem item = new TreeItem(directoryItem, SWT.BORDER);
item.setText(entry.getFilename());
item.setData(entry);
addDirectoriesToTree(item, entry);
}
}
}
/**
* Creates the FILE tab toolbar.
*/
private void createFileToolBar(Composite composite, Object layoutData) {
toolBar = new ToolBar(composite, SWT.FLAT);
if (layoutData != null) toolBar.setLayoutData(layoutData);
ToolItem item = new ToolItem(toolBar, SWT.RADIO);
item.setImage(imageManager.getStandardFileViewIcon());
item.setText("Standard");
item.setToolTipText("Displays files in standard format");
item.setSelection(true);
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
changeCurrentFormat(FormattedDisk.FILE_DISPLAY_STANDARD);
}
});
item = new ToolItem(toolBar, SWT.RADIO);
item.setImage(imageManager.getNativeFileViewIcon());
item.setText("Native");
item.setToolTipText("Displays files in native format for the operating system");
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
changeCurrentFormat(FormattedDisk.FILE_DISPLAY_NATIVE);
}
});
item = new ToolItem(toolBar, SWT.RADIO);
item.setImage(imageManager.getDetailFileViewIcon());
item.setText("Detail");
item.setToolTipText("Displays files in with full details");
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
changeCurrentFormat(FormattedDisk.FILE_DISPLAY_DETAIL);
}
});
item = new ToolItem(toolBar, SWT.SEPARATOR);
item = new ToolItem(toolBar, SWT.CHECK);
item.setImage(imageManager.getDeletedFilesIcon());
item.setText("Deleted");
item.setToolTipText("Show deleted files");
item.setEnabled(disk.supportsDeletedFiles());
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
ToolItem button = (ToolItem) e.getSource();
showDeletedFiles = button.getSelection();
fillFileTable(currentFileList);
}
});
item = new ToolItem(toolBar, SWT.SEPARATOR);
importToolItem = new ToolItem(toolBar, SWT.PUSH);
importToolItem.setImage(imageManager.getImportFileIcon());
importToolItem.setText("Import...");
importToolItem.setToolTipText("Import a file");
importToolItem.setEnabled(false);
importToolItem.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
// FIXME
}
});
exportToolItem = new ToolItem(toolBar, SWT.DROP_DOWN);
exportToolItem.setImage(imageManager.getExportFileIcon());
exportToolItem.setText("Export...");
exportToolItem.setToolTipText("Export a file");
exportToolItem.setEnabled(false);
exportToolItem.addSelectionListener(
new DropDownSelectionListener(createFileExportMenu(SWT.NONE)));
exportToolItem.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent event) {
if (event.detail != SWT.ARROW) {
ExportWizard wizard = new ExportWizard(shell,
imageManager.getExportWizardLogo(), disk);
// Get a suggseted filter, if possible:
FileEntry fileEntry = (FileEntry) fileTable.getSelection()[0].getData();
if (fileEntry != null) {
fileFilter = fileEntry.getSuggestedFilter();
}
// Start wizard:
wizard.setFileFilter(fileFilter);
wizard.setDirectory(userPreferences.getExportDirectory());
wizard.open();
if (wizard.isWizardCompleted()) {
fileFilter = wizard.getFileFilter();
String exportDirectory = wizard.getDirectory();
exportFile(exportDirectory);
}
}
}
});
item = new ToolItem(toolBar, SWT.SEPARATOR);
deleteToolItem = new ToolItem(toolBar, SWT.PUSH);
deleteToolItem.setImage(imageManager.getDeleteFileIcon());
deleteToolItem.setText("Delete");
deleteToolItem.setToolTipText("Delete a file");
deleteToolItem.setEnabled(false);
deleteToolItem.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
// FIXME
}
});
item = new ToolItem(toolBar, SWT.SEPARATOR);
item = new ToolItem(toolBar, SWT.PUSH);
item.setImage(imageManager.getSaveImageIcon());
item.setText("Save");
item.setToolTipText("Save disk image");
item.setEnabled(false);
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
// not available yet
}
});
toolBar.pack();
}
/**
* Change the current format and refresh the display.
*/
protected void changeCurrentFormat(int newFormat) {
TreeItem selection = directoryTree.getSelection()[0];
Object data = selection.getData();
List fileList = null;
if (data instanceof FileEntry) {
FileEntry directory = (FileEntry) data;
fileList = directory.getFiles();
} else if (data instanceof FormattedDisk) {
FormattedDisk disk = (FormattedDisk) data;
fileList = disk.getFiles();
}
formatChanged = (currentFormat != newFormat);
if (formatChanged || !fileList.equals(currentFileList)) {
preserveColumnWidths(); // must be done before assigning newFormat
currentFormat = newFormat;
fillFileTable(fileList);
}
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.ui.swt;
import java.util.Iterator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.FormattedDisk.DiskInformation;
/**
* Build the Disk Info tab for the Disk Window.
* <p>
* Date created: Nov 17, 2002 9:15:29 PM
* @author: Rob Greene
*/
public class DiskInfoTab {
/**
* Create the DISK INFO tab.
*/
public DiskInfoTab(CTabFolder tabFolder, FormattedDisk disk) {
CTabItem ctabitem = new CTabItem(tabFolder, SWT.NULL);
ctabitem.setText("Disk Info");
Table table = new Table(tabFolder, SWT.FULL_SELECTION);
ctabitem.setControl(table);
table.setHeaderVisible(true);
TableColumn column = new TableColumn(table, SWT.LEFT);
column.setResizable(true);
column.setText("Label");
column.setWidth(200);
column = new TableColumn(table, SWT.LEFT);
column.setResizable(true);
column.setText("Value");
column.setWidth(400);
Iterator iterator = disk.getDiskInformation().iterator();
TableItem item = null;
while (iterator.hasNext()) {
DiskInformation diskinfo = (DiskInformation) iterator.next();
item = new TableItem(table, SWT.NULL);
item.setText(new String[] { diskinfo.getLabel(), diskinfo.getValue() });
}
}
/**
* Dispose of resources.
*/
public void dispose() {
}
}

View File

@ -0,0 +1,351 @@
/*
* 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.ui.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.FormattedDisk.DiskUsage;
/**
* Build the Disk Map tab for the Disk Window.
* <p>
* Date created: Nov 17, 2002 9:27:44 PM
* @author: Rob Greene
*/
public class DiskMapTab {
private FormattedDisk disk;
// used locally - not shared between windows; hopefully will
// not be a resource drain!
private Color freeFill;
private Color usedFill;
private Color black;
private Color gray;
/**
* Construct the DiskMapTab.
*/
public DiskMapTab(CTabFolder tabFolder, FormattedDisk disk) {
this.disk = disk;
// these items are reused; need to dispose of them when done!
freeFill = new Color(tabFolder.getDisplay(), 100,200,100);
usedFill = new Color(tabFolder.getDisplay(), 200,100,100);
black = new Color(tabFolder.getDisplay(), 0,0,0);
gray = new Color(tabFolder.getDisplay(), 50,50,50);
createDiskMapTab(tabFolder);
}
/**
* Create the DISK MAP tab.
*/
protected void createDiskMapTab(CTabFolder tabFolder) {
CTabItem item = new CTabItem(tabFolder, SWT.NULL);
item.setText("Disk Map");
Canvas canvas = new Canvas(tabFolder, SWT.NULL);
GridLayout grid = new GridLayout(2, false);
canvas.setLayout(grid);
item.setControl(canvas);
String[] labels = disk.getBitmapLabels();
// ROW #1 - title
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 2;
data.heightHint = 20;
Label title = new Label(canvas, SWT.LEFT);
StringBuffer buf = new StringBuffer();
if (labels.length == 1) {
buf.append("This disk is organized by the ");
buf.append(labels[0].toLowerCase());
buf.append(". Therefore, no organization has been forced on the ");
buf.append(labels[0].toLowerCase());
buf.append(" layout.");
} else {
buf.append("This disk is organized by ");
for (int i=0; i<labels.length; i++) {
if (i > 0) buf.append(" and ");
buf.append(labels[i].toLowerCase());
}
buf.append(", and this implies a rigid layout to the disk. ");
buf.append("This will be reflected in the disk map.");
}
title.setText(buf.toString());
title.setLayoutData(data);
// ROW #2 - blank and horizontal ruler
data = new GridData();
data.heightHint = 20;
data.widthHint = 20;
Composite blank = new Composite(canvas, SWT.NULL);
blank.setLayoutData(data);
data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
data.heightHint = 20;
Canvas ruler = new Canvas(canvas, SWT.NULL);
ruler.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
paintHorizontalRuler(event);
}
});
ruler.setLayoutData(data);
// ROW #3 - vertical ruler and map
data = new GridData(GridData.VERTICAL_ALIGN_FILL);
data.widthHint = 20;
ruler = new Canvas(canvas, SWT.NULL);
ruler.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
paintVerticalRuler(event);
}
});
ruler.setLayoutData(data);
data = new GridData(GridData.FILL_BOTH);
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
Canvas map = new Canvas(canvas, SWT.BORDER);
map.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
paintMap(event);
}
});
map.setLayoutData(data);
// ROW #5
data = new GridData(GridData.FILL_HORIZONTAL);
data.heightHint = 20;
data.horizontalSpan = 2;
Canvas legend = new Canvas(canvas, SWT.NULL);
legend.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
paintLegend(event);
}
});
legend.setLayoutData(data);
}
/**
* Dispose of resources.
*/
public void dispose() {
freeFill.dispose();
usedFill.dispose();
black.dispose();
gray.dispose();
}
/**
* Handle paint requests for horizontal ruler.
*/
private void paintHorizontalRuler(PaintEvent event) {
String label = (disk.getBitmapLabels()[0] + "s").toUpperCase();
Canvas canvas = (Canvas) event.widget;
Rectangle area = canvas.getClientArea();
event.gc.drawLine(area.x, area.y + area.height/2, area.x + area.width, area.y + area.height/2);
Point size = event.gc.textExtent(label);
event.gc.drawString(label, area.x + area.width/2 - size.x, area.y + area.height/2 - size.y/2);
}
/**
* Handle paint requests for vertical ruler.
*/
private void paintVerticalRuler(PaintEvent event) {
String label = (disk.getBitmapLabels()[0] + "s").toUpperCase();
if (disk.getBitmapLabels().length == 2) {
label = (disk.getBitmapLabels()[1] + "s").toUpperCase();
}
StringBuffer buf = new StringBuffer();
for (int i=0; i<label.length(); i++) {
if (i>0) buf.append("\n");
buf.append(label.charAt(i));
}
label = buf.toString();
Canvas canvas = (Canvas) event.widget;
Rectangle area = canvas.getClientArea();
event.gc.drawLine(area.x + area.width/2, area.y, area.x + area.width/2, area.y + area.height);
Point size = event.gc.textExtent(label);
event.gc.drawText(label, area.x + area.width/2 - size.x/2, area.y + area.height/2 - size.y/2);
}
/**
* Handle paint requests for disk map.
*/
private void paintMap(PaintEvent event) {
if (disk.getDiskUsage() == null) {
paintNoMap(event);
} else if (disk.getBitmapDimensions() == null) {
paintBlockMap(event);
} else {
paintSectorMap(event);
}
}
/**
* Handle paint requests for legend.
*/
private void paintLegend(PaintEvent event) {
Color background = event.gc.getBackground();
Canvas canvas = (Canvas) event.widget;
int height = event.gc.getFontMetrics().getHeight();
String freeText = " = Free";
String usedText = " = Used";
int padding = 50; // space between items
int totalWidth =
(height + 5) * 2 // free/used box
+ event.gc.textExtent(freeText).x
+ event.gc.textExtent(usedText).x;
int offset = canvas.getClientArea().width / 2 - totalWidth;
Rectangle box = new Rectangle(offset, 0, height, height);
drawBox(box, event.gc, freeFill, black, gray);
offset+= height;
event.gc.setBackground(background);
event.gc.drawText(freeText, offset, 0);
offset+= event.gc.textExtent(freeText).x;
offset+= padding;
box = new Rectangle(offset, 0, height, height);
drawBox(box, event.gc, usedFill, black, gray);
offset+= height;
event.gc.setBackground(background);
event.gc.drawText(" = Used", offset, 0);
}
/**
* Display message to user regarding no disk map being available.
*/
private void paintNoMap(PaintEvent event) {
Canvas canvas = (Canvas) event.widget;
Rectangle bounds = canvas.getClientArea();
event.gc.drawString("A disk map is unavailable.", 0, 0);
}
/**
* Paint a track/sector map.
*/
private void paintSectorMap(PaintEvent event) {
int[] dimensions = disk.getBitmapDimensions();
int ydim = dimensions[1];
int xdim = dimensions[0];
paintDiskMap(xdim, ydim, event);
}
/**
* Paint a block map.
*/
private void paintBlockMap(PaintEvent event) {
Canvas canvas = (Canvas) event.widget;
Rectangle area = canvas.getClientArea();
double blocks = disk.getBitmapLength();
double width = area.width;
double height = area.height;
double factor = Math.sqrt(blocks / (width * height));
int xdim = (int) (width * factor + 0.5);
int ydim = (int) (height * factor + 0.5);
if (xdim * ydim < blocks) {
xdim++;
}
if (xdim * ydim < blocks) {
ydim++;
}
paintDiskMap(xdim, ydim, event);
}
/**
* Paint a map with the given dimensions.
*/
private void paintDiskMap(int xdim, int ydim, PaintEvent event) {
int bitmapLength = disk.getBitmapLength();
Canvas canvas = (Canvas) event.widget;
Rectangle area = canvas.getClientArea();
area.width-= 2;
area.height-= 2;
int[] ypos = new int[ydim + 1];
for (int i=0; i<ydim; i++) {
ypos[i] = (i * area.height) / ydim + 1;
}
ypos[ydim] = area.height;
int[] xpos = new int[xdim + 1];
for (int i=0; i<xdim; i++) {
xpos[i] = (i * area.width) / xdim + 1;
}
xpos[xdim] = area.width;
Image image = new Image(canvas.getDisplay(), area);
GC gc = new GC(image);
int x = 0;
int y = 0;
DiskUsage usage = disk.getDiskUsage();
for (x=0; x<xdim && usage.hasNext(); x++) {
for (y=0; y<ydim && usage.hasNext(); y++) {
usage.next();
boolean free = usage.isFree();
Rectangle box = new Rectangle(xpos[x], ypos[y],
xpos[x+1]-xpos[x], ypos[y+1]-ypos[y]);
drawBox(box, gc, free ? freeFill : usedFill, black, gray);
}
}
event.gc.drawImage(image, 0, 0);
gc.dispose();
image.dispose();
}
/**
* Draw a box on the screen. The shadowed box is only drawn if there is
* enough space within the box; otherwise, the box is just filled in with
* the fill color. Additionally, drawBox ensures that a square is drawn.
*/
protected void drawBox(Rectangle box, GC gc, Color fill, Color outline, Color shadow) {
if (box.width >= 10 && box.height >= 10) {
// square the rectangle shape:
int size = Math.min(box.height, box.width);
box.height = size + ((box.height - size) / 2);
box.width = size + ((box.width - size) / 2);
// offset internal box:
box.x+= 2;
box.y+= 2;
box.width-= 5;
box.height-= 5;
// draw!
gc.setBackground(shadow);
gc.fillRectangle(box);
box.x-= 2;
box.y-= 2;
gc.setBackground(fill);
gc.fillRectangle(box);
gc.setForeground(outline);
gc.drawRectangle(box);
} else {
// just fill:
gc.setBackground(fill);
gc.fillRectangle(box);
}
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.ui.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Shell;
import com.webcodepro.applecommander.storage.FormattedDisk;
/**
* Displays disk information on the screen.
* <p>
* Date created: Oct 12, 2002 3:28:41 PM
* @author: Rob Greene
*/
public class DiskWindow {
private Shell parentShell;
private ImageManager imageManager;
private Shell shell;
private FormattedDisk disk;
private DiskInfoTab diskInfoTab;
private DiskMapTab diskMapTab;
private DiskExplorerTab diskExplorerTab;
/**
* Construct the disk window.
*/
public DiskWindow(Shell parentShell, FormattedDisk disk, ImageManager imageManager) {
this.parentShell = shell;
this.disk = disk;
this.imageManager = imageManager;
}
/**
* Setup the Disk window and display (open) it.
*/
public void open() {
shell = new Shell(parentShell, SWT.SHELL_TRIM);
shell.setLayout(new FillLayout());
shell.setImage(imageManager.getDiskIcon());
shell.setText("AppleCommander - " + disk.getFilename());
shell.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
dispose(event);
}
});
CTabFolder tabFolder = new CTabFolder(shell, SWT.BOTTOM);
diskExplorerTab = new DiskExplorerTab(tabFolder, disk, imageManager);
diskMapTab = new DiskMapTab(tabFolder, disk);
diskInfoTab = new DiskInfoTab(tabFolder, disk);
tabFolder.setSelection(tabFolder.getItems()[0]);
shell.open();
}
/**
* Dispose of all shared resources.
*/
private void dispose(DisposeEvent event) {
diskMapTab.dispose();
diskInfoTab.dispose();
disk = null;
diskMapTab = null;
diskInfoTab = null;
System.gc();
}
}

View File

@ -0,0 +1,106 @@
/*
* 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.ui.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
/**
* Listens to widgetSelected() events on SWT.DROP_DOWN type ToolItems
* and opens/closes a menu when appropriate.
* Code taken and modified from SWT examples to be more generic.
* <p>
* Date created: Nov 2, 2002 8:25:11 PM
* @author: Rob Greene
*/
public class DropDownSelectionListener extends SelectionAdapter {
private Menu menu = null;
private boolean visible = false;
/**
* Construct the DropDownSelectionListener with the specific menu to be used.
*/
public DropDownSelectionListener(Menu menu) {
this.menu = menu;
MenuItem[] menuItems = menu.getItems();
for (int i=0; i<menuItems.length; i++) {
/*
* Add a menu selection listener so that the menu is hidden
* when the user selects an item from the drop down menu.
*/
menuItems[i].addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
setMenuVisible(false);
}
});
}
}
/**
* Handle selection events.
*/
public void widgetSelected(SelectionEvent event) {
/**
* A selection event will be fired when a drop down tool
* item is selected in the main area and in the drop
* down arrow. Examine the event detail to determine
* where the widget was selected.
*/
if (event.detail == SWT.ARROW) {
/*
* The drop down arrow was selected.
*/
if (visible) {
// Hide the menu to give the Arrow the appearance of being a toggle button.
setMenuVisible(false);
} else {
// Position the menu below and vertically aligned with the the drop down tool button.
final ToolItem toolItem = (ToolItem) event.widget;
final ToolBar toolBar = toolItem.getParent();
Rectangle toolItemBounds = toolItem.getBounds();
Point point = toolBar.toDisplay(new Point(toolItemBounds.x, toolItemBounds.y));
menu.setLocation(point.x, point.y + toolItemBounds.height);
setMenuVisible(true);
}
} else {
/*
* Main area of drop down tool item selected.
* An application would invoke the code to perform the action for the tool item.
*/
}
}
/**
* Set menu visibility and track state.
*/
private void setMenuVisible(boolean visible) {
menu.setVisible(visible);
this.visible = visible;
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.ui.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
/**
* Set locations and file names for the export.
* <p>
* Date created: Nov 8, 2002 11:18:47 PM
* @author: Rob Greene
*/
public class ExportFileDestinationPane extends WizardPane {
private Composite parent;
private Object layoutData;
private Composite control;
private ExportWizard wizard;
private Text directoryText;
/**
* Constructor for ExportFileDestinationPane.
*/
public ExportFileDestinationPane(Composite parent, ExportWizard exportWizard, Object layoutData) {
super();
this.parent = parent;
this.wizard = exportWizard;
this.layoutData = layoutData;
}
/**
* This is the last pane in the wizard, so a null is returned to indicate no
* more pages.
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
*/
public WizardPane getNextPane() {
return null;
}
/**
* Create and display the wizard pane.
* @see com.webcodepro.applecommander.gui.WizardPane#open()
*/
public void open() {
control = new Composite(parent, SWT.NULL);
control.setLayoutData(layoutData);
wizard.enableNextButton(false);
wizard.enableFinishButton(true);
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.justify = true;
layout.marginBottom = 5;
layout.marginLeft = 5;
layout.marginRight = 5;
layout.marginTop = 5;
layout.spacing = 3;
control.setLayout(layout);
Label label = new Label(control, SWT.WRAP);
label.setText("Please indicate the destination for the exported files:");
directoryText = new Text(control, SWT.WRAP | SWT.BORDER);
if (wizard.getDirectory() != null) directoryText.setText(wizard.getDirectory());
directoryText.setLayoutData(new RowData(parent.getSize().x - 30, -1));
directoryText.setBackground(new Color(control.getDisplay(), 255,255,255));
directoryText.setFocus();
directoryText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent event) {
Text text = (Text) event.getSource();
wizard.setDirectory(text.getText());
}
});
Button button = new Button(control, SWT.PUSH);
button.setText("Browse...");
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
DirectoryDialog directoryDialog = new DirectoryDialog(control.getShell());
directoryDialog.setFilterPath(directoryText.getText());
directoryDialog.setMessage(
"Please choose the directory to which exported files will be written");
String directory = directoryDialog.open();
if (directory != null) {
directoryText.setText(directory);
}
}
});
}
/**
* Dispose of any resources.
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
*/
public void dispose() {
directoryText.dispose();
control.dispose();
control = null;
}
}

View File

@ -0,0 +1,160 @@
/*
* 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.ui.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import com.webcodepro.applecommander.storage.AppleWorksWordProcessorFileFilter;
import com.webcodepro.applecommander.storage.ApplesoftFileFilter;
import com.webcodepro.applecommander.storage.BinaryFileFilter;
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
import com.webcodepro.applecommander.storage.IntegerBasicFileFilter;
import com.webcodepro.applecommander.storage.TextFileFilter;
/**
* Provides the wizard pane which gets the export filter.
* <p>
* Date created: Nov 7, 2002 8:43:27 PM
* @author: Rob Greene
*/
public class ExportFileStartPane extends WizardPane {
private Composite parent;
private Object layoutData;
private Composite control;
private ExportWizard wizard;
private GraphicsFileFilter graphicsFileFilter = new GraphicsFileFilter();
private AppleWorksWordProcessorFileFilter awpFilter = new AppleWorksWordProcessorFileFilter();
/**
* Constructor for ExportFileStartPane.
*/
public ExportFileStartPane(Composite parent, ExportWizard exportWizard, Object layoutData) {
super();
this.parent = parent;
this.wizard = exportWizard;
this.layoutData = layoutData;
}
/**
* Open up and configure the wizard pane.
*/
public void open() {
control = new Composite(parent, SWT.NULL);
control.setLayoutData(layoutData);
wizard.enableNextButton(true);
wizard.enableFinishButton(false);
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.justify = true;
layout.marginBottom = 5;
layout.marginLeft = 5;
layout.marginRight = 5;
layout.marginTop = 5;
layout.spacing = 3;
control.setLayout(layout);
Label label = new Label(control, SWT.WRAP);
label.setText("Please choose the type of file that is being exported.");
RowLayout subpanelLayout = new RowLayout(SWT.VERTICAL);
subpanelLayout.justify = true;
subpanelLayout.spacing = 3;
Composite buttonSubpanel = new Composite(control, SWT.NULL);
buttonSubpanel.setLayout(subpanelLayout);
Button button = new Button(buttonSubpanel, SWT.RADIO);
button.setText("Raw disk data");
button.setSelection(wizard.getFileFilter() == null);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizard.setFileFilter(null);
}
});
button = new Button(buttonSubpanel, SWT.RADIO);
button.setText("Binary file");
button.setSelection(wizard.getFileFilter() instanceof BinaryFileFilter);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizard.setFileFilter(new BinaryFileFilter());
}
});
button = new Button(buttonSubpanel, SWT.RADIO);
button.setText("ASCII text file");
button.setSelection(wizard.getFileFilter() instanceof TextFileFilter);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizard.setFileFilter(new TextFileFilter());
}
});
button = new Button(buttonSubpanel, SWT.RADIO);
button.setText("Applesoft BASIC file");
button.setSelection(wizard.getFileFilter() instanceof ApplesoftFileFilter);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizard.setFileFilter(new ApplesoftFileFilter());
}
});
button = new Button(buttonSubpanel, SWT.RADIO);
button.setText("Integer BASIC file");
button.setSelection(wizard.getFileFilter() instanceof IntegerBasicFileFilter);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizard.setFileFilter(new IntegerBasicFileFilter());
}
});
button = new Button(buttonSubpanel, SWT.RADIO);
button.setText("AppleWorks Word Processor file");
button.setSelection(wizard.getFileFilter() instanceof AppleWorksWordProcessorFileFilter);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizard.setFileFilter(new AppleWorksWordProcessorFileFilter());
}
});
button = new Button(buttonSubpanel, SWT.RADIO);
button.setText("Graphic file...");
button.setEnabled(graphicsFileFilter.isCodecAvailable());
button.setSelection(wizard.getFileFilter() instanceof GraphicsFileFilter);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizard.setFileFilter(graphicsFileFilter);
}
});
}
/**
* Get the next pane. A null return indicates the end of the wizard.
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
*/
public WizardPane getNextPane() {
if (wizard.getFileFilter() instanceof GraphicsFileFilter) {
return new ExportGraphicsTypePane(parent, wizard, layoutData);
} else if (wizard.getFileFilter() instanceof AppleWorksWordProcessorFileFilter) {
return new AppleWorksWordProcessorPane(parent, wizard, layoutData);
}
return new ExportFileDestinationPane(parent, wizard, layoutData);
}
/**
* Dispose of resources.
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
*/
public void dispose() {
control.dispose();
control = null;
}
}

View File

@ -0,0 +1,146 @@
/*
* 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.ui.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
/**
* Choose graphics options for file export.
* <p>
* Date created: Nov 7, 2002 10:25:43 PM
* @author: Rob Greene
*/
public class ExportGraphicsTypePane extends WizardPane {
private Composite parent;
private Object layoutData;
private Composite control;
private ExportWizard wizard;
/**
* Constructor for ExportGraphicsTypePane.
*/
public ExportGraphicsTypePane(Composite parent, ExportWizard exportWizard, Object layoutData) {
super();
this.parent = parent;
this.wizard = exportWizard;
this.layoutData = layoutData;
}
/**
* Determine the next wizard pane and return an instance.
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
*/
public WizardPane getNextPane() {
return new ExportFileDestinationPane(parent, wizard, layoutData);
}
/**
* Open up and configure the wizard pane.
* @see com.webcodepro.applecommander.gui.WizardPane#open()
*/
public void open() {
wizard.enableFinishButton(false);
wizard.enableNextButton(true);
control = new Composite(parent, SWT.NULL);
control.setLayoutData(layoutData);
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.justify = true;
layout.marginBottom = 5;
layout.marginLeft = 5;
layout.marginRight = 5;
layout.marginTop = 5;
layout.spacing = 3;
control.setLayout(layout);
Label label = new Label(control, SWT.WRAP);
label.setText("Please choose the appropriate graphics mode:");
RowLayout subpanelLayout = new RowLayout(SWT.VERTICAL);
subpanelLayout.justify = true;
subpanelLayout.spacing = 3;
Composite graphicsModeGroup = new Composite(control, SWT.NULL);
graphicsModeGroup.setLayout(subpanelLayout);
Button button = new Button(graphicsModeGroup, SWT.RADIO);
button.setText("Hires black and white (280x192)");
button.setSelection(getGraphicsFilter().isHiresBlackAndWhiteMode());
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_HGR_BLACK_AND_WHITE);
}
});
button = new Button(graphicsModeGroup, SWT.RADIO);
button.setText("Hires color (280x192)");
button.setSelection(getGraphicsFilter().isHiresColorMode());
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_HGR_COLOR);
}
});
button = new Button(graphicsModeGroup, SWT.RADIO);
button.setText("Double hires black and white (560x384)");
button.setSelection(getGraphicsFilter().isDoubleHiresBlackAndWhiteMode());
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_DHR_BLACK_AND_WHITE);
}
});
button = new Button(graphicsModeGroup, SWT.RADIO);
button.setText("Double hires color (560x384)");
button.setSelection(getGraphicsFilter().isDoubleHiresColorMode());
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_DHR_COLOR);
}
});
label = new Label(control, SWT.WRAP);
label.setText("Please choose the appropriate file format to save image as:");
Composite graphicsFormatGroup = new Composite(control, SWT.NULL);
graphicsFormatGroup.setLayout(subpanelLayout);
String[] formats = getGraphicsFilter().getFileExtensions();
for (int i=0; i<formats.length; i++) {
button = new Button(graphicsFormatGroup, SWT.RADIO);
button.setText(formats[i]);
button.setSelection(formats[i].equals(getGraphicsFilter().getExtension()));
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Button source = (Button) e.getSource();
getGraphicsFilter().setExtension(source.getText());
}
});
}
}
/**
* Dispose of widgets.
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
*/
public void dispose() {
control.dispose();
control = null;
}
/**
* Get the graphics file filter.
*/
protected GraphicsFileFilter getGraphicsFilter() {
return (GraphicsFileFilter) wizard.getFileFilter();
}
}

View File

@ -0,0 +1,225 @@
/*
* 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.ui.swt;
import java.util.Stack;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import com.webcodepro.applecommander.storage.FileFilter;
import com.webcodepro.applecommander.storage.FormattedDisk;
/**
* File export wizard.
* <p>
* Date created: Nov 7, 2002 9:22:35 PM
* @author: Rob Greene
*/
public class ExportWizard {
private FormattedDisk disk;
private Shell parent;
private Shell dialog;
private Image logo; // managed by SwtAppleCommander
private Stack wizardPanes = new Stack();
private FileFilter fileFilter;
private String directory;
private boolean wizardCompleted;
private Button backButton;
private Button nextButton;
private Button finishButton;
private Composite contentPane;
/**
* Constructor for ExportWizard.
*/
public ExportWizard(Shell parent, Image logo, FormattedDisk disk) {
super();
this.parent = parent;
this.logo = logo;
this.disk = disk;
}
/**
* Create the dialog.
*/
private void createDialog() {
dialog = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
dialog.setText("Export Wizard");
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.justify = true;
layout.marginBottom = 5;
layout.marginLeft = 5;
layout.marginRight = 5;
layout.marginTop = 5;
layout.spacing = 3;
dialog.setLayout(layout);
// Wizard logo
RowData rowData = new RowData();
rowData.width = logo.getImageData().width;
rowData.height = logo.getImageData().height;
ImageCanvas imageCanvas = new ImageCanvas(dialog, SWT.BORDER, logo, rowData);
// Starting pane
rowData = new RowData();
rowData.width = logo.getImageData().width;
contentPane = new Composite(dialog, SWT.BORDER);
contentPane.setLayoutData(rowData);
contentPane.setLayout(new FillLayout());
// Bottom row of buttons
Composite composite = new Composite(dialog, SWT.NONE);
composite.setLayoutData(rowData);
composite.setLayout(new FillLayout(SWT.HORIZONTAL));
Button button = new Button(composite, SWT.PUSH);
button.setText("Cancel");
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizardCompleted = false;
dialog.close();
}
});
backButton = new Button(composite, SWT.PUSH);
backButton.setEnabled(false);
backButton.setText("< Back");
backButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
WizardPane current = (WizardPane) wizardPanes.pop();
WizardPane previous = (WizardPane) wizardPanes.peek();
backButton.setEnabled(wizardPanes.size() > 1);
current.dispose();
previous.open();
dialog.pack();
}
});
nextButton = new Button(composite, SWT.PUSH);
nextButton.setText("Next >");
nextButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
WizardPane current = (WizardPane) wizardPanes.peek();
WizardPane next = current.getNextPane();
wizardPanes.add(next);
backButton.setEnabled(wizardPanes.size() > 1);
current.dispose();
next.open();
dialog.pack();
}
});
finishButton = new Button(composite, SWT.PUSH);
finishButton.setEnabled(false);
finishButton.setText("Finish");
finishButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
wizardCompleted = true;
dialog.close();
}
});
WizardPane wizardPane = new ExportFileStartPane(contentPane, this, null);
wizardPanes.add(wizardPane);
wizardPane.open();
dialog.pack();
}
/**
* Open and display the dialog.
*/
public void open() {
createDialog();
dialog.open();
Display display = dialog.getDisplay();
while (!dialog.isDisposed()) {
if (!display.readAndDispatch()) display.sleep ();
}
}
/**
* Dispose of all panels and resources.
*/
public void dispose() {
while (!wizardPanes.empty()) {
WizardPane pane = (WizardPane) wizardPanes.pop();
pane.dispose();
pane = null;
}
dialog.dispose();
backButton.dispose();
nextButton.dispose();
finishButton.dispose();
contentPane.dispose();
}
/**
* Get the FileFilter.
*/
public FileFilter getFileFilter() {
return fileFilter;
}
/**
* Set the FileFilter.
*/
public void setFileFilter(FileFilter fileFilter) {
this.fileFilter = fileFilter;
}
/**
* Indicates if the wizard was completed.
*/
public boolean isWizardCompleted() {
return wizardCompleted;
}
/**
* Enable/disable the next button.
*/
public void enableNextButton(boolean state) {
nextButton.setEnabled(state);
}
/**
* Enable/disable the finish button.
*/
public void enableFinishButton(boolean state) {
finishButton.setEnabled(state);
}
/**
* Get the disk that is being worked on.
*/
public FormattedDisk getDisk() {
return disk;
}
/**
* Returns the directory.
* @return String
*/
public String getDirectory() {
return directory;
}
/**
* Sets the directory.
* @param directory The directory to set
*/
public void setDirectory(String directory) {
this.directory = directory;
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.ui.swt;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
/**
* Displays an image.
* <p>
* Date created: Nov 7, 2002 9:28:21 PM
* @author: Rob Greene
*/
public class ImageCanvas extends Canvas implements PaintListener {
private Image image;
/**
* Constructor for ImageCanvas.
*/
public ImageCanvas(Composite parent, int style, Image image, Object layoutData) {
super(parent, style);
this.image = image;
setLayoutData(layoutData);
setSize(image.getImageData().width, image.getImageData().height);
addPaintListener(this);
}
/**
* Handle paint events.
*/
public void paintControl(PaintEvent event) {
event.gc.drawImage(
image,
0,
0,
image.getImageData().width,
image.getImageData().height,
0,
0,
image.getImageData().width,
image.getImageData().height);
}
}

View File

@ -0,0 +1,214 @@
/*
* 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.ui.swt;
import java.io.InputStream;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
/**
* Manage image for the SWT-base AppleCommander.
* <p>
* Date created: Nov 17, 2002 6:53:08 PM
* @author: Rob Greene
*/
public class ImageManager {
private Image logoImage;
private Image diskIcon;
private Image standardFileViewIcon;
private Image nativeFileViewIcon;
private Image detailFileViewIcon;
private Image importFileIcon;
private Image exportFileIcon;
private Image saveImageIcon;
private Image deleteFileIcon;
private Image deletedFilesIcon;
private Image exportWizardLogo;
private Image openDiskIcon;
private Image newDiskIcon;
private Image aboutIcon;
/**
* Construct the ImageManager.
*/
protected ImageManager(Display display) {
diskIcon = createImage(display, "diskicon.gif");
standardFileViewIcon = createImage(display, "standardfileview.gif");
nativeFileViewIcon = createImage(display, "nativefileview.gif");
detailFileViewIcon = createImage(display, "detailfileview.gif");
importFileIcon = createImage(display, "importfile.gif");
exportFileIcon = createImage(display, "exportfile.gif");
saveImageIcon = createImage(display, "saveimage.gif");
deleteFileIcon = createImage(display, "deletefile.gif");
deletedFilesIcon = createImage(display, "deletedfiles.gif");
exportWizardLogo = createImage(display, "ExportWizardLogo.gif");
logoImage = createImage(display, "AppleCommanderLogo.gif");
openDiskIcon = createImage(display, "opendisk.gif");
newDiskIcon = createImage(display, "newdisk.gif");
aboutIcon = createImage(display, "about.gif");
}
/**
* Dispose of resources.
*/
public void dispose() {
diskIcon.dispose();
standardFileViewIcon.dispose();
nativeFileViewIcon.dispose();
detailFileViewIcon.dispose();
importFileIcon.dispose();
exportFileIcon.dispose();
saveImageIcon.dispose();
deleteFileIcon.dispose();
deletedFilesIcon.dispose();
logoImage.dispose();
exportWizardLogo.dispose();
openDiskIcon.dispose();
newDiskIcon.dispose();
aboutIcon.dispose();
}
/**
* Creates an image.
*/
private Image createImage(Display display, String path) {
try {
InputStream stream = getClass().getResourceAsStream(
"/com/webcodepro/applecommander/ui/images/" + path);
if (stream != null) {
Image image = new Image(display, stream);
stream.close();
return image;
}
} catch (Exception e) {
}
return null;
}
/**
* Returns the deletedFilesIcon.
* @return Image
*/
public Image getDeletedFilesIcon() {
return deletedFilesIcon;
}
/**
* Returns the deleteFileIcon.
* @return Image
*/
public Image getDeleteFileIcon() {
return deleteFileIcon;
}
/**
* Returns the detailFileViewIcon.
* @return Image
*/
public Image getDetailFileViewIcon() {
return detailFileViewIcon;
}
/**
* Returns the diskIcon.
* @return Image
*/
public Image getDiskIcon() {
return diskIcon;
}
/**
* Returns the exportFileIcon.
* @return Image
*/
public Image getExportFileIcon() {
return exportFileIcon;
}
/**
* Returns the exportWizardLogo.
* @return Image
*/
public Image getExportWizardLogo() {
return exportWizardLogo;
}
/**
* Returns the importFileIcon.
* @return Image
*/
public Image getImportFileIcon() {
return importFileIcon;
}
/**
* Returns the logoImage.
* @return Image
*/
public Image getLogoImage() {
return logoImage;
}
/**
* Returns the nativeFileViewIcon.
* @return Image
*/
public Image getNativeFileViewIcon() {
return nativeFileViewIcon;
}
/**
* Returns the saveImageIcon.
* @return Image
*/
public Image getSaveImageIcon() {
return saveImageIcon;
}
/**
* Returns the standardFileViewIcon.
* @return Image
*/
public Image getStandardFileViewIcon() {
return standardFileViewIcon;
}
/**
* Returns the aboutIcon.
* @return Image
*/
public Image getAboutIcon() {
return aboutIcon;
}
/**
* Returns the newDiskIcon.
* @return Image
*/
public Image getNewDiskIcon() {
return newDiskIcon;
}
/**
* Returns the openDiskIcon.
* @return Image
*/
public Image getOpenDiskIcon() {
return openDiskIcon;
}
}

View File

@ -0,0 +1,229 @@
/*
* 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.ui.swt;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.Disk.FilenameFilter;
import com.webcodepro.applecommander.ui.AppleCommander;
import com.webcodepro.applecommander.ui.UserPreferences;
/**
* Main class for the SwtAppleCommander interface.
* <p>
* Date created: Oct 7, 2002 9:43:37 PM
* @author: Rob Greene
*/
public class SwtAppleCommander {
private Display display;
private Shell shell;
private ToolBar toolBar;
private UserPreferences userPreferences = UserPreferences.getInstance();
private static ImageManager imageManager;
/**
* Launch SwtAppleCommander.
*/
public static void main(String[] args) {
Display display = new Display();
imageManager = new ImageManager(display);
SwtAppleCommander application = new SwtAppleCommander();
Shell shell = application.open(display);
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
UserPreferences.getInstance().save();
}
/**
* Constructor for SwtAppleCommander.
*/
public SwtAppleCommander() {
super();
}
/**
* Opens the main program.
*/
private Shell open(Display display) {
this.display = display;
display.setAppName("AppleCommander");
shell = new Shell(display, SWT.BORDER | SWT.CLOSE | SWT.MIN | SWT.TITLE);
shell.setText("AppleCommander");
shell.setImage(imageManager.getDiskIcon());
shell.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
dispose(event);
}
});
GridLayout gridLayout = new GridLayout();
gridLayout.marginHeight = 5;
gridLayout.marginWidth = 5;
shell.setLayout(gridLayout);
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.grabExcessHorizontalSpace = true;
createToolBar(shell, gridData);
gridData = new GridData();
Image logoImage = imageManager.getLogoImage();
gridData.widthHint = logoImage.getImageData().width;
gridData.heightHint = logoImage.getImageData().height;
ImageCanvas imageCanvas = new ImageCanvas(shell, SWT.BORDER, logoImage, gridData);
shell.pack();
shell.open();
return shell;
}
/**
* Dispose of all shared resources.
*/
private void dispose(DisposeEvent event) {
toolBar.dispose();
imageManager.dispose();
}
/**
* Exits the main program.
*/
private void exit() {
shell.close();
}
/**
* Open a file.
*/
private void openFile() {
FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
FilenameFilter[] fileFilters = Disk.getFilenameFilters();
String[] names = new String[fileFilters.length];
String[] extensions = new String[fileFilters.length];
for (int i=0; i<fileFilters.length; i++) {
names[i] = fileFilters[i].getNames();
extensions[i] = fileFilters[i].getExtensions();
}
fileDialog.setFilterNames(names);
fileDialog.setFilterExtensions(extensions);
fileDialog.setFilterPath(userPreferences.getDiskImageDirectory());
String fullpath = fileDialog.open();
if (fullpath != null) {
userPreferences.setDiskImageDirectory(fileDialog.getFilterPath());
try {
Disk disk = new Disk(fullpath);
FormattedDisk formattedDisk = disk.getFormattedDisk();
if (formattedDisk != null) {
DiskWindow window = new DiskWindow(shell, formattedDisk, imageManager);
window.open();
} else {
Shell finalShell = shell;
MessageBox box = new MessageBox(finalShell, SWT.ICON_ERROR | SWT.OK);
box.setText("Unrecognized Disk Format");
box.setMessage(
"Unable to load '" + fullpath + "'.\n\n"
+ "AppleCommander did not recognize the format\n"
+ "of that disk. Either this is a new format\n"
+ "or a protected disk.\n\n"
+ "Sorry!");
box.open();
}
} catch (IOException ignored) {
}
}
}
/**
* Creates the toolbar.
*/
private void createToolBar(Shell shell, Object layoutData) {
toolBar = new ToolBar(shell, SWT.FLAT);
if (layoutData != null) toolBar.setLayoutData(layoutData);
ToolItem item = new ToolItem(toolBar, SWT.PUSH);
item.setImage(imageManager.getOpenDiskIcon());
item.setText("Open...");
item.setToolTipText("Open a disk image");
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
openFile();
}
});
item = new ToolItem(toolBar, SWT.SEPARATOR);
item = new ToolItem(toolBar, SWT.PUSH);
item.setImage(imageManager.getNewDiskIcon());
item.setText("Create...");
item.setToolTipText("Create a disk image");
item.setEnabled(false);
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
// not available yet
}
});
item = new ToolItem(toolBar, SWT.SEPARATOR);
item = new ToolItem(toolBar, SWT.PUSH);
item.setImage(imageManager.getAboutIcon());
item.setText("About");
item.setToolTipText("About AppleCommander");
final Shell finalShell = shell;
item.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
MessageBox box = new MessageBox(finalShell, SWT.ICON_INFORMATION | SWT.OK);
box.setText("About AppleCommander");
box.setMessage(
"AppleCommander\n"
+ "Version " + AppleCommander.VERSION + "\n"
+ "Copyright (c) 2002\n\n"
+ "AppleCommander was created for the express\n"
+ "purpose of assisting those-who-remember.\n\n"
+ "I wish you many hours of vintage pleasure!\n"
+ "-Rob");
box.open();
}
});
item = new ToolItem(toolBar, SWT.SEPARATOR);
toolBar.pack();
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.ui.swt;
/**
* Represents a pane of a wizard.
* <p>
* Date created: Nov 7, 2002 8:40:44 PM
* @author: Rob Greene
*/
public abstract class WizardPane {
/**
* Constructor for WizardPane.
*/
public WizardPane() {
super();
}
/**
* Get the next WizardPane.
*/
public abstract WizardPane getNextPane();
/**
* Create and display the wizard pane.
*/
public abstract void open();
/**
* Dispose of any resources.
*/
public abstract void dispose();
}