commit ebf72c103e3cb75a66050fdc210e372af949d196 Author: Robert Greene Date: Sun Dec 1 02:21:00 2002 +0000 Version 1.1.1 is the initial GPL release of AppleCommander. diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..657091d --- /dev/null +++ b/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..4e62da2 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,2 @@ +bin +AppleCommander.preferences diff --git a/.project b/.project new file mode 100644 index 0000000..c9433a1 --- /dev/null +++ b/.project @@ -0,0 +1,18 @@ + + + AppleCommander + + + JUnit + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/HEADER b/HEADER new file mode 100644 index 0000000..04c1f40 --- /dev/null +++ b/HEADER @@ -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 + */ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..037b5fe --- /dev/null +++ b/LICENSE @@ -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 + diff --git a/build/manifest.mf b/build/manifest.mf new file mode 100644 index 0000000..7f627e3 --- /dev/null +++ b/build/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: swt.jar +Main-Class: com.webcodepro.applecommander.ui.AppleCommander diff --git a/imageSource/AppleCommanderLogo.mix b/imageSource/AppleCommanderLogo.mix new file mode 100644 index 0000000..8cff003 Binary files /dev/null and b/imageSource/AppleCommanderLogo.mix differ diff --git a/imageSource/ExportWizardLogo.mix b/imageSource/ExportWizardLogo.mix new file mode 100644 index 0000000..2372efd Binary files /dev/null and b/imageSource/ExportWizardLogo.mix differ diff --git a/src/com/webcodepro/applecommander/storage/AppleUtil.java b/src/com/webcodepro/applecommander/storage/AppleUtil.java new file mode 100644 index 0000000..a47f688 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/AppleUtil.java @@ -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. + *

+ * 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> 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(); + } +} diff --git a/src/com/webcodepro/applecommander/storage/AppleWorksWordProcessorFileFilter.java b/src/com/webcodepro/applecommander/storage/AppleWorksWordProcessorFileFilter.java new file mode 100644 index 0000000..5727e15 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/AppleWorksWordProcessorFileFilter.java @@ -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 + *

+ * 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(""); + } + 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(""); + } + 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(" "); + while (fileData[offset] == ' ') { + offset++; + length--; + printWriter.print(" "); + } + } 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("
"); + else printWriter.println(); + } + /** + * Process special coding of a text record. + */ + protected void handleSpecialCodesAsHtml(PrintWriter printWriter, byte ch) { + switch (ch) { + case 0x01: printWriter.print(""); + break; + case 0x02: printWriter.print(""); + break; + case 0x03: printWriter.print(""); + break; + case 0x04: printWriter.print(""); + break; + case 0x05: printWriter.print(""); + break; + case 0x06: printWriter.print(""); + break; + case 0x07: printWriter.print(""); + break; + case 0x08: printWriter.print(""); + break; + case 0x09: printWriter.print("[Page#]"); + break; + case 0x0b: printWriter.print(" "); + 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(""); + break; + case 0xdf: printWriter.println(""); + break; + case 0xe0: printWriter.println(""); + break; + case 0xe1: printWriter.println(""); + break; + case 0xee: for (int i=0; i"); + } + 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; + } +} \ No newline at end of file diff --git a/src/com/webcodepro/applecommander/storage/ApplesoftFileFilter.java b/src/com/webcodepro/applecommander/storage/ApplesoftFileFilter.java new file mode 100644 index 0000000..2c64a43 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/ApplesoftFileFilter.java @@ -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. + *

+ * Applesoft memory format:
+ * [Line]
+ * ... + * [Line]
+ *
+ * where is:
+ * [Next addr - $0000 is end of program] (word)
+ * [Line no] (word)
+ * [Tokens and/or characters]
+ * [End-of-line marker: $00 bytes] + *

+ * 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(""); + } else { + String tokenString = tokens[token]; + printWriter.print(tokenString); + } + } else { + char ch = (char)byt; + if (ch < 0x20) { + 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; + } +} diff --git a/src/com/webcodepro/applecommander/storage/BinaryFileFilter.java b/src/com/webcodepro/applecommander/storage/BinaryFileFilter.java new file mode 100644 index 0000000..c043df7 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/BinaryFileFilter.java @@ -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. + *

+ * 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; + } +} diff --git a/src/com/webcodepro/applecommander/storage/Disk.java b/src/com/webcodepro/applecommander/storage/Disk.java new file mode 100644 index 0000000..fbe134c --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/Disk.java @@ -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). + *

+ * 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); + } +} diff --git a/src/com/webcodepro/applecommander/storage/DiskHelper.java b/src/com/webcodepro/applecommander/storage/DiskHelper.java new file mode 100644 index 0000000..5fccdd8 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/DiskHelper.java @@ -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. + *

+ * 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(); + } +} diff --git a/src/com/webcodepro/applecommander/storage/DosFileEntry.java b/src/com/webcodepro/applecommander/storage/DosFileEntry.java new file mode 100644 index 0000000..a5ab69a --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/DosFileEntry.java @@ -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. + *

+ * 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= 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()); + } +} diff --git a/src/com/webcodepro/applecommander/storage/DosFormatDisk.java b/src/com/webcodepro/applecommander/storage/DosFormatDisk.java new file mode 100644 index 0000000..0ae6343 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/DosFormatDisk.java @@ -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. + *

+ * 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 + * 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(); +} diff --git a/src/com/webcodepro/applecommander/storage/FileEntryComparator.java b/src/com/webcodepro/applecommander/storage/FileEntryComparator.java new file mode 100644 index 0000000..639e0c0 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/FileEntryComparator.java @@ -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). + *

+ * 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 + * 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); +} diff --git a/src/com/webcodepro/applecommander/storage/FormattedDisk.java b/src/com/webcodepro/applecommander/storage/FormattedDisk.java new file mode 100644 index 0000000..ea370ec --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/FormattedDisk.java @@ -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). + *

+ * 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 + * Address for Apple2 HGR/DHR address is calculated from an observation of a pattern:
+ * line number bits: 87654321
+ * 87 are multipled by 0x0028
+ * 65 are multipled by 0x0100
+ * 4 is multiplied by 0x0080
+ * 321 are multipled by 0x0400 + *

+ * 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). + *

+ * 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. + *

+ * 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:
+ *

+	 * 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
+	 * 
+ * 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. + *

+ * From the Apple2 + * technical note: + *

+	 *                                          Repeated
+ * Binary
+ * Color aux1 main1 aux2 main2 Pattern
+ * Black 00 00 00 00 0000
+ * Magenta 08 11 22 44 0001
+ * Brown 44 08 11 22 0010
+ * Orange 4C 19 33 66 0011
+ * Dark Green 22 44 08 11 0100
+ * Grey1 2A 55 2A 55 0101
+ * Green 66 4C 19 33 0110
+ * Yellow 6E 5D 3B 77 0111
+ * Dark Blue 11 22 44 08 1000
+ * Violet 19 33 66 4C 1001
+ * Grey2 55 2A 55 2A 1010
+ * Pink 5D 3B 77 6E 1011
+ * Medium Blue 33 66 4C 19 1100
+ * Light Blue 3B 77 6E 5D 1101
+ * Aqua 77 6E 5D 3B 1110
+ * White 7F 7F 7F 7F 1111 + *
+ */ + 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(); + } +} diff --git a/src/com/webcodepro/applecommander/storage/IntegerBasicFileFilter.java b/src/com/webcodepro/applecommander/storage/IntegerBasicFileFilter.java new file mode 100644 index 0000000..fe32afe --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/IntegerBasicFileFilter.java @@ -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. + *

+ * On disk, it looks similar to Applesoft - first two bytes are length; rest of + * image is raw data for Integer Basic. + *

+ * [byte] length of line
+ * [word] line number
+ * [byte]* line data
+ * $01 end of line
+ * Repeat until end of program (line length of 0). + *

+ * Tokens are $00 - $7F, some are duplicated.
+ * $01 = end of line.
+ * $B0 - $B9 = signifies a number stored in a word.
+ *

+ * 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(""); + } 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; + } +} diff --git a/src/com/webcodepro/applecommander/storage/PascalFileEntry.java b/src/com/webcodepro/applecommander/storage/PascalFileEntry.java new file mode 100644 index 0000000..410aaf2 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/PascalFileEntry.java @@ -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. + *

+ * 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(); + } + +} diff --git a/src/com/webcodepro/applecommander/storage/PascalFormatDisk.java b/src/com/webcodepro/applecommander/storage/PascalFormatDisk.java new file mode 100644 index 0000000..20fa590 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/PascalFormatDisk.java @@ -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. + *

+ * 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 + * 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); + } +} diff --git a/src/com/webcodepro/applecommander/storage/ProdosCommonEntry.java b/src/com/webcodepro/applecommander/storage/ProdosCommonEntry.java new file mode 100644 index 0000000..cb61e53 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/ProdosCommonEntry.java @@ -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. + *

+ * 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); + } + +} diff --git a/src/com/webcodepro/applecommander/storage/ProdosFileEntry.java b/src/com/webcodepro/applecommander/storage/ProdosFileEntry.java new file mode 100644 index 0000000..dd2f799 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/ProdosFileEntry.java @@ -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. + *

+ * 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> 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. + *

+ * 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. + *

+ * 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 ? " " : + dateFormat.format(getLastModificationDate())); + list.add(getCreationDate() == null ? " " : + 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 ? " " : + dateFormat.format(getLastModificationDate())); + list.add(getCreationDate() == null ? " " : + 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(); + } +} diff --git a/src/com/webcodepro/applecommander/storage/ProdosFormatDisk.java b/src/com/webcodepro/applecommander/storage/ProdosFormatDisk.java new file mode 100644 index 0000000..8a04e0c --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/ProdosFormatDisk.java @@ -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. + *

+ * 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 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; + } +} diff --git a/src/com/webcodepro/applecommander/storage/ProdosSubdirectoryHeader.java b/src/com/webcodepro/applecommander/storage/ProdosSubdirectoryHeader.java new file mode 100644 index 0000000..39a3cd8 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/ProdosSubdirectoryHeader.java @@ -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. + *

+ * 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); + } +} diff --git a/src/com/webcodepro/applecommander/storage/ProdosVolumeDirectoryHeader.java b/src/com/webcodepro/applecommander/storage/ProdosVolumeDirectoryHeader.java new file mode 100644 index 0000000..7659b22 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/ProdosVolumeDirectoryHeader.java @@ -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. + *

+ * 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); + } +} diff --git a/src/com/webcodepro/applecommander/storage/RdosFileEntry.java b/src/com/webcodepro/applecommander/storage/RdosFileEntry.java new file mode 100644 index 0000000..c7770f2 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/RdosFileEntry.java @@ -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. + *

+ * Since I was unable to locate the file entries on the internet, it is documented here: + * + * Offset Description
+ * ====== ====================================================
+ * $00-$17 File name; space-filled. If the first byte is $00, that is the end of the
+ * directory. If the first byte is $80, the file is deleted.
+ * $18 File type. Appears to be actual letter ('A'=Applesoft, etc)
+ * $19 File length in blocks (block = sector = 256 bytes)
+ * $1A-$1B Address of application. For Applesoft and binary; others may vary.
+ * $1C-$1D Length in bytes of file.
+ * $1E-$1F Starting block of application.
+ *
+ *

+ * 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() ? " " : 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()); + } +} diff --git a/src/com/webcodepro/applecommander/storage/RdosFormatDisk.java b/src/com/webcodepro/applecommander/storage/RdosFormatDisk.java new file mode 100644 index 0000000..2db2572 --- /dev/null +++ b/src/com/webcodepro/applecommander/storage/RdosFormatDisk.java @@ -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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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".) + *

+ * 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 + * 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 + * 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 + * 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 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 + * 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."); + } + } + } +} diff --git a/src/com/webcodepro/applecommander/ui/UserPreferences.java b/src/com/webcodepro/applecommander/ui/UserPreferences.java new file mode 100644 index 0000000..ea0256a --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/UserPreferences.java @@ -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. + *

+ * 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); + } +} diff --git a/src/com/webcodepro/applecommander/ui/images/AppleCommanderLogo.gif b/src/com/webcodepro/applecommander/ui/images/AppleCommanderLogo.gif new file mode 100644 index 0000000..e2b8283 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/AppleCommanderLogo.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/ExportWizardLogo.gif b/src/com/webcodepro/applecommander/ui/images/ExportWizardLogo.gif new file mode 100644 index 0000000..3e52027 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/ExportWizardLogo.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/about.gif b/src/com/webcodepro/applecommander/ui/images/about.gif new file mode 100644 index 0000000..6d8d1be Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/about.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/appleicon.gif b/src/com/webcodepro/applecommander/ui/images/appleicon.gif new file mode 100644 index 0000000..4c9a77a Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/appleicon.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/deletedfiles.gif b/src/com/webcodepro/applecommander/ui/images/deletedfiles.gif new file mode 100644 index 0000000..7fd2453 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/deletedfiles.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/deletefile.gif b/src/com/webcodepro/applecommander/ui/images/deletefile.gif new file mode 100644 index 0000000..6f64766 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/deletefile.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/detailfileview.gif b/src/com/webcodepro/applecommander/ui/images/detailfileview.gif new file mode 100644 index 0000000..f84852b Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/detailfileview.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/diskicon.gif b/src/com/webcodepro/applecommander/ui/images/diskicon.gif new file mode 100644 index 0000000..a6978e3 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/diskicon.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/exportfile.gif b/src/com/webcodepro/applecommander/ui/images/exportfile.gif new file mode 100644 index 0000000..3aa1272 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/exportfile.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/importfile.gif b/src/com/webcodepro/applecommander/ui/images/importfile.gif new file mode 100644 index 0000000..7db9b64 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/importfile.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/nativefileview.gif b/src/com/webcodepro/applecommander/ui/images/nativefileview.gif new file mode 100644 index 0000000..4c9a77a Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/nativefileview.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/newdisk.gif b/src/com/webcodepro/applecommander/ui/images/newdisk.gif new file mode 100644 index 0000000..d670c0d Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/newdisk.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/opendisk.gif b/src/com/webcodepro/applecommander/ui/images/opendisk.gif new file mode 100644 index 0000000..0429f57 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/opendisk.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/saveimage.gif b/src/com/webcodepro/applecommander/ui/images/saveimage.gif new file mode 100644 index 0000000..698d9a2 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/saveimage.gif differ diff --git a/src/com/webcodepro/applecommander/ui/images/standardfileview.gif b/src/com/webcodepro/applecommander/ui/images/standardfileview.gif new file mode 100644 index 0000000..0c1a698 Binary files /dev/null and b/src/com/webcodepro/applecommander/ui/images/standardfileview.gif differ diff --git a/src/com/webcodepro/applecommander/ui/swt/AppleWorksWordProcessorPane.java b/src/com/webcodepro/applecommander/ui/swt/AppleWorksWordProcessorPane.java new file mode 100644 index 0000000..f8b5f8c --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/AppleWorksWordProcessorPane.java @@ -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. + *

+ * 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(); + } +} diff --git a/src/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java b/src/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java new file mode 100644 index 0000000..39d4d0c --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java @@ -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. + *

+ * 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= 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 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 + * 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() { + } +} diff --git a/src/com/webcodepro/applecommander/ui/swt/DiskMapTab.java b/src/com/webcodepro/applecommander/ui/swt/DiskMapTab.java new file mode 100644 index 0000000..22a4ee4 --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/DiskMapTab.java @@ -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. + *

+ * 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 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; i0) 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= 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); + } + } +} diff --git a/src/com/webcodepro/applecommander/ui/swt/DiskWindow.java b/src/com/webcodepro/applecommander/ui/swt/DiskWindow.java new file mode 100644 index 0000000..186a41f --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/DiskWindow.java @@ -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. + *

+ * 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(); + } + +} diff --git a/src/com/webcodepro/applecommander/ui/swt/DropDownSelectionListener.java b/src/com/webcodepro/applecommander/ui/swt/DropDownSelectionListener.java new file mode 100644 index 0000000..ebaa1d5 --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/DropDownSelectionListener.java @@ -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. + *

+ * 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

+ * 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; + } +} diff --git a/src/com/webcodepro/applecommander/ui/swt/ExportGraphicsTypePane.java b/src/com/webcodepro/applecommander/ui/swt/ExportGraphicsTypePane.java new file mode 100644 index 0000000..1d22c5b --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/ExportGraphicsTypePane.java @@ -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. + *

+ * 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 + * 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; + } +} diff --git a/src/com/webcodepro/applecommander/ui/swt/ImageCanvas.java b/src/com/webcodepro/applecommander/ui/swt/ImageCanvas.java new file mode 100644 index 0000000..4ff907a --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/ImageCanvas.java @@ -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. + *

+ * 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); + } +} diff --git a/src/com/webcodepro/applecommander/ui/swt/ImageManager.java b/src/com/webcodepro/applecommander/ui/swt/ImageManager.java new file mode 100644 index 0000000..dd1fc01 --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/ImageManager.java @@ -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. + *

+ * 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; + } + +} diff --git a/src/com/webcodepro/applecommander/ui/swt/SwtAppleCommander.java b/src/com/webcodepro/applecommander/ui/swt/SwtAppleCommander.java new file mode 100644 index 0000000..33fb408 --- /dev/null +++ b/src/com/webcodepro/applecommander/ui/swt/SwtAppleCommander.java @@ -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. + *

+ * 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 + * 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(); +}