Version 1.1.1 is the initial GPL release of AppleCommander.
8
.classpath
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
|
||||||
|
<classpathentry kind="src" path="/JUnit"/>
|
||||||
|
<classpathentry kind="lib" path="C:/Program Files/Eclipse 2.0/plugins/org.eclipse.swt.win32_2.0.2/ws/win32/swt.jar"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
2
.cvsignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bin
|
||||||
|
AppleCommander.preferences
|
18
.project
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>AppleCommander</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
<project>JUnit</project>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
21
HEADER
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Attach the following code to the header of all source files.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
270
LICENSE
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
The GNU General Public License (GPL)
|
||||||
|
Version 2, June 1991
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to
|
||||||
|
share and change it. By contrast, the GNU General Public License is
|
||||||
|
intended to guarantee your freedom to share and change free software--to
|
||||||
|
make sure the software is free for all its users. This General Public
|
||||||
|
License applies to most of the Free Software Foundation's software and to
|
||||||
|
any other program whose authors commit to using it. (Some other Free
|
||||||
|
Software Foundation software is covered by the GNU Library General Public
|
||||||
|
License instead.) You can apply it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price.
|
||||||
|
Our General Public Licenses are designed to make sure that you have the
|
||||||
|
freedom to distribute copies of free software (and charge for this service
|
||||||
|
if you wish), that you receive source code or can get it if you want it,
|
||||||
|
that you can change the software or use pieces of it in new free programs;
|
||||||
|
and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid anyone to
|
||||||
|
deny you these rights or to ask you to surrender the rights. These
|
||||||
|
restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or
|
||||||
|
for a fee, you must give the recipients all the rights that you have. You
|
||||||
|
must make sure that they, too, receive or can get the source code. And you
|
||||||
|
must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and (2)
|
||||||
|
offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain that
|
||||||
|
everyone understands that there is no warranty for this free software. If
|
||||||
|
the software is modified by someone else and passed on, we want its
|
||||||
|
recipients to know that what they have is not the original, so that any
|
||||||
|
problems introduced by others will not reflect on the original authors'
|
||||||
|
reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software patents. We
|
||||||
|
wish to avoid the danger that redistributors of a free program will
|
||||||
|
individually obtain patent licenses, in effect making the program
|
||||||
|
proprietary. To prevent this, we have made it clear that any patent must
|
||||||
|
be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains a
|
||||||
|
notice placed by the copyright holder saying it may be distributed under
|
||||||
|
the terms of this General Public License. The "Program", below, refers to
|
||||||
|
any such program or work, and a "work based on the Program" means either
|
||||||
|
the Program or any derivative work under copyright law: that is to say, a
|
||||||
|
work containing the Program or a portion of it, either verbatim or with
|
||||||
|
modifications and/or translated into another language. (Hereinafter,
|
||||||
|
translation is included without limitation in the term "modification".)
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of running
|
||||||
|
the Program is not restricted, and the output from the Program is covered
|
||||||
|
only if its contents constitute a work based on the Program (independent
|
||||||
|
of having been made by running the Program). Whether that is true depends
|
||||||
|
on what theProgram does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's source
|
||||||
|
code as you receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice and
|
||||||
|
disclaimer of warranty; keep intact all the notices that refer to this
|
||||||
|
License and to the absence of any warranty; and give any other recipients
|
||||||
|
of the Program a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion of it,
|
||||||
|
thus forming a work based on the Program, and copy and distribute such
|
||||||
|
modifications or work under the terms of Section 1 above, provided that
|
||||||
|
you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices stating
|
||||||
|
that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in whole
|
||||||
|
or in part contains or is derived from the Program or any part thereof, to
|
||||||
|
be licensed as a whole at no charge to all third parties under the terms
|
||||||
|
of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively when run,
|
||||||
|
you must cause it, when started running for such interactive use in the
|
||||||
|
most ordinary way, to print or display an announcement including an
|
||||||
|
appropriate copyright notice and a notice that there is no warranty (or
|
||||||
|
else, saying that you provide a warranty) and that users may redistribute
|
||||||
|
the program under these conditions, and telling the user how to view a
|
||||||
|
copy of this License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on the
|
||||||
|
Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Program, and can be
|
||||||
|
reasonably considered independent and separate works in themselves, then
|
||||||
|
this License, and its terms, do not apply to those sections when you
|
||||||
|
distribute them as separate works. But when you distribute the same
|
||||||
|
sections as part of a whole which is a work based on the Program, the
|
||||||
|
distribution of the whole must be on the terms of this License, whose
|
||||||
|
permissions for other licensees extend to the entire whole, and thus to
|
||||||
|
each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works
|
||||||
|
based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of a
|
||||||
|
storage or distribution medium does not bring the other work under the
|
||||||
|
scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it, under
|
||||||
|
Section 2) in object code or executable form under the terms of Sections 1
|
||||||
|
and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable source
|
||||||
|
code, which must be distributed under the terms of Sections 1 and 2 above
|
||||||
|
on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three years, to
|
||||||
|
give any third party, for a charge no more than your cost of physically
|
||||||
|
performing source distribution, a complete machine-readable copy of the
|
||||||
|
corresponding source code, to be distributed under the terms of Sections 1
|
||||||
|
and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer to
|
||||||
|
distribute corresponding source code. (This alternative is allowed only
|
||||||
|
for noncommercial distribution and only if you received the program in
|
||||||
|
object code or executable form with such an offer, in accord with
|
||||||
|
Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for making
|
||||||
|
modifications to it. For an executable work, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the executable. However, as a special exception, the
|
||||||
|
source code distributed need not include anything that is normally
|
||||||
|
distributed (in either source or binary form) with the major components
|
||||||
|
(compiler, kernel, and so on) of the operating system on which the
|
||||||
|
executable runs, unless that component itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering access to
|
||||||
|
copy from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place counts as distribution of the source code,
|
||||||
|
even though third parties are not compelled to copy the source along with
|
||||||
|
the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program except
|
||||||
|
as expressly provided under this License. Any attempt otherwise to copy,
|
||||||
|
modify, sublicense or distribute the Program is void, and will
|
||||||
|
automatically terminate your rights under this License. However, parties
|
||||||
|
who have received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in full
|
||||||
|
compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute
|
||||||
|
the Program or its derivative works. These actions are prohibited by law
|
||||||
|
if you do not accept this License. Therefore, by modifying or distributing
|
||||||
|
the Program (or any work based on the Program), you indicate your
|
||||||
|
acceptance of this License to do so, and all its terms and conditions for
|
||||||
|
copying, distributing or modifying the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the original
|
||||||
|
licensor to copy, distribute or modify the Program subject to these terms
|
||||||
|
and conditions. You may not impose any further restrictions on the
|
||||||
|
recipients' exercise of the rights granted herein. You are not responsible
|
||||||
|
for enforcing compliance by third parties to this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot distribute
|
||||||
|
so as to satisfy simultaneously your obligations under this License and
|
||||||
|
any other pertinent obligations, then as a consequence you may not
|
||||||
|
distribute the Program at all. For example, if a patent license would not
|
||||||
|
permit royalty-free redistribution of the Program by all those who receive
|
||||||
|
copies directly or indirectly through you, then the only way you could
|
||||||
|
satisfy both it and this License would be to refrain entirely from
|
||||||
|
distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any such
|
||||||
|
claims; this section has the sole purpose of protecting the integrity of
|
||||||
|
the free software distribution system, which is implemented by public
|
||||||
|
license practices. Many people have made generous contributions to the
|
||||||
|
wide range of software distributed through that system in reliance on
|
||||||
|
consistent application of that system; it is up to the author/donor to
|
||||||
|
decide if he or she is willing to distribute software through any other
|
||||||
|
system and a licensee cannot impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original
|
||||||
|
copyright holder who places the Program under this License may add an
|
||||||
|
explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the General Public License from time to time. Such new versions will be
|
||||||
|
similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free
|
||||||
|
Software Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals of
|
||||||
|
preserving the free status of all derivatives of our free software and of
|
||||||
|
promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
|
||||||
|
ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT
|
||||||
|
LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES
|
||||||
|
SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
|
||||||
|
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
3
build/manifest.mf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
Class-Path: swt.jar
|
||||||
|
Main-Class: com.webcodepro.applecommander.ui.AppleCommander
|
BIN
imageSource/AppleCommanderLogo.mix
Normal file
BIN
imageSource/ExportWizardLogo.mix
Normal file
170
src/com/webcodepro/applecommander/storage/AppleUtil.java
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains helper methods for dealing with Apple2 data.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 4:16:16 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class AppleUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the value of a word.
|
||||||
|
* Pulls value from buffer given the offset.
|
||||||
|
* A word is two bytes, in standard Apple LO/HI format.
|
||||||
|
*/
|
||||||
|
public static int getWordValue(byte[] buffer, int offset) {
|
||||||
|
return getWordValue(buffer[offset], buffer[offset+1]);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Compute the value of a word.
|
||||||
|
*/
|
||||||
|
public static int getWordValue(byte low, byte high) {
|
||||||
|
return getUnsignedByte(low) + getUnsignedByte(high)*256;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the value of a 3 byte value. This may be ProDOS specific.
|
||||||
|
* Pulls value from buffer given the offset.
|
||||||
|
* Stored in standard Apple LO/HI format.
|
||||||
|
*/
|
||||||
|
public static int get3ByteValue(byte[] buffer, int offset) {
|
||||||
|
return getUnsignedByte(buffer[offset])
|
||||||
|
+ getUnsignedByte(buffer[offset+1])*256
|
||||||
|
+ getUnsignedByte(buffer[offset+2])*65536;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract out an unsigned byte as an int.
|
||||||
|
* All Java bytes are signed; need to convert to an int
|
||||||
|
* and remove the sign.
|
||||||
|
*/
|
||||||
|
public static int getUnsignedByte(byte value) {
|
||||||
|
return (int) value & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of bits set in a byte.
|
||||||
|
*/
|
||||||
|
public static int getBitCount(byte byt) {
|
||||||
|
int count = 0;
|
||||||
|
for (int ix=0; ix<8; ix++) {
|
||||||
|
if (isBitSet(byt, ix)) count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a specific bit is set.
|
||||||
|
*/
|
||||||
|
public static boolean isBitSet(byte byt, int bit) {
|
||||||
|
byte[] masks = { (byte)0x01, (byte)0x02,(byte)0x04, (byte)0x08,
|
||||||
|
(byte)0x10, (byte)0x20, (byte)0x40, (byte)0x80 };
|
||||||
|
return (byt & masks[bit]) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a string from the buffer.
|
||||||
|
*/
|
||||||
|
public static String getString(byte[] buffer, int offset, int length) {
|
||||||
|
byte[] value = new byte[length];
|
||||||
|
for (int i=0; i<length; i++) {
|
||||||
|
byte ch = buffer[offset+i];
|
||||||
|
ch &= 0x7f;
|
||||||
|
value[i] = ch;
|
||||||
|
}
|
||||||
|
return new String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a Pascal string from the buffer.
|
||||||
|
*/
|
||||||
|
public static String getPascalString(byte[] buffer, int offset) {
|
||||||
|
int length = getUnsignedByte(buffer[offset]);
|
||||||
|
return getString(buffer, offset+1, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a Pascal date from the buffer.
|
||||||
|
*/
|
||||||
|
public static Date getPascalDate(byte[] buffer, int offset) {
|
||||||
|
int pascalDate = getWordValue(buffer, offset);
|
||||||
|
int month = pascalDate & 0x000f;
|
||||||
|
int day = (pascalDate & 0x00f0) >> 4;
|
||||||
|
int year = (pascalDate & 0xff00) >> 8;
|
||||||
|
if (year < 50) year+= 2000;
|
||||||
|
if (year < 100) year+= 1900;
|
||||||
|
GregorianCalendar gc = new GregorianCalendar(year, month, day);
|
||||||
|
return gc.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a ProDOS string from the buffer.
|
||||||
|
*/
|
||||||
|
public static String getProdosString(byte[] buffer, int offset) {
|
||||||
|
int length = getUnsignedByte(buffer[offset]) & 0x0f;
|
||||||
|
return getString(buffer, offset+1, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a byte value as hexidecimal.
|
||||||
|
*/
|
||||||
|
public static String getFormattedByte(int byt) {
|
||||||
|
String[] values = { "0", "1", "2", "3", "4", "5", "6", "7",
|
||||||
|
"8", "9", "A", "B", "C", "D", "E", "F" };
|
||||||
|
int byt1 = byt & 0x0f;
|
||||||
|
int byt2 = (byt & 0xf0) >> 4;
|
||||||
|
return values[byt2] + values[byt1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a word value as hexidecimal.
|
||||||
|
*/
|
||||||
|
public static String getFormattedWord(int word) {
|
||||||
|
return getFormattedByte((word & 0xff00) >> 8)
|
||||||
|
+ getFormattedByte(word & 0x00ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a ProDOS date from the buffer.
|
||||||
|
*/
|
||||||
|
public static Date getProdosDate(byte[] buffer, int offset) {
|
||||||
|
int ymd = getWordValue(buffer, offset);
|
||||||
|
if (ymd == 0) return null;
|
||||||
|
int hm = getWordValue(buffer, offset+2);
|
||||||
|
|
||||||
|
int day = ymd & 0x001f; // bits 0-4
|
||||||
|
int month = (ymd & 0x01e0) >> 5; // bits 5-8
|
||||||
|
int year = (ymd & 0xfe00) >> 9; // bits 9-15
|
||||||
|
int minute = hm & 0x003f; // bits 0-5
|
||||||
|
int hour = (hm & 0x1f00) >> 8; // bits 8-12
|
||||||
|
|
||||||
|
if (year < 50) year+= 2000;
|
||||||
|
if (year < 100) year+= 1900;
|
||||||
|
|
||||||
|
GregorianCalendar gc = new GregorianCalendar(year, month, day, hour, minute);
|
||||||
|
return gc.getTime();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the contents of an AWP (AppleWorks word processor) document and
|
||||||
|
* convert to a text format.
|
||||||
|
* See format documentation at:
|
||||||
|
* http://www.gno.org/pub/apple2/doc/apple/filetypes/ftn.1a.xxxx
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 15, 2002 3:55:21 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class AppleWorksWordProcessorFileFilter implements FileFilter {
|
||||||
|
public static final int RENDER_AS_TEXT = 0;
|
||||||
|
public static final int RENDER_AS_HTML = 1;
|
||||||
|
private int rendering = RENDER_AS_TEXT;
|
||||||
|
/**
|
||||||
|
* Constructor for AppleWorksWordProcessorFileFilter.
|
||||||
|
*/
|
||||||
|
public AppleWorksWordProcessorFileFilter() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Process the given FileEntry and return a byte array with filtered data.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileFilter#filter(FileEntry)
|
||||||
|
*/
|
||||||
|
public byte[] filter(FileEntry fileEntry) {
|
||||||
|
byte[] fileData = fileEntry.getFileData();
|
||||||
|
if (fileData[4] != 0x4f) return null; // not an AWP file!
|
||||||
|
ByteArrayOutputStream byteArray = new ByteArrayOutputStream(fileData.length);
|
||||||
|
PrintWriter printWriter = new PrintWriter(byteArray, true);
|
||||||
|
if (isHtmlRendering()) {
|
||||||
|
printWriter.println("<html><style>BODY { font-family: monospace; }</style><body>");
|
||||||
|
}
|
||||||
|
boolean version3 = (fileData[183] != 0);
|
||||||
|
int offset = 300 + (version3 ? 2 : 0); // version 3.0's first line record is invalid
|
||||||
|
while (offset < fileData.length) {
|
||||||
|
int byte0 = AppleUtil.getUnsignedByte(fileData[offset++]);
|
||||||
|
int byte1 = AppleUtil.getUnsignedByte(fileData[offset++]);
|
||||||
|
|
||||||
|
if (byte0 == 0xff && byte1 == 0xff) { // end of file
|
||||||
|
break;
|
||||||
|
} else if (byte1 == 0xd0) { // Carriage return line records
|
||||||
|
handleReturn(printWriter);
|
||||||
|
} else if (byte1 > 0xd0) { // Command line records
|
||||||
|
if (isHtmlRendering()) {
|
||||||
|
offset = handleCommandRecordAsHtml(byte0, byte1, printWriter, offset);
|
||||||
|
}
|
||||||
|
} else { // Text records (assumed)
|
||||||
|
offset = handleTextRecord(fileData, printWriter, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isHtmlRendering()) {
|
||||||
|
printWriter.println("</body></html>");
|
||||||
|
}
|
||||||
|
return byteArray.toByteArray();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Deal with an individual text record.
|
||||||
|
*/
|
||||||
|
protected int handleTextRecord(byte[] fileData, PrintWriter printWriter, int offset) {
|
||||||
|
int byte2 = AppleUtil.getUnsignedByte(fileData[offset++]);
|
||||||
|
int byte3 = AppleUtil.getUnsignedByte(fileData[offset++]);
|
||||||
|
boolean addReturn = (byte3 >= 0x80);
|
||||||
|
int length = (byte3 & 0x7f);
|
||||||
|
while (length > 0) {
|
||||||
|
byte ch = fileData[offset++];
|
||||||
|
length--;
|
||||||
|
if (ch < 0x20) { // special formatting character
|
||||||
|
if (isHtmlRendering()) handleSpecialCodesAsHtml(printWriter, ch);
|
||||||
|
} else {
|
||||||
|
if (isHtmlRendering() && ch == ' ') {
|
||||||
|
int extraSpaces = 0;
|
||||||
|
while (fileData[offset+extraSpaces] == ' ') {
|
||||||
|
extraSpaces++;
|
||||||
|
}
|
||||||
|
if (extraSpaces > 0) {
|
||||||
|
printWriter.print(" ");
|
||||||
|
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("<br>");
|
||||||
|
else printWriter.println();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Process special coding of a text record.
|
||||||
|
*/
|
||||||
|
protected void handleSpecialCodesAsHtml(PrintWriter printWriter, byte ch) {
|
||||||
|
switch (ch) {
|
||||||
|
case 0x01: printWriter.print("<b>");
|
||||||
|
break;
|
||||||
|
case 0x02: printWriter.print("</b>");
|
||||||
|
break;
|
||||||
|
case 0x03: printWriter.print("<sup>");
|
||||||
|
break;
|
||||||
|
case 0x04: printWriter.print("</sup>");
|
||||||
|
break;
|
||||||
|
case 0x05: printWriter.print("<sub>");
|
||||||
|
break;
|
||||||
|
case 0x06: printWriter.print("</sub>");
|
||||||
|
break;
|
||||||
|
case 0x07: printWriter.print("<u>");
|
||||||
|
break;
|
||||||
|
case 0x08: printWriter.print("</u>");
|
||||||
|
break;
|
||||||
|
case 0x09: printWriter.print("[Page#]");
|
||||||
|
break;
|
||||||
|
case 0x0b: printWriter.print(" ");
|
||||||
|
break;
|
||||||
|
case 0x0e: SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy");
|
||||||
|
printWriter.print(dateFormat.format(new Date()));
|
||||||
|
break;
|
||||||
|
case 0x0f: SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
|
||||||
|
printWriter.print(timeFormat.format(new Date()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Deal with an individual command line record.
|
||||||
|
*/
|
||||||
|
protected int handleCommandRecordAsHtml(int byte0, int byte1,
|
||||||
|
PrintWriter printWriter, int offset) {
|
||||||
|
|
||||||
|
switch (byte1) {
|
||||||
|
case 0xd7: printWriter.println("<style>BODY: text-align: right;</style>");
|
||||||
|
break;
|
||||||
|
case 0xdf: printWriter.println("<style>BODY: text-align: justify;</style>");
|
||||||
|
break;
|
||||||
|
case 0xe0: printWriter.println("<style>BODY: text-align: left;</style>");
|
||||||
|
break;
|
||||||
|
case 0xe1: printWriter.println("<style>BODY: text-align: center;</style>");
|
||||||
|
break;
|
||||||
|
case 0xee: for (int i=0; i<byte0; i++) {
|
||||||
|
printWriter.println("<br>");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Give suggested file name.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileFilter#getSuggestedFileName(FileEntry)
|
||||||
|
*/
|
||||||
|
public String getSuggestedFileName(FileEntry fileEntry) {
|
||||||
|
String fileName = fileEntry.getFilename().trim();
|
||||||
|
String extension = ".txt";
|
||||||
|
if (isHtmlRendering()) extension = ".html";
|
||||||
|
|
||||||
|
if (!fileName.toLowerCase().endsWith(extension)) {
|
||||||
|
fileName = fileName + extension;
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the rendering method.
|
||||||
|
*/
|
||||||
|
public void setRendering(int rendering) {
|
||||||
|
this.rendering = rendering;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Indicates if this is a text rendering.
|
||||||
|
*/
|
||||||
|
public boolean isTextRendering() {
|
||||||
|
return rendering == RENDER_AS_TEXT;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Indicates if this is an HTML rendering.
|
||||||
|
*/
|
||||||
|
public boolean isHtmlRendering() {
|
||||||
|
return rendering == RENDER_AS_HTML;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the given file as an Applesoft file.
|
||||||
|
* <p>
|
||||||
|
* Applesoft memory format:<br>
|
||||||
|
* [Line]<br>
|
||||||
|
* ...
|
||||||
|
* [Line]<br>
|
||||||
|
* <br>
|
||||||
|
* where <Line> is:<br>
|
||||||
|
* [Next addr - $0000 is end of program] (word)<br>
|
||||||
|
* [Line no] (word)<br>
|
||||||
|
* [Tokens and/or characters]<br>
|
||||||
|
* [End-of-line marker: $00 bytes]
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 2, 2002 10:04:10 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ApplesoftFileFilter implements FileFilter {
|
||||||
|
private static String tokens[] = { // starts at $80
|
||||||
|
" END ", " FOR ", " NEXT ", " DATA ", " INPUT ", " DEL ",
|
||||||
|
" DIM ", " READ ", " GR ", " TEXT ", " PR# ", " IN# ",
|
||||||
|
" CALL ", " PLOT ", " HLIN ", " VLIN ", " HGR2 ", " HGR ",
|
||||||
|
" HCOLOR= ", " HPLOT ", " DRAW ", " XDRAW ", " HTAB ", " HOME ",
|
||||||
|
" ROT= ", " SCALE= ", " SHLOAD ", " TRACE ", " NOTRACE ", " NORMAL ",
|
||||||
|
" INVERSE ", " FLASH ", " COLOR= ", " POP ", " VTAB ", " HIMEM: ",
|
||||||
|
" LOMEM: ", " ONERR ", " RESUME ", " RECALL ", " STORE ", " SPEED= ",
|
||||||
|
" LET ", " GOTO ", " RUN ", " IF ", " RESTORE ", " & ",
|
||||||
|
" GOSUB ", " RETURN ", " REM ", " STOP ", " ON ", " WAIT ",
|
||||||
|
" LOAD ", " SAVE ", " DEF ", " POKE ", " PRINT ", " CONT ",
|
||||||
|
" LIST ", " CLEAR ", " GET ", " NEW ", " TAB( ", " TO ",
|
||||||
|
" FN ", " SPC( ", " THEN ", " AT ", " NOT ", " STEP ",
|
||||||
|
" +", " -", " *", "/", " ^", " AND ",
|
||||||
|
" OR ", " >", " =", " <", " SGN", " INT",
|
||||||
|
" ABS", " USR", " FRE", " SCRN( ", " PDL", " POS",
|
||||||
|
" SQR", " RND", " LOG", " EXP", " COS", " SIN",
|
||||||
|
" TAN", " ATN", " PEEK", " LEN", " STR$", " VAL",
|
||||||
|
" ASC", " CHR$", " LEFT$", " RIGHT$", " MID$ " };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ApplesoftFileFilter.
|
||||||
|
*/
|
||||||
|
public ApplesoftFileFilter() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the given FileEntry and return a text image of the Applesoft file.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileFilter#filter(FileEntry)
|
||||||
|
*/
|
||||||
|
public byte[] filter(FileEntry fileEntry) {
|
||||||
|
byte[] fileData = fileEntry.getFileData();
|
||||||
|
int offset = 0;
|
||||||
|
ByteArrayOutputStream byteArray = new ByteArrayOutputStream(fileData.length * 2);
|
||||||
|
PrintWriter printWriter = new PrintWriter(byteArray, true);
|
||||||
|
while (offset < fileData.length) {
|
||||||
|
int nextAddress = AppleUtil.getWordValue(fileData, offset);
|
||||||
|
if (nextAddress == 0) break; // next address of 0 indicates end of program
|
||||||
|
offset+= 2;
|
||||||
|
int lineNumber = AppleUtil.getWordValue(fileData, offset);
|
||||||
|
offset+= 2;
|
||||||
|
printWriter.print(lineNumber);
|
||||||
|
printWriter.print(' ');
|
||||||
|
while (fileData[offset] != 0) {
|
||||||
|
byte byt = fileData[offset++];
|
||||||
|
if ((byt & 0x80) != 0) {
|
||||||
|
int token = AppleUtil.getUnsignedByte(byt) - 0x80;
|
||||||
|
if (token >= tokens.length) {
|
||||||
|
printWriter.print("<UNKNOWN TOKEN>");
|
||||||
|
} else {
|
||||||
|
String tokenString = tokens[token];
|
||||||
|
printWriter.print(tokenString);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char ch = (char)byt;
|
||||||
|
if (ch < 0x20) {
|
||||||
|
printWriter.print("<CTRL-");
|
||||||
|
printWriter.print((char)('@' + ch));
|
||||||
|
printWriter.print(">");
|
||||||
|
} else {
|
||||||
|
printWriter.print(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printWriter.println();
|
||||||
|
offset++; // skip to next line
|
||||||
|
}
|
||||||
|
return byteArray.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give suggested file name.
|
||||||
|
*/
|
||||||
|
public String getSuggestedFileName(FileEntry fileEntry) {
|
||||||
|
String fileName = fileEntry.getFilename().trim();
|
||||||
|
if (!fileName.toLowerCase().endsWith(".bas")) {
|
||||||
|
fileName = fileName + ".bas";
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the given file data to be the appropriate length.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 2, 2002 9:07:52 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class BinaryFileFilter implements FileFilter {
|
||||||
|
/**
|
||||||
|
* Constructor for BinaryFileFilter.
|
||||||
|
*/
|
||||||
|
public BinaryFileFilter() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the given FileEntry and return a byte array with filtered data.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileFilter#filter(byte[])
|
||||||
|
*/
|
||||||
|
public byte[] filter(FileEntry fileEntry) {
|
||||||
|
return fileEntry.getFileData(); // should be nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give suggested file name.
|
||||||
|
*/
|
||||||
|
public String getSuggestedFileName(FileEntry fileEntry) {
|
||||||
|
String fileName = fileEntry.getFilename().trim();
|
||||||
|
if (!fileName.toLowerCase().endsWith(".dump")) {
|
||||||
|
fileName = fileName + ".dump";
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
316
src/com/webcodepro/applecommander/storage/Disk.java
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract representation of an Apple2 disk (floppy, 800k, hard disk).
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 3, 2002 10:59:47 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class Disk {
|
||||||
|
/**
|
||||||
|
* Specifies a filter to be used in determining filetypes which are supported.
|
||||||
|
* This works from a file extension, so it may or may not apply to the Macintosh.
|
||||||
|
*/
|
||||||
|
public class FilenameFilter {
|
||||||
|
private String names;
|
||||||
|
private String extensions;
|
||||||
|
public FilenameFilter(String names, String extensions) {
|
||||||
|
this.names = names;
|
||||||
|
this.extensions = extensions;
|
||||||
|
}
|
||||||
|
public String getExtensions() {
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
public String getNames() {
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int BLOCK_SIZE = 512;
|
||||||
|
public static final int SECTOR_SIZE = 256;
|
||||||
|
public static final int APPLE_140KB_DISK = 143360;
|
||||||
|
public static final int APPLE_800KB_DISK = 819200;
|
||||||
|
public static final int APPLE_800KB_2IMG_DISK = APPLE_800KB_DISK + 0x40;
|
||||||
|
|
||||||
|
private static FilenameFilter[] filenameFilters;
|
||||||
|
private byte[] diskImage;
|
||||||
|
private String filename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the supported file filters supported by the Disk interface.
|
||||||
|
* This is due to the fact that FilenameFilter is an innerclass of Disk -
|
||||||
|
* without an instance of the class, the filters cannot be created.
|
||||||
|
*/
|
||||||
|
public static FilenameFilter[] getFilenameFilters() {
|
||||||
|
if (filenameFilters == null) {
|
||||||
|
new Disk();
|
||||||
|
}
|
||||||
|
return filenameFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a Disk - used only to generate FilenameFilter objects.
|
||||||
|
*/
|
||||||
|
private Disk() {
|
||||||
|
filenameFilters = new FilenameFilter[] {
|
||||||
|
new FilenameFilter("All Emulator Images",
|
||||||
|
"*.do; *.dsk; *.po; *.2mg; *.2img; *.hdv; *.do.gz; *.dsk.gz; *.po.gz; *.2mg.gz; *.2img.gz"),
|
||||||
|
new FilenameFilter("140K DOS 3.3 Ordered Images (*.do, *.dsk)",
|
||||||
|
"*.do; *.dsk; *.do.gz; *.dsk.gz"),
|
||||||
|
new FilenameFilter("140K ProDOS Ordered Images (*.po)",
|
||||||
|
"*.po; *.po.gz"),
|
||||||
|
new FilenameFilter("800K ProDOS Ordered Images (*.2mg, *.2img)",
|
||||||
|
"*.2mg; *.2img; *.2mg.gz, *.2img.gz"),
|
||||||
|
new FilenameFilter("ApplePC Hard Disk Images (*.hdv)",
|
||||||
|
"*.hdv"),
|
||||||
|
new FilenameFilter("All Compressed Images",
|
||||||
|
"*.do.gz; *.dsk.gz; *.po.gz; *.2mg.gz; *.2img.gz"),
|
||||||
|
new FilenameFilter("All Files",
|
||||||
|
"*.*")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Disk with the given byte array.
|
||||||
|
*/
|
||||||
|
protected Disk(String filename, byte[] diskImage) {
|
||||||
|
this.diskImage = diskImage;
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Disk and load the specified file.
|
||||||
|
* Read in the entire contents of the file.
|
||||||
|
*/
|
||||||
|
public Disk(String filename) throws IOException {
|
||||||
|
this.filename = filename;
|
||||||
|
InputStream input = new FileInputStream(filename);
|
||||||
|
if (isCompressed()) {
|
||||||
|
input = new GZIPInputStream(input);
|
||||||
|
}
|
||||||
|
int diskSize = APPLE_140KB_DISK;
|
||||||
|
if (is2ImgOrder()) {
|
||||||
|
diskSize = APPLE_800KB_2IMG_DISK;
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream diskImageByteArray =
|
||||||
|
new ByteArrayOutputStream(diskSize);
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
int bytes;
|
||||||
|
while ((bytes = input.read(data)) > 0) {
|
||||||
|
diskImageByteArray.write(data, 0, bytes);
|
||||||
|
}
|
||||||
|
input.close();
|
||||||
|
this.diskImage = diskImageByteArray.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine type of disk, and return the appropriate
|
||||||
|
* FormattedDisk object. Returns null if none are
|
||||||
|
* recognized.
|
||||||
|
*/
|
||||||
|
public FormattedDisk getFormattedDisk() {
|
||||||
|
if (isProdosFormat()) {
|
||||||
|
return new ProdosFormatDisk(filename, diskImage);
|
||||||
|
} else if (isDosFormat()) {
|
||||||
|
return new DosFormatDisk(filename, diskImage);
|
||||||
|
} else if (isPascalFormat()) {
|
||||||
|
return new PascalFormatDisk(filename, diskImage);
|
||||||
|
} else if (isRdosFormat()) {
|
||||||
|
return new RdosFormatDisk(filename, diskImage);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the diskImage.
|
||||||
|
* @return byte[]
|
||||||
|
*/
|
||||||
|
public byte[] getDiskImage() {
|
||||||
|
return diskImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a portion of the disk image.
|
||||||
|
*/
|
||||||
|
public byte[] readBytes(int start, int length) {
|
||||||
|
byte[] buffer = new byte[length];
|
||||||
|
System.arraycopy(diskImage, start + (is2ImgOrder() ? 0x40 : 0), buffer, 0, length);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the filename.
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if this disk is GZIP compressed.
|
||||||
|
*/
|
||||||
|
public boolean isCompressed() {
|
||||||
|
return filename.toLowerCase().endsWith(".gz");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if this disk is ProDOS ordered (beginning with block 0).
|
||||||
|
*/
|
||||||
|
public boolean isProdosOrder() {
|
||||||
|
return filename.toLowerCase().endsWith(".po")
|
||||||
|
|| filename.toLowerCase().endsWith(".po.gz")
|
||||||
|
|| is2ImgOrder()
|
||||||
|
|| filename.toLowerCase().endsWith(".hdv");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if this disk is DOS ordered (T0,S0 - T35,S15).
|
||||||
|
*/
|
||||||
|
public boolean isDosOrder() {
|
||||||
|
return filename.toLowerCase().endsWith(".do")
|
||||||
|
|| filename.toLowerCase().endsWith(".do.gz")
|
||||||
|
|| filename.toLowerCase().endsWith(".dsk")
|
||||||
|
|| filename.toLowerCase().endsWith(".dsk.gz");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if this disk is a 2IMG disk.
|
||||||
|
* This is ProDOS ordered, but with a header on the disk.
|
||||||
|
*/
|
||||||
|
public boolean is2ImgOrder() {
|
||||||
|
return filename.toLowerCase().endsWith(".2img")
|
||||||
|
|| filename.toLowerCase().endsWith(".2img.gz")
|
||||||
|
|| filename.toLowerCase().endsWith(".2mg")
|
||||||
|
|| filename.toLowerCase().endsWith(".2mg.gz");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the size of this disk.
|
||||||
|
*/
|
||||||
|
public int getPhysicalSize() {
|
||||||
|
return diskImage.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block from the disk image.
|
||||||
|
*/
|
||||||
|
public byte[] readBlock(int block) {
|
||||||
|
if (block * BLOCK_SIZE > getPhysicalSize()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
if (isProdosOrder()) {
|
||||||
|
return readBytes(block*BLOCK_SIZE, BLOCK_SIZE);
|
||||||
|
} else if (isDosOrder()) {
|
||||||
|
int[] sectorMapping1 = { 0, 13, 11, 9, 7, 5, 3, 1 };
|
||||||
|
int[] sectorMapping2 = { 14, 12, 10, 8, 6, 4, 2, 15 };
|
||||||
|
int track = block / 8;
|
||||||
|
int sectorOffset = block % 8;
|
||||||
|
int sector1 = sectorMapping1[sectorOffset];
|
||||||
|
int sector2 = sectorMapping2[sectorOffset];
|
||||||
|
int physicalLocation1 = (track * 16 + sector1) * SECTOR_SIZE;
|
||||||
|
int physicalLocation2 = (track * 16 + sector2) * SECTOR_SIZE;
|
||||||
|
byte[] data = new byte[BLOCK_SIZE];
|
||||||
|
System.arraycopy(readBytes(physicalLocation1, SECTOR_SIZE),
|
||||||
|
0, data, 0, SECTOR_SIZE);
|
||||||
|
System.arraycopy(readBytes(physicalLocation2, SECTOR_SIZE),
|
||||||
|
0, data, SECTOR_SIZE, SECTOR_SIZE);
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the specified sector.
|
||||||
|
*/
|
||||||
|
public byte[] readSector(int track, int sector) {
|
||||||
|
if ((track * 16 + sector) * SECTOR_SIZE > getPhysicalSize()) {
|
||||||
|
return null;
|
||||||
|
} else if (isProdosOrder()) {
|
||||||
|
// what block a sector belongs to:
|
||||||
|
int[] blockInterleave = { 0, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 7 };
|
||||||
|
// where in that block a sector resides:
|
||||||
|
int[] blockOffsets = { 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 };
|
||||||
|
return readBytes(
|
||||||
|
((track * 8) + blockInterleave[sector]) * BLOCK_SIZE
|
||||||
|
+ blockOffsets[sector] * SECTOR_SIZE, SECTOR_SIZE);
|
||||||
|
} else if (isDosOrder()) {
|
||||||
|
return readBytes((track * 16 + sector) * SECTOR_SIZE, SECTOR_SIZE);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the disk format to see if this is a ProDOS formatted
|
||||||
|
* disk.
|
||||||
|
*/
|
||||||
|
public boolean isProdosFormat() {
|
||||||
|
byte[] prodosVolumeDirectory = readBlock(2);
|
||||||
|
return prodosVolumeDirectory[0] == 0 &&
|
||||||
|
prodosVolumeDirectory[1] == 0 &&
|
||||||
|
(prodosVolumeDirectory[4]&0xf0) == 0xf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the disk format to see if this is a DOS 3.3 formatted
|
||||||
|
* disk.
|
||||||
|
*/
|
||||||
|
public boolean isDosFormat() {
|
||||||
|
byte[] vtoc = readSector(17, 0);
|
||||||
|
return vtoc[0x01] == 17 // expect catalog to start on track 17
|
||||||
|
&& vtoc[0x02] == 15 // expect catalog to start on sector 15
|
||||||
|
&& vtoc[0x03] == 3 // expect DOS release number of 3
|
||||||
|
&& vtoc[0x27] == 122 // expect 122 tract/sector pairs per sector
|
||||||
|
&& vtoc[0x34] == 35 // expect 35 tracks per disk (140KB disk only!)
|
||||||
|
&& vtoc[0x35] == 16 // expect 16 sectors per disk (140KB disk only!)
|
||||||
|
&& vtoc[0x36] == 0 // bytes per sector (low byte)
|
||||||
|
&& vtoc[0x37] == 1; // bytes per sector (high byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the disk format to see if this is a Pascal formatted
|
||||||
|
* disk.
|
||||||
|
*/
|
||||||
|
public boolean isPascalFormat() {
|
||||||
|
byte[] directory = readBlock(2);
|
||||||
|
return directory[0] == 0 && directory[1] == 0
|
||||||
|
&& directory[2] == 6 && directory[3] == 0
|
||||||
|
&& directory[4] == 0 && directory[5] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the disk format to see if this is a RDOS formatted
|
||||||
|
* disk.
|
||||||
|
*/
|
||||||
|
public boolean isRdosFormat() {
|
||||||
|
byte[] block = readSector(0, 0x0d);
|
||||||
|
String id = AppleUtil.getString(block, 0xe0, 4);
|
||||||
|
return "RDOS".equals(id);
|
||||||
|
}
|
||||||
|
}
|
89
src/com/webcodepro/applecommander/storage/DiskHelper.java
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to load Apple2 disk images.
|
||||||
|
* This class identifies the format, and the type of disk, loading the
|
||||||
|
* appropriate disk management class.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 3, 2002 10:54:34 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public class DiskHelper {
|
||||||
|
public static final int APPLE_140KB_DISK = 143360;
|
||||||
|
public static final int APPLE_800KB_DISK = 819200;
|
||||||
|
public static final int APPLE_800KB_2IMG_DISK = APPLE_800KB_DISK + 0x40;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an Apple2 disk from a given filename.
|
||||||
|
* Determine type of disk, load approparitely, and
|
||||||
|
* construct the appropriate Disk object.
|
||||||
|
*/
|
||||||
|
public static FormattedDisk load(String filename) throws IOException {
|
||||||
|
if (filename == null) return null;
|
||||||
|
byte[] diskImage = loadDisk(filename);
|
||||||
|
|
||||||
|
Disk test = new Disk(filename, diskImage);
|
||||||
|
if (test.isProdosFormat()) {
|
||||||
|
return new ProdosFormatDisk(filename, diskImage);
|
||||||
|
} else if (test.isDosFormat()) {
|
||||||
|
return new DosFormatDisk(filename, diskImage);
|
||||||
|
} else if (test.isPascalFormat()) {
|
||||||
|
return new PascalFormatDisk(filename, diskImage);
|
||||||
|
} else if (test.isRdosFormat()) {
|
||||||
|
return new RdosFormatDisk(filename, diskImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Should return unknown disk Disk
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read in a disk in the same order as the image.
|
||||||
|
* Disk itself will handle location translation.
|
||||||
|
*/
|
||||||
|
private static byte[] loadDisk(String filename) throws IOException {
|
||||||
|
InputStream input = new FileInputStream(filename);
|
||||||
|
if (filename.toLowerCase().endsWith(".gz")) {
|
||||||
|
input = new GZIPInputStream(input);
|
||||||
|
}
|
||||||
|
int diskSize = APPLE_140KB_DISK;
|
||||||
|
if (filename.toLowerCase().endsWith(".2img") || filename.toLowerCase().endsWith(".2mg")) {
|
||||||
|
diskSize = APPLE_800KB_2IMG_DISK;
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream diskImage =
|
||||||
|
new ByteArrayOutputStream(diskSize);
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
int bytes;
|
||||||
|
while ((bytes = input.read(data)) > 0) {
|
||||||
|
diskImage.write(data, 0, bytes);
|
||||||
|
}
|
||||||
|
input.close();
|
||||||
|
return diskImage.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
271
src/com/webcodepro/applecommander/storage/DosFileEntry.java
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a DOS file entry on disk.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 4, 2002 5:15:25 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DosFileEntry implements FileEntry {
|
||||||
|
private byte[] fileEntry;
|
||||||
|
private DosFormatDisk disk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for DosFileEntry.
|
||||||
|
*/
|
||||||
|
public DosFileEntry(byte[] fileEntry, DosFormatDisk disk) {
|
||||||
|
super();
|
||||||
|
this.fileEntry = fileEntry;
|
||||||
|
this.disk = disk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this file.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileEntry#getFilename()
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
byte[] filename = new byte[30];
|
||||||
|
System.arraycopy(fileEntry, 3, filename, 0, filename.length);
|
||||||
|
for (int i=0; i<filename.length; i++) {
|
||||||
|
filename[i] &= 0x7f;
|
||||||
|
}
|
||||||
|
return new String(filename).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the filetype of this file.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileEntry#getFiletype()
|
||||||
|
*/
|
||||||
|
public String getFiletype() {
|
||||||
|
int filetype = (AppleUtil.getUnsignedByte(fileEntry[2]) & 0x7f);
|
||||||
|
if (filetype == 0x00) return "T";
|
||||||
|
// the "^" operator is exclusive or - used to ensure only that
|
||||||
|
// bit was turned on. if others are turned on, fall through and
|
||||||
|
// return a "?" as the file type
|
||||||
|
if ((filetype ^ 0x01) == 0) return "I";
|
||||||
|
if ((filetype ^ 0x02) == 0) return "A";
|
||||||
|
if ((filetype ^ 0x04) == 0) return "B";
|
||||||
|
if ((filetype ^ 0x08) == 0) return "S";
|
||||||
|
if ((filetype ^ 0x10) == 0) return "R";
|
||||||
|
if ((filetype ^ 0x20) == 0) return "a";
|
||||||
|
if ((filetype ^ 0x40) == 0) return "b";
|
||||||
|
return "?"; // should never occur (read the code!)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file is locked.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileEntry#isLocked()
|
||||||
|
*/
|
||||||
|
public boolean isLocked() {
|
||||||
|
return (fileEntry[2] & 0x80) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the size of this file (in bytes).
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileEntry#getSize()
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
byte[] rawdata = null;
|
||||||
|
if (!isDeleted()) {
|
||||||
|
rawdata = disk.getFileData(this);
|
||||||
|
}
|
||||||
|
// default to nothing special, just compute from number of sectors
|
||||||
|
int size = (getSectorsUsed()-1) * Disk.SECTOR_SIZE;
|
||||||
|
if (rawdata != null) {
|
||||||
|
if ("B".equals(getFiletype())) {
|
||||||
|
// binary
|
||||||
|
return AppleUtil.getWordValue(rawdata, 2);
|
||||||
|
} else if ("A".equals(getFiletype()) || "I".equals(getFiletype())) {
|
||||||
|
// applesoft, integer basic
|
||||||
|
return AppleUtil.getWordValue(rawdata, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the number of sectors used.
|
||||||
|
*/
|
||||||
|
public int getSectorsUsed() {
|
||||||
|
return AppleUtil.getUnsignedByte(fileEntry[0x21])
|
||||||
|
+ AppleUtil.getUnsignedByte(fileEntry[0x22])*16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this is a directory file.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileEntry#isDirectory()
|
||||||
|
*/
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the list of files in this directory.
|
||||||
|
* Always returns null for DOS.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileEntry#getFiles()
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file has been deleted.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileEntry#isDeleted()
|
||||||
|
*/
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return AppleUtil.getUnsignedByte(fileEntry[0]) == 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
* displayMode is specified in FormattedDisk.
|
||||||
|
*/
|
||||||
|
public List getFileColumnData(int displayMode) {
|
||||||
|
NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
||||||
|
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FormattedDisk.FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(isLocked() ? "*" : " ");
|
||||||
|
list.add(getFiletype());
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getSectorsUsed()));
|
||||||
|
list.add(getFilename());
|
||||||
|
break;
|
||||||
|
case FormattedDisk.FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(isLocked() ? "*" : " ");
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(getFilename());
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getSectorsUsed()));
|
||||||
|
list.add(isDeleted() ? "Deleted" : "");
|
||||||
|
list.add("T" + getTrack() + " S" + getSector());
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.add(getFilename());
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
list.add(isLocked() ? "Locked" : "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the track of first track/sector list sector.
|
||||||
|
*/
|
||||||
|
public int getTrack() {
|
||||||
|
return AppleUtil.getUnsignedByte(fileEntry[0x00]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sector of first track/sector list sector.
|
||||||
|
*/
|
||||||
|
public int getSector() {
|
||||||
|
return AppleUtil.getUnsignedByte(fileEntry[0x01]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file data. This handles any operating-system specific issues.
|
||||||
|
* Specifically, DOS 3.3 places address and length into binary files
|
||||||
|
* and length into Applesoft files.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData() {
|
||||||
|
byte[] rawdata = disk.getFileData(this);
|
||||||
|
byte[] filedata;
|
||||||
|
if (isBinaryFile()) {
|
||||||
|
int length = AppleUtil.getWordValue(rawdata, 2);
|
||||||
|
filedata = new byte[length];
|
||||||
|
System.arraycopy(rawdata, 4, filedata, 0, length);
|
||||||
|
} else if (isApplesoftBasicFile() || isIntegerBasicFile()) {
|
||||||
|
filedata = new byte[getSize()];
|
||||||
|
System.arraycopy(rawdata, 2, filedata, 0, filedata.length);
|
||||||
|
} else {
|
||||||
|
filedata = rawdata;
|
||||||
|
}
|
||||||
|
return filedata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the suggested FileFilter. This appears to be operating system
|
||||||
|
* specific, so each operating system needs to implement some manner
|
||||||
|
* of guessing the appropriate filter.
|
||||||
|
* FIXME - this code should be a helper class for DOS and RDOS!
|
||||||
|
*/
|
||||||
|
public FileFilter getSuggestedFilter() {
|
||||||
|
if (isApplesoftBasicFile()) {
|
||||||
|
return new ApplesoftFileFilter();
|
||||||
|
} else if (isIntegerBasicFile()) {
|
||||||
|
return new IntegerBasicFileFilter();
|
||||||
|
} else if (isTextFile()) {
|
||||||
|
return new TextFileFilter();
|
||||||
|
} else if (isBinaryFile()) {
|
||||||
|
int size = getSize();
|
||||||
|
// the minimum size is guessed a bit - I don't remember, but maybe there
|
||||||
|
// are 8 spare bytes at the end of the graphics screen
|
||||||
|
GraphicsFileFilter filter = new GraphicsFileFilter();
|
||||||
|
if (size >= 8185 && size <= 8192) {
|
||||||
|
filter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
|
||||||
|
return filter;
|
||||||
|
} else if (size >= 16377 && size <= 16384) {
|
||||||
|
filter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
// fall through to BinaryFileFilter...
|
||||||
|
}
|
||||||
|
return new BinaryFileFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is a text file.
|
||||||
|
*/
|
||||||
|
public boolean isTextFile() {
|
||||||
|
return "T".equals(getFiletype());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is an Applesoft BASIC file.
|
||||||
|
*/
|
||||||
|
public boolean isApplesoftBasicFile() {
|
||||||
|
return "A".equals(getFiletype());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is an Integer BASIC file.
|
||||||
|
*/
|
||||||
|
public boolean isIntegerBasicFile() {
|
||||||
|
return "I".equals(getFiletype());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is a binary file.
|
||||||
|
*/
|
||||||
|
public boolean isBinaryFile() {
|
||||||
|
return "B".equals(getFiletype());
|
||||||
|
}
|
||||||
|
}
|
398
src/com/webcodepro/applecommander/storage/DosFormatDisk.java
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages a disk that is in Apple DOS 3.3 format.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 4, 2002 12:29:23 AM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DosFormatDisk extends FormattedDisk {
|
||||||
|
/**
|
||||||
|
* Indicates the length in bytes of the DOS file entry field.
|
||||||
|
*/
|
||||||
|
public static final int FILE_DESCRIPTIVE_ENTRY_LENGTH = 35;
|
||||||
|
/**
|
||||||
|
* Indicates the index of the track in the location array.
|
||||||
|
*/
|
||||||
|
public static final int TRACK_LOCATION_INDEX = 0;
|
||||||
|
/**
|
||||||
|
* Indicates the index of the sector in the location array.
|
||||||
|
*/
|
||||||
|
public static final int SECTOR_LOCATION_INDEX = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this inner interface for managing the disk usage data.
|
||||||
|
* This offloads format-specific implementation to the implementing class.
|
||||||
|
*/
|
||||||
|
private class DosDiskUsage implements DiskUsage {
|
||||||
|
private int[] location = null;
|
||||||
|
public boolean hasNext() {
|
||||||
|
return location == null
|
||||||
|
|| (location[TRACK_LOCATION_INDEX] < getTracks()
|
||||||
|
&& location[SECTOR_LOCATION_INDEX] < getSectors());
|
||||||
|
}
|
||||||
|
public void next() {
|
||||||
|
if (location == null) {
|
||||||
|
location = new int[2];
|
||||||
|
} else {
|
||||||
|
location[SECTOR_LOCATION_INDEX]++;
|
||||||
|
if (location[SECTOR_LOCATION_INDEX] >= getSectors()) {
|
||||||
|
location[SECTOR_LOCATION_INDEX] = 0;
|
||||||
|
location[TRACK_LOCATION_INDEX]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the free setting for the bitmap at the current location.
|
||||||
|
*/
|
||||||
|
public boolean isFree() {
|
||||||
|
if (location == null || location.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Invalid dimension for isFree! Did you call next first?");
|
||||||
|
}
|
||||||
|
byte[] vtoc = getVtoc();
|
||||||
|
byte byt = vtoc[0x38 + (location[TRACK_LOCATION_INDEX] * 4)
|
||||||
|
+ (location[SECTOR_LOCATION_INDEX] / 8)];
|
||||||
|
boolean free = AppleUtil.isBitSet(byt, 7 - (location[SECTOR_LOCATION_INDEX] % 8));
|
||||||
|
return free;
|
||||||
|
}
|
||||||
|
public boolean isUsed() {
|
||||||
|
return !isFree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for DosFormatDisk.
|
||||||
|
* @param filename
|
||||||
|
* @param diskImage
|
||||||
|
* @param order
|
||||||
|
*/
|
||||||
|
public DosFormatDisk(String filename, byte[] diskImage) {
|
||||||
|
super(filename, diskImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the operating system format of this disk as DOS 3.3.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFormat()
|
||||||
|
*/
|
||||||
|
public String getFormat() {
|
||||||
|
return "DOS 3.3";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a list of files.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFiles()
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
List list = new ArrayList();
|
||||||
|
byte[] vtoc = getVtoc();
|
||||||
|
int track = AppleUtil.getUnsignedByte(vtoc[1]);
|
||||||
|
int sector = AppleUtil.getUnsignedByte(vtoc[2]);
|
||||||
|
while (track != 0) { // iterate through all catalog sectors
|
||||||
|
byte[] catalogSector = readSector(track, sector);
|
||||||
|
int offset = 0x0b;
|
||||||
|
while (offset < 0xff) { // iterate through all entries
|
||||||
|
byte[] entry = new byte[FILE_DESCRIPTIVE_ENTRY_LENGTH];
|
||||||
|
System.arraycopy(catalogSector, offset, entry, 0, entry.length);
|
||||||
|
if (entry[0] != 0) {
|
||||||
|
list.add(new DosFileEntry(entry, this));
|
||||||
|
}
|
||||||
|
offset+= entry.length;
|
||||||
|
}
|
||||||
|
track = catalogSector[1];
|
||||||
|
sector = catalogSector[2];
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this disk format as not capable of having directories.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#hasDirectories()
|
||||||
|
*/
|
||||||
|
public boolean canHaveDirectories() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the amount of freespace available on the disk.
|
||||||
|
* This algorithm completely ignores tracks and sectors by
|
||||||
|
* running through the entire bitmap stored on the VTOC.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFreeSpace()
|
||||||
|
*/
|
||||||
|
public int getFreeSpace() {
|
||||||
|
return getFreeSectors() * SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comput the number of free sectors available on the disk.
|
||||||
|
*/
|
||||||
|
public int getFreeSectors() {
|
||||||
|
byte[] vtoc = getVtoc();
|
||||||
|
int freeSectors = 0;
|
||||||
|
for (int offset=0x38; offset<0xff; offset++) {
|
||||||
|
byte bitmap = vtoc[offset];
|
||||||
|
freeSectors+= AppleUtil.getBitCount(bitmap);
|
||||||
|
}
|
||||||
|
return freeSectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of used space in bytes.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getUsedSpace()
|
||||||
|
*/
|
||||||
|
public int getUsedSpace() {
|
||||||
|
return getUsedSectors() * SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the number of used sectors on the disk.
|
||||||
|
*/
|
||||||
|
public int getUsedSectors() {
|
||||||
|
return getTotalSectors() - getFreeSectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the total number of sectors available on the disk.
|
||||||
|
*/
|
||||||
|
public int getTotalSectors() {
|
||||||
|
int tracks = getTracks();
|
||||||
|
int sectors = getSectors();
|
||||||
|
return tracks * sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the DOS disk name. Basically, the DISK VOLUME #xxx
|
||||||
|
* that a CATALOG command would show. Note that Java bytes are
|
||||||
|
* signed, so a little mojo is in order.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getDiskName()
|
||||||
|
*/
|
||||||
|
public String getDiskName() {
|
||||||
|
int volumeNumber = AppleUtil.getUnsignedByte(getVtoc()[0x06]);
|
||||||
|
return "DISK VOLUME #" + volumeNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the VTOC (Volume Table Of Contents).
|
||||||
|
*/
|
||||||
|
protected byte[] getVtoc() {
|
||||||
|
return readSector(0x11, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the disk usage bitmap. The size could vary and is stored in the
|
||||||
|
* VTOC.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FormattedDisk#getBitmap()
|
||||||
|
* @deprecated DOS 3.3.po comes up with 448 entries in the bitmap?!
|
||||||
|
*/
|
||||||
|
public BitSet getBitmap() {
|
||||||
|
byte[] vtoc = getVtoc();
|
||||||
|
int tracks = getTracks();
|
||||||
|
int sectors = getSectors();
|
||||||
|
BitSet bitmap = new BitSet(tracks * sectors);
|
||||||
|
// individually test each track & sector - should handle 140K or 400K disks!
|
||||||
|
int count = 0;
|
||||||
|
for (int t=0; t<tracks; t++) {
|
||||||
|
for (int s=0; s<sectors; s++) {
|
||||||
|
byte byt = vtoc[0x38 + (t * 4) + (s / 8)];
|
||||||
|
boolean free = AppleUtil.isBitSet(byt, 7 - (s % 8));
|
||||||
|
bitmap.set(count, free);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the free setting for the bitmap at a specific location.
|
||||||
|
* The location is specified by an int array to support block
|
||||||
|
* and track/sector formatted disks.
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public boolean isLocationFree(int[] location) {
|
||||||
|
if (location == null || location.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Invalid dimension for isLocationFree!");
|
||||||
|
}
|
||||||
|
byte[] vtoc = getVtoc();
|
||||||
|
byte byt = vtoc[0x38 + (location[TRACK_LOCATION_INDEX] * 4)
|
||||||
|
+ (location[SECTOR_LOCATION_INDEX] / 8)];
|
||||||
|
boolean free = AppleUtil.isBitSet(byt, 7 - (location[SECTOR_LOCATION_INDEX] % 8));
|
||||||
|
return free;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the disk usage iterator.
|
||||||
|
*/
|
||||||
|
public DiskUsage getDiskUsage() {
|
||||||
|
return new DosDiskUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of tracks on this disk.
|
||||||
|
*/
|
||||||
|
public int getTracks() {
|
||||||
|
byte[] vtoc = getVtoc();
|
||||||
|
return AppleUtil.getUnsignedByte(vtoc[0x34]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of sectors on this disk.
|
||||||
|
*/
|
||||||
|
public int getSectors() {
|
||||||
|
byte[] vtoc = getVtoc();
|
||||||
|
return AppleUtil.getUnsignedByte(vtoc[0x35]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get suggested dimensions for display of bitmap. For DOS 3.3, that information
|
||||||
|
* is stored in the VTOC, and that information is fairly important.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FormattedDisk#getBitmapDimensions()
|
||||||
|
*/
|
||||||
|
public int[] getBitmapDimensions() {
|
||||||
|
int tracks = getTracks();
|
||||||
|
int sectors = getSectors();
|
||||||
|
return new int[] { tracks, sectors };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of the bitmap.
|
||||||
|
*/
|
||||||
|
public int getBitmapLength() {
|
||||||
|
return getTotalSectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the labels to use in the bitmap.
|
||||||
|
*/
|
||||||
|
public String[] getBitmapLabels() {
|
||||||
|
return new String[] { "Track", "Sector" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get DOS-specific disk information.
|
||||||
|
*/
|
||||||
|
public List getDiskInformation() {
|
||||||
|
List list = super.getDiskInformation();
|
||||||
|
list.add(new DiskInformation("Total Sectors", getTotalSectors()));
|
||||||
|
list.add(new DiskInformation("Free Sectors", getFreeSectors()));
|
||||||
|
list.add(new DiskInformation("Used Sectors", getUsedSectors()));
|
||||||
|
list.add(new DiskInformation("Tracks On Disk", getTracks()));
|
||||||
|
list.add(new DiskInformation("Sectors On Disk", getSectors()));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
*/
|
||||||
|
public List getFileColumnHeaders(int displayMode) {
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Size (sectors)", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
break;
|
||||||
|
case FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Size (bytes)", 6, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Size (sectors)", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Deleted?", 7, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Track/Sector List", 7, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.addAll(super.getFileColumnHeaders(displayMode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk format supports "deleted" files.
|
||||||
|
*/
|
||||||
|
public boolean supportsDeletedFiles() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can read data from a file.
|
||||||
|
*/
|
||||||
|
public boolean canReadFileData() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can write data to a file.
|
||||||
|
*/
|
||||||
|
public boolean canWriteFileData() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can create a file.
|
||||||
|
*/
|
||||||
|
public boolean canCreateFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can delete a file.
|
||||||
|
*/
|
||||||
|
public boolean canDeleteFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data associated with the specified FileEntry.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData(FileEntry fileEntry) {
|
||||||
|
if ( !(fileEntry instanceof DosFileEntry)) {
|
||||||
|
throw new IllegalArgumentException("Most have a DOS 3.3 file entry!");
|
||||||
|
}
|
||||||
|
DosFileEntry dosEntry = (DosFileEntry) fileEntry;
|
||||||
|
// Size is calculated by sectors used - not actual size - as size varies
|
||||||
|
// on filetype, etc.
|
||||||
|
byte[] fileData = new byte[(dosEntry.getSectorsUsed()-1) * SECTOR_SIZE];
|
||||||
|
int track = dosEntry.getTrack();
|
||||||
|
int sector = dosEntry.getSector();
|
||||||
|
int offset = 0;
|
||||||
|
while (track != 0) {
|
||||||
|
byte[] trackSectorList = readSector(track, sector);
|
||||||
|
track = AppleUtil.getUnsignedByte(trackSectorList[0x01]);
|
||||||
|
sector = AppleUtil.getUnsignedByte(trackSectorList[0x02]);
|
||||||
|
for (int i=0x0c; i<0x100; i+=2) {
|
||||||
|
int t = AppleUtil.getUnsignedByte(trackSectorList[i]);
|
||||||
|
if (t == 0) break;
|
||||||
|
int s = AppleUtil.getUnsignedByte(trackSectorList[i+1]);
|
||||||
|
byte[] sectorData = readSector(t,s);
|
||||||
|
System.arraycopy(sectorData, 0, fileData, offset, sectorData.length);
|
||||||
|
offset+= sectorData.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
}
|
91
src/com/webcodepro/applecommander/storage/FileEntry.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a file entry on disk - not the data.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 4, 2002 4:46:42 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public interface FileEntry {
|
||||||
|
/**
|
||||||
|
* Return the name of this file.
|
||||||
|
*/
|
||||||
|
public String getFilename();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the filetype of this file.
|
||||||
|
* This will be OS specific.
|
||||||
|
*/
|
||||||
|
public String getFiletype();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file is locked.
|
||||||
|
*/
|
||||||
|
public boolean isLocked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the size of this file (in bytes).
|
||||||
|
*/
|
||||||
|
public int getSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this is a directory file.
|
||||||
|
*/
|
||||||
|
public boolean isDirectory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the list of files in this directory.
|
||||||
|
* Note that if this is not a directory, the return
|
||||||
|
* value should be null. If this a directory, the
|
||||||
|
* return value should always be a list - a directory
|
||||||
|
* with 0 entries returns an empty list.
|
||||||
|
*/
|
||||||
|
public List getFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file has been deleted.
|
||||||
|
*/
|
||||||
|
public boolean isDeleted();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
* displayMode is specified in FormattedDisk.
|
||||||
|
*/
|
||||||
|
public List getFileColumnData(int displayMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file data. This handles any operating-system specific issues.
|
||||||
|
* Specifically, DOS 3.3 places address and length into binary files
|
||||||
|
* and length into Applesoft files.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the suggested FileFilter. This appears to be operating system
|
||||||
|
* specific, so each operating system needs to implement some manner
|
||||||
|
* of guessing the appropriate filter.
|
||||||
|
*/
|
||||||
|
public FileFilter getSuggestedFilter();
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort FileEntry objects by the columnIndex. Mostly useful to the interface.
|
||||||
|
* The columns are tested to see if they are numerical - if so, an Integer compare
|
||||||
|
* is done (instead of a String).
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 27, 2002 8:24:39 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class FileEntryComparator implements Comparator {
|
||||||
|
private int columnIndex;
|
||||||
|
private int displayMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a FileEntryComparator for the given columnIndex.
|
||||||
|
*/
|
||||||
|
public FileEntryComparator(int columnIndex, int displayMode) {
|
||||||
|
this.columnIndex = columnIndex;
|
||||||
|
this.displayMode = displayMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two FileEntry objects.
|
||||||
|
* @see java.util.Comparator#compare(Object, Object)
|
||||||
|
*/
|
||||||
|
public int compare(Object o1, Object o2) {
|
||||||
|
if (!(o1 instanceof FileEntry) || !(o2 instanceof FileEntry)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o1 == null || o2 == null) {
|
||||||
|
return ((o1 == null) ? -1 : 0) + ((o2 == null) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileEntry entry1 = (FileEntry) o1;
|
||||||
|
FileEntry entry2 = (FileEntry) o2;
|
||||||
|
|
||||||
|
String column1 = (String) entry1.getFileColumnData(displayMode).get(columnIndex);
|
||||||
|
String column2 = (String) entry2.getFileColumnData(displayMode).get(columnIndex);
|
||||||
|
|
||||||
|
if (isAllDigits(column1) && isAllDigits(column2)) {
|
||||||
|
int int1 = toInt(column1);
|
||||||
|
int int2 = toInt(column2);
|
||||||
|
return int1 - int2;
|
||||||
|
} else {
|
||||||
|
return column1.compareTo(column2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for digits in the screen.
|
||||||
|
*/
|
||||||
|
protected boolean isAllDigits(String string) {
|
||||||
|
if (string == null || string.length() == 0) return false;
|
||||||
|
for (int i=0; i<string.length(); i++) {
|
||||||
|
char ch = string.charAt(i);
|
||||||
|
if (!Character.isDigit(ch) && ch != ',') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert String to int.
|
||||||
|
*/
|
||||||
|
protected int toInt(String string) {
|
||||||
|
StringBuffer buf = new StringBuffer(string.length());
|
||||||
|
for (int i=0; i<string.length(); i++) {
|
||||||
|
char ch = string.charAt(i);
|
||||||
|
if (Character.isDigit(ch)) {
|
||||||
|
buf.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Integer.parseInt(buf.toString());
|
||||||
|
}
|
||||||
|
}
|
40
src/com/webcodepro/applecommander/storage/FileFilter.java
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file filter taks a buffer of bytes and filters or converts the bytes
|
||||||
|
* into another format. An example would be to filter Apple text by
|
||||||
|
* removing the high bit from each byte and stripping out all $00 values,
|
||||||
|
* as that signified either the end of a file or filler space.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 2, 2002 9:02:47 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public interface FileFilter {
|
||||||
|
/**
|
||||||
|
* Process the given FileEntry and return a byte array with filtered data.
|
||||||
|
*/
|
||||||
|
public byte[] filter(FileEntry fileEntry);
|
||||||
|
/**
|
||||||
|
* Give suggested file name.
|
||||||
|
*/
|
||||||
|
public String getSuggestedFileName(FileEntry fileEntry);
|
||||||
|
}
|
299
src/com/webcodepro/applecommander/storage/FormattedDisk.java
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract representation of a formatted Apple2 disk (floppy, 800k, hard disk).
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 3:51:44 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public abstract class FormattedDisk extends Disk {
|
||||||
|
/**
|
||||||
|
* Use this inner class for label/value mappings in the disk info page.
|
||||||
|
*/
|
||||||
|
public class DiskInformation {
|
||||||
|
private String label;
|
||||||
|
private String value;
|
||||||
|
public DiskInformation(String label, String value) {
|
||||||
|
this.label = label;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
public DiskInformation(String label, int value) {
|
||||||
|
this.label = label;
|
||||||
|
this.value = Integer.toString(value);
|
||||||
|
}
|
||||||
|
public DiskInformation(String label, Date value) {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
|
||||||
|
this.label = label;
|
||||||
|
if (value != null) {
|
||||||
|
this.value = dateFormat.format(value);
|
||||||
|
} else {
|
||||||
|
this.value = "-None-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public String getLabel() {
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this inner interface for managing the disk usage data.
|
||||||
|
* This offloads format-specific implementation to the implementing class.
|
||||||
|
* The usage is very similar to a Java2 Iterator - next must be called to
|
||||||
|
* set the value and isFree/isUsed are available for that location.
|
||||||
|
*/
|
||||||
|
public interface DiskUsage {
|
||||||
|
public boolean hasNext();
|
||||||
|
public void next();
|
||||||
|
public boolean isFree();
|
||||||
|
public boolean isUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This inner class represents the column header information used
|
||||||
|
* in the directory display. Note that this needs to be synchronized
|
||||||
|
* with the appropriate FileEntry objects.
|
||||||
|
*/
|
||||||
|
public static final int FILE_DISPLAY_STANDARD = 1;
|
||||||
|
public static final int FILE_DISPLAY_NATIVE = 2;
|
||||||
|
public static final int FILE_DISPLAY_DETAIL = 3;
|
||||||
|
public class FileColumnHeader {
|
||||||
|
public static final int ALIGN_LEFT = 1;
|
||||||
|
public static final int ALIGN_CENTER = 2;
|
||||||
|
public static final int ALIGN_RIGHT = 3;
|
||||||
|
private String title;
|
||||||
|
private int maximumWidth;
|
||||||
|
private int alignment;
|
||||||
|
public FileColumnHeader(String title, int maximumWidth, int alignment) {
|
||||||
|
this.title = title;
|
||||||
|
this.maximumWidth = maximumWidth;
|
||||||
|
this.alignment = alignment;
|
||||||
|
}
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
public int getMaximumWidth() {
|
||||||
|
return maximumWidth;
|
||||||
|
}
|
||||||
|
public int getAlignment() {
|
||||||
|
return alignment;
|
||||||
|
}
|
||||||
|
public boolean isLeftAlign() {
|
||||||
|
return alignment == ALIGN_LEFT;
|
||||||
|
}
|
||||||
|
public boolean isCenterAlign() {
|
||||||
|
return alignment == ALIGN_CENTER;
|
||||||
|
}
|
||||||
|
public boolean isRightAlign() {
|
||||||
|
return alignment == ALIGN_RIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for FormattedDisk.
|
||||||
|
* @param filename
|
||||||
|
* @param diskImage
|
||||||
|
*/
|
||||||
|
public FormattedDisk(String filename, byte[] diskImage) {
|
||||||
|
super(filename, diskImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this disk format is capable of having directories.
|
||||||
|
*/
|
||||||
|
public abstract boolean canHaveDirectories();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the disk. Not the physical file name,
|
||||||
|
* but "DISK VOLUME #xxx" (DOS 3.3) or "/MY.DISK" (ProDOS).
|
||||||
|
*/
|
||||||
|
public abstract String getDiskName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a list of files.
|
||||||
|
*/
|
||||||
|
public abstract List getFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the operating system format of this disk.
|
||||||
|
*/
|
||||||
|
public abstract String getFormat();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of free space in bytes.
|
||||||
|
*/
|
||||||
|
public abstract int getFreeSpace();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of used space in bytes.
|
||||||
|
*/
|
||||||
|
public abstract int getUsedSpace();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get suggested dimensions for display of bitmap.
|
||||||
|
* Typically, this will be only used for 5.25" floppies.
|
||||||
|
* This can return null if there is no suggestion.
|
||||||
|
*/
|
||||||
|
public abstract int[] getBitmapDimensions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of the bitmap.
|
||||||
|
*/
|
||||||
|
public abstract int getBitmapLength();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the disk usage iterator.
|
||||||
|
*/
|
||||||
|
public abstract DiskUsage getDiskUsage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the labels to use in the bitmap.
|
||||||
|
* Note that this should, at a minimum, return an array of
|
||||||
|
* String[1] unless the bitmap has not been implemented.
|
||||||
|
*/
|
||||||
|
public abstract String[] getBitmapLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get disk information. This is intended to be pretty generic -
|
||||||
|
* each disk format can build this as appropriate. Each subclass should
|
||||||
|
* override this method and add its own detail.
|
||||||
|
*/
|
||||||
|
public List getDiskInformation() {
|
||||||
|
List list = new ArrayList();
|
||||||
|
list.add(new DiskInformation("File Name", getFilename()));
|
||||||
|
list.add(new DiskInformation("Disk Name", getDiskName()));
|
||||||
|
list.add(new DiskInformation("Physical Size (bytes)", getPhysicalSize()));
|
||||||
|
list.add(new DiskInformation("Free Space (bytes)", getFreeSpace()));
|
||||||
|
list.add(new DiskInformation("Used Space (bytes)", getUsedSpace()));
|
||||||
|
list.add(new DiskInformation("Physical Size (KB)", getPhysicalSize() / 1024));
|
||||||
|
list.add(new DiskInformation("Free Space (KB)", getFreeSpace() / 1024));
|
||||||
|
list.add(new DiskInformation("Used Space (KB)", getUsedSpace() / 1024));
|
||||||
|
list.add(new DiskInformation("Archive Order",
|
||||||
|
is2ImgOrder() ? "2IMG" :
|
||||||
|
isDosOrder() ? "DOS 3.3" :
|
||||||
|
isProdosOrder() ? "ProDOS" : "Unknown"));
|
||||||
|
list.add(new DiskInformation("Disk Format", getFormat()));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
*/
|
||||||
|
public List getFileColumnHeaders(int displayMode) {
|
||||||
|
List list = new ArrayList();
|
||||||
|
list.add(new FileColumnHeader("Name", 30, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Type", 8, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Size (bytes)", 6, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Locked?", 6, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk format supports "deleted" files.
|
||||||
|
* Not to be confused with being able to delete a file, this indicates that
|
||||||
|
* deleted entries remain in the filesystem after the file has been deleted.
|
||||||
|
* There are some filesystems that "compress" the file out of the structure
|
||||||
|
* by completely removing the entry instead of marking it deleted (like
|
||||||
|
* Apple Pascal).
|
||||||
|
*/
|
||||||
|
public abstract boolean supportsDeletedFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can read data from a file.
|
||||||
|
* If not, the reason may be as simple as it has not beem implemented
|
||||||
|
* to something specific about the disk.
|
||||||
|
*/
|
||||||
|
public abstract boolean canReadFileData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can write data to a file.
|
||||||
|
* If not, the reason may be as simple as it has not beem implemented
|
||||||
|
* to something specific about the disk (such as read-only image).
|
||||||
|
*/
|
||||||
|
public abstract boolean canWriteFileData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can create a file.
|
||||||
|
* If not, the reason may be as simple as it has not beem implemented
|
||||||
|
* to something specific about the disk.
|
||||||
|
*/
|
||||||
|
public abstract boolean canCreateFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can delete a file.
|
||||||
|
* If not, the reason may be as simple as it has not beem implemented
|
||||||
|
* to something specific about the disk.
|
||||||
|
*/
|
||||||
|
public abstract boolean canDeleteFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data associated with the specified FileEntry.
|
||||||
|
* This is just the raw data. Use the FileEntry itself to read
|
||||||
|
* data appropriately! For instance, DOS "B" (binary) files store
|
||||||
|
* length and address as part of the file itself, but it is not treated
|
||||||
|
* as file data.
|
||||||
|
* @see FileEntry#getFileData()
|
||||||
|
*/
|
||||||
|
public abstract byte[] getFileData(FileEntry fileEntry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate a specific file by filename.
|
||||||
|
* Returns a null if specific filename is not located.
|
||||||
|
*/
|
||||||
|
public FileEntry getFile(String filename) {
|
||||||
|
List files = getFiles();
|
||||||
|
return getFile(files, filename.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive routine to locate a specific file by filename.
|
||||||
|
* Note that in the instance of a system with directories (ie, ProDOS),
|
||||||
|
* this really returns the first file with the given filename.
|
||||||
|
*/
|
||||||
|
protected FileEntry getFile(List files, String filename) {
|
||||||
|
FileEntry theFileEntry = null;
|
||||||
|
if (files != null) {
|
||||||
|
for (int i=0; i<files.size(); i++) {
|
||||||
|
FileEntry entry = (FileEntry) files.get(i);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
theFileEntry = getFile(entry.getFiles(), filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String otherFilename = entry.getFilename();
|
||||||
|
if (otherFilename != null) otherFilename = otherFilename.trim();
|
||||||
|
if (filename.equalsIgnoreCase(otherFilename)) {
|
||||||
|
theFileEntry = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return theFileEntry;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,402 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import com.sun.image.codec.jpeg.JPEGCodec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the given file as if it were a graphics image.
|
||||||
|
* <p>
|
||||||
|
* Address for Apple2 HGR/DHR address is calculated from an observation of a pattern:<br>
|
||||||
|
* line number bits: 87654321<br>
|
||||||
|
* 87 are multipled by 0x0028<br>
|
||||||
|
* 65 are multipled by 0x0100<br>
|
||||||
|
* 4 is multiplied by 0x0080<br>
|
||||||
|
* 321 are multipled by 0x0400
|
||||||
|
* <p>
|
||||||
|
* HGR bit values ignore the high bit, as that switches the "palette", and for B&W mode,
|
||||||
|
* the bit does nothing. The other 7 bits simply toggle the pixel on or off. Double hires
|
||||||
|
* does not follow this - it uses a real 4 bit value, but the high bit is still ignored for
|
||||||
|
* graphics (hence, the 560 instead of 640 resolution).
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 3, 2002 12:06:36 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class GraphicsFileFilter implements FileFilter {
|
||||||
|
public static final int MODE_HGR_BLACK_AND_WHITE = 1;
|
||||||
|
public static final int MODE_HGR_COLOR = 2;
|
||||||
|
public static final int MODE_DHR_BLACK_AND_WHITE = 3;
|
||||||
|
public static final int MODE_DHR_COLOR = 4;
|
||||||
|
|
||||||
|
private String extension;
|
||||||
|
private int mode = MODE_HGR_COLOR;
|
||||||
|
|
||||||
|
private static final int CODEC_NONE = 0; // disabled!
|
||||||
|
private static final int CODEC_IMAGEIO = 1; // JDK 1.4
|
||||||
|
private static final int CODEC_JPEGCODEC = 2; // SUN JDK's only
|
||||||
|
private int imageCodec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for GraphicsFileFilter.
|
||||||
|
*/
|
||||||
|
public GraphicsFileFilter() {
|
||||||
|
super();
|
||||||
|
determineImageCodec();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start guessing which codec is avilable for images.
|
||||||
|
*/
|
||||||
|
protected void determineImageCodec() {
|
||||||
|
try {
|
||||||
|
Class.forName("javax.imageio.ImageIO");
|
||||||
|
imageCodec = CODEC_IMAGEIO;
|
||||||
|
extension = "PNG";
|
||||||
|
return;
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
try {
|
||||||
|
Class.forName("com.sun.image.codec.jpeg.JPEGCodec");
|
||||||
|
imageCodec = CODEC_JPEGCODEC;
|
||||||
|
extension = "JPEG";
|
||||||
|
} catch (ClassNotFoundException ignored2) {
|
||||||
|
imageCodec = CODEC_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if a codec is available (assist with interface requirements).
|
||||||
|
*/
|
||||||
|
public boolean isCodecAvailable() {
|
||||||
|
return imageCodec != CODEC_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if the ImageIO codec is avilable.
|
||||||
|
*/
|
||||||
|
protected boolean isCodecImageIo() {
|
||||||
|
return imageCodec == CODEC_IMAGEIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if the SUN JPEG Codec is avilable.
|
||||||
|
*/
|
||||||
|
protected boolean isCodecJpegCodec() {
|
||||||
|
return imageCodec == CODEC_JPEGCODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the file data and produce an image.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileFilter#filter(FileEntry)
|
||||||
|
*/
|
||||||
|
public byte[] filter(FileEntry fileEntry) {
|
||||||
|
byte[] fileData = fileEntry.getFileData();
|
||||||
|
BufferedImage image = null;
|
||||||
|
if (isHiresColorMode()) {
|
||||||
|
image = new BufferedImage(280, 192, BufferedImage.TYPE_INT_RGB);
|
||||||
|
} else if (isDoubleHiresMode()) {
|
||||||
|
image = new BufferedImage(560, 192*2, BufferedImage.TYPE_INT_RGB);
|
||||||
|
} else {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
for (int y=0; y<192; y++) {
|
||||||
|
int base = ( // odd notation - bit value shifted right * hex value
|
||||||
|
((y & 0x7) << 10) // 00000111 * 0x0400
|
||||||
|
| (y & 0x8) << 4 // 00001000 * 0x0080
|
||||||
|
| (y & 0x30) << 4 // 00110000 * 0x0100
|
||||||
|
| ((y & 0xc0) >> 6) * 0x028 // 11000000 * 0x0028
|
||||||
|
) & 0x1fff;
|
||||||
|
byte[] lineData = new byte[40];
|
||||||
|
System.arraycopy(fileData, base, lineData, 0, 40);
|
||||||
|
if (isHiresBlackAndWhiteMode()) {
|
||||||
|
processHiresBlackAndWhiteLine(lineData, image, y);
|
||||||
|
} else if (isHiresColorMode()) {
|
||||||
|
processHiresColorLine(lineData, image, y);
|
||||||
|
} else if (isDoubleHiresMode()) {
|
||||||
|
byte[] lineData2 = new byte[40];
|
||||||
|
System.arraycopy(fileData, base + 0x2000, lineData2, 0, 40);
|
||||||
|
if (isDoubleHiresBlackAndWhiteMode()) {
|
||||||
|
processDoubleHiresBlackAndWhiteLine(lineData, lineData2, image, y);
|
||||||
|
} else if (isDoubleHiresColorMode()) {
|
||||||
|
processDoubleHiresColorLine(lineData, lineData2, image, y);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// oops...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
if (isCodecImageIo()) {
|
||||||
|
ImageIO.write(image, getExtension(), outputStream);
|
||||||
|
} else if (isCodecJpegCodec()) {
|
||||||
|
JPEGCodec.createJPEGEncoder(outputStream).encode(image);
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a specific line in the image, process it in hires black and white
|
||||||
|
* mode.
|
||||||
|
*/
|
||||||
|
protected void processHiresBlackAndWhiteLine(byte[] lineData, BufferedImage image, int y) {
|
||||||
|
for (int x=0; x<280; x++) {
|
||||||
|
int offset = x / 7; // byte across row
|
||||||
|
int bit = x % 7; // bit to test
|
||||||
|
byte byt = lineData[offset];
|
||||||
|
if (AppleUtil.isBitSet(byt, bit)) {
|
||||||
|
image.setRGB(x, y, 0xffffff);
|
||||||
|
} else {
|
||||||
|
image.setRGB(x, y, 0x0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a specific line in the image, process it in hires color mode.
|
||||||
|
* HGR color is two bits to determine color - essentially resolution is
|
||||||
|
* 140 horizontally, but it indicates the color for two pixels.
|
||||||
|
* <p>
|
||||||
|
* The names of pixles is a bit confusion - pixel0 is really the left-most
|
||||||
|
* pixel (not the low-value bit).
|
||||||
|
* To alleviate my bad naming, here is a color table to assist:<br>
|
||||||
|
* <pre>
|
||||||
|
* Color Bits RGB
|
||||||
|
* ======= ==== ========
|
||||||
|
* Black1 000 0x000000
|
||||||
|
* Green 001 0x00ff00
|
||||||
|
* Violet 010 0xff00ff
|
||||||
|
* White1 011 0xffffff
|
||||||
|
* Black2 100 0x000000
|
||||||
|
* Orange 101 0xff8000
|
||||||
|
* Blue 110 0x0000ff
|
||||||
|
* White2 111 0xffffff
|
||||||
|
* </pre>
|
||||||
|
* Remember: bits are listed as "highbit", "pixel0", "pixel1"!
|
||||||
|
*/
|
||||||
|
protected void processHiresColorLine(byte[] lineData, BufferedImage image, int y) {
|
||||||
|
for (int x=0; x<140; x++) {
|
||||||
|
int x0 = x*2;
|
||||||
|
int x1 = x0+1;
|
||||||
|
int offset0 = x0 / 7; // byte across row
|
||||||
|
int bit0 = x0 % 7; // bit to test
|
||||||
|
boolean pixel0 = AppleUtil.isBitSet(lineData[offset0], bit0);
|
||||||
|
int offset1 = x1 / 7; // byte across row
|
||||||
|
int bit1 = x1 % 7; // bit to test
|
||||||
|
boolean pixel1 = AppleUtil.isBitSet(lineData[offset1], bit1);
|
||||||
|
int color;
|
||||||
|
if (pixel0 && pixel1) {
|
||||||
|
color = 0xffffff; // white
|
||||||
|
} else if (!pixel0 && !pixel1) {
|
||||||
|
color = 0; // black
|
||||||
|
} else {
|
||||||
|
boolean highbit = pixel0 ? AppleUtil.isBitSet(lineData[offset0], 7) :
|
||||||
|
AppleUtil.isBitSet(lineData[offset1], 7);
|
||||||
|
if (pixel0 && highbit) {
|
||||||
|
color = 0x0000ff; // blue
|
||||||
|
} else if (pixel0 && !highbit) {
|
||||||
|
color = 0xff00ff; // voilet
|
||||||
|
} else if (pixel1 && !highbit) {
|
||||||
|
color = 0x00ff00; // green
|
||||||
|
} else { // pixel1 && highbit
|
||||||
|
color = 0xff8000; // orange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pixel0) image.setRGB(x0, y, color);
|
||||||
|
if (pixel1) image.setRGB(x1, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a specific line in the image, process it in double hires black and white
|
||||||
|
* mode.
|
||||||
|
*/
|
||||||
|
protected void processDoubleHiresBlackAndWhiteLine(byte[] lineData1, byte[] lineData2,
|
||||||
|
BufferedImage image, int y) {
|
||||||
|
|
||||||
|
for (int x=0; x<560; x++) {
|
||||||
|
// alternate bytes - switching memory banks
|
||||||
|
byte[] lineData = (x % 14 < 7) ? lineData1 : lineData2;
|
||||||
|
int rowOffset = x / 14; // byte across row
|
||||||
|
int bit = x % 7; // bit to test
|
||||||
|
byte byt = lineData[rowOffset];
|
||||||
|
if (AppleUtil.isBitSet(byt, bit)) {
|
||||||
|
image.setRGB(x, y*2, 0xffffff);
|
||||||
|
image.setRGB(x, y*2+1, 0xffffff);
|
||||||
|
} else {
|
||||||
|
image.setRGB(x, y*2, 0x0);
|
||||||
|
image.setRGB(x, y*2+1, 0x0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a specific line in the image, process it in double hires color
|
||||||
|
* mode. Treat image as 140x192 mode.
|
||||||
|
* <p>
|
||||||
|
* From the <a href='http://web.pdx.edu/~heiss/technotes/aiie/tn.aiie.03.html'>Apple2
|
||||||
|
* technical note:
|
||||||
|
* <pre>
|
||||||
|
* Repeated<br>
|
||||||
|
* Binary<br>
|
||||||
|
* Color aux1 main1 aux2 main2 Pattern<br>
|
||||||
|
* Black 00 00 00 00 0000<br>
|
||||||
|
* Magenta 08 11 22 44 0001<br>
|
||||||
|
* Brown 44 08 11 22 0010<br>
|
||||||
|
* Orange 4C 19 33 66 0011<br>
|
||||||
|
* Dark Green 22 44 08 11 0100<br>
|
||||||
|
* Grey1 2A 55 2A 55 0101<br>
|
||||||
|
* Green 66 4C 19 33 0110<br>
|
||||||
|
* Yellow 6E 5D 3B 77 0111<br>
|
||||||
|
* Dark Blue 11 22 44 08 1000<br>
|
||||||
|
* Violet 19 33 66 4C 1001<br>
|
||||||
|
* Grey2 55 2A 55 2A 1010<br>
|
||||||
|
* Pink 5D 3B 77 6E 1011<br>
|
||||||
|
* Medium Blue 33 66 4C 19 1100<br>
|
||||||
|
* Light Blue 3B 77 6E 5D 1101<br>
|
||||||
|
* Aqua 77 6E 5D 3B 1110<br>
|
||||||
|
* White 7F 7F 7F 7F 1111
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
protected void processDoubleHiresColorLine(byte[] lineData1, byte[] lineData2,
|
||||||
|
BufferedImage image, int y) {
|
||||||
|
|
||||||
|
int[] bitValues = { 8,4,2,1 };
|
||||||
|
int[] colorValues = {
|
||||||
|
0x000000, 0xff0000, 0x800000, 0xff8000, // black, magenta, brown, orange
|
||||||
|
0x008000, 0x808080, 0x00ff00, 0xffff00, // dark green, grey1, green, yellow
|
||||||
|
0x000080, 0xff00ff, 0x808080, 0xff80c0, // dark blue, voilet, grey2, pink
|
||||||
|
0x0000a0, 0x0000ff, 0x00c080, 0xffffff // medium blue, light blue, aqua, white
|
||||||
|
};
|
||||||
|
for (int x=0; x<560; x+=4) {
|
||||||
|
int colorValue = 0;
|
||||||
|
for (int b = 0; b < 4; b++) {
|
||||||
|
int xb = x+b;
|
||||||
|
// alternate bytes - switching memory banks
|
||||||
|
byte[] lineData = (xb % 14 < 7) ? lineData1 : lineData2;
|
||||||
|
int rowOffset = xb / 14; // byte across row
|
||||||
|
int bit = xb % 7; // bit to test
|
||||||
|
byte byt = lineData[rowOffset];
|
||||||
|
if (AppleUtil.isBitSet(byt, bit)) {
|
||||||
|
colorValue+= bitValues[b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int b = 0; b < 4; b++) {
|
||||||
|
image.setRGB(x+b, y*2, colorValues[colorValue]);
|
||||||
|
image.setRGB(x+b, y*2+1, colorValues[colorValue]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give file extensions.
|
||||||
|
*/
|
||||||
|
public String[] getFileExtensions() {
|
||||||
|
if (isCodecImageIo()) {
|
||||||
|
return new String[] { "PNG", "JPEG" };
|
||||||
|
} else if (isCodecJpegCodec()) {
|
||||||
|
return new String[] { "JPEG" };
|
||||||
|
} else {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give suggested file name.
|
||||||
|
*/
|
||||||
|
public String getSuggestedFileName(FileEntry fileEntry) {
|
||||||
|
String fileName = fileEntry.getFilename().trim();
|
||||||
|
if (!fileName.toLowerCase().endsWith("." + getExtension())) {
|
||||||
|
fileName = fileName + "." + getExtension();
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the format name.
|
||||||
|
*/
|
||||||
|
public void setExtension(String extension) {
|
||||||
|
this.extension = extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the format name.
|
||||||
|
*/
|
||||||
|
public String getExtension() {
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the color mode.
|
||||||
|
*/
|
||||||
|
public void setMode(int mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is configured for hires black & white mode.
|
||||||
|
*/
|
||||||
|
public boolean isHiresBlackAndWhiteMode() {
|
||||||
|
return mode == MODE_HGR_BLACK_AND_WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is configured for hires color mode.
|
||||||
|
*/
|
||||||
|
public boolean isHiresColorMode() {
|
||||||
|
return mode == MODE_HGR_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is configured for double hires black & white mode.
|
||||||
|
*/
|
||||||
|
public boolean isDoubleHiresBlackAndWhiteMode() {
|
||||||
|
return mode == MODE_DHR_BLACK_AND_WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is configured for double hires color mode.
|
||||||
|
*/
|
||||||
|
public boolean isDoubleHiresColorMode() {
|
||||||
|
return mode == MODE_DHR_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is a hires mode.
|
||||||
|
*/
|
||||||
|
protected boolean isHiresMode() {
|
||||||
|
return isHiresBlackAndWhiteMode() || isHiresColorMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is a double hires mode.
|
||||||
|
*/
|
||||||
|
protected boolean isDoubleHiresMode() {
|
||||||
|
return isDoubleHiresBlackAndWhiteMode() || isDoubleHiresColorMode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the given file as an Integer BASIC file.
|
||||||
|
* <p>
|
||||||
|
* On disk, it looks similar to Applesoft - first two bytes are length; rest of
|
||||||
|
* image is raw data for Integer Basic.
|
||||||
|
* <p>
|
||||||
|
* [byte] length of line<br>
|
||||||
|
* [word] line number<br>
|
||||||
|
* [byte]* line data<br>
|
||||||
|
* $01 end of line<br>
|
||||||
|
* Repeat until end of program (line length of 0).
|
||||||
|
* <p>
|
||||||
|
* Tokens are $00 - $7F, some are duplicated.<br>
|
||||||
|
* $01 = end of line.<br>
|
||||||
|
* $B0 - $B9 = signifies a number stored in a word.<br>
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 3, 2002 1:14:47 AM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class IntegerBasicFileFilter implements FileFilter {
|
||||||
|
private static String[] tokens = {
|
||||||
|
null, null, null, ": ", "LOAD ", "SAVE ", null, "RUN ", // $00-$07
|
||||||
|
null, "DEL ", ", ", "NEW ", "CLR ", "AUTO ", null, "MAN ", // $08-$0F
|
||||||
|
"HIMEM: ", "LOMEM: ", "+", "-", "*", "/", "=", "#", // $10-$17
|
||||||
|
">=", ">", "<=", "<>", "<", " AND ", " OR ", " MOD ",// $18-$1F
|
||||||
|
"^", null, "(", ",", " THEN ", " THEN ", ",", ",", // $20-$27
|
||||||
|
"\"", "\"", "(", null, null, "(", " PEEK ", "RND ", // $28-$2F
|
||||||
|
"SGN ", "ABS ", "PDL ", null, "(", "+", "-", "NOT ", // $30-$37
|
||||||
|
"(", "=", "#", " LEN(", " ASC(", " SCRN(", ",", " (", // $38-$3F
|
||||||
|
"$", null, "(", ",", ",", ";", ";", ";", // $40-$47
|
||||||
|
",", ",", ",", "TEXT ", "GR ", "CALL ", "DIM ", "DIM ", // $48-$4F
|
||||||
|
"TAB ", "END ", "INPUT ", "INPUT ", "INPUT ", "FOR ", "=", " TO ", // $50-$57
|
||||||
|
" STEP ", "NEXT ", ",", "RETURN ", "GOSUB ", "REM ", "LET ", "GOTO ",// $58-$5F
|
||||||
|
"IF ", "PRINT ", "PRINT ", "PRINT ", " POKE ", ",", "COLOR= ", "PLOT ",// $60-$67
|
||||||
|
",", "HLIN ", ",", " AT ", "VLIN ", ",", " AT ", "VTAB ",// $68-$6F
|
||||||
|
"=", "=", ")", null, "LIST ", ",", null, "POP ", // $70-$77
|
||||||
|
null, "NO DSP ", "NO TRACE ", "DSP ", "DSP ", "TRACE ", "PR # ", "IN # " // $78-$7F
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for IntegerBasicFileFilter.
|
||||||
|
*/
|
||||||
|
public IntegerBasicFileFilter() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the given FileEntry and return a text image of the Integer BASIC file.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileFilter#filter(FileEntry)
|
||||||
|
*/
|
||||||
|
public byte[] filter(FileEntry fileEntry) {
|
||||||
|
byte[] fileData = fileEntry.getFileData();
|
||||||
|
int offset = 0;
|
||||||
|
ByteArrayOutputStream byteArray = new ByteArrayOutputStream(fileData.length * 2);
|
||||||
|
PrintWriter printWriter = new PrintWriter(byteArray, true);
|
||||||
|
while (offset < fileData.length) {
|
||||||
|
int lineLength = AppleUtil.getUnsignedByte(fileData[offset]);
|
||||||
|
int lineNumber = AppleUtil.getWordValue(fileData, offset+1);
|
||||||
|
boolean inComment = false;
|
||||||
|
printWriter.print(lineNumber);
|
||||||
|
printWriter.print(' ');
|
||||||
|
for (int i=offset+3; i<(offset+lineLength); i++) {
|
||||||
|
byte byt = fileData[i];
|
||||||
|
if ((byt & 0x80) != 0) {
|
||||||
|
int value = AppleUtil.getUnsignedByte(byt);
|
||||||
|
// numbers follow a number digit
|
||||||
|
if (!inComment && value >= 0xb1 && value <= 0xb9) {
|
||||||
|
int integer = AppleUtil.getWordValue(fileData, i+1);
|
||||||
|
printWriter.print(integer);
|
||||||
|
i+= 2;
|
||||||
|
} else {
|
||||||
|
char ch = (char)(byt&0x7f);
|
||||||
|
if (ch < 0x20) { // handle control characters
|
||||||
|
printWriter.print("<CTRL-");
|
||||||
|
printWriter.print((char)('@' + ch));
|
||||||
|
printWriter.print(">");
|
||||||
|
} else {
|
||||||
|
printWriter.print(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String token = tokens[(int)byt];
|
||||||
|
if (token != null) {
|
||||||
|
printWriter.print(token);
|
||||||
|
inComment = (byt == 0x5d); // REM statement
|
||||||
|
} else {
|
||||||
|
// ignoring unknown tokens
|
||||||
|
// $00 and $01 seem to be valid; the others may or may not be valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset+= lineLength;
|
||||||
|
printWriter.println();
|
||||||
|
}
|
||||||
|
return byteArray.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give suggested file name.
|
||||||
|
*/
|
||||||
|
public String getSuggestedFileName(FileEntry fileEntry) {
|
||||||
|
String fileName = fileEntry.getFilename().trim();
|
||||||
|
if (!fileName.toLowerCase().endsWith(".int")) {
|
||||||
|
fileName = fileName + ".int";
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
209
src/com/webcodepro/applecommander/storage/PascalFileEntry.java
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Pascal file entry on disk.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 12:22:34 AM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class PascalFileEntry implements FileEntry {
|
||||||
|
private byte[] fileEntry;
|
||||||
|
private PascalFormatDisk disk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for PascalFileEntry.
|
||||||
|
*/
|
||||||
|
public PascalFileEntry(byte[] fileEntry, PascalFormatDisk disk) {
|
||||||
|
super();
|
||||||
|
this.fileEntry = fileEntry;
|
||||||
|
this.disk = disk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block number of the files 1st block.
|
||||||
|
*/
|
||||||
|
public int getFirstBlock() {
|
||||||
|
return AppleUtil.getWordValue(fileEntry, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block number of the files last block +1.
|
||||||
|
*/
|
||||||
|
public int getLastBlock() {
|
||||||
|
return AppleUtil.getWordValue(fileEntry, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this file.
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
return AppleUtil.getPascalString(fileEntry, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the filetype of this file.
|
||||||
|
*/
|
||||||
|
public String getFiletype() {
|
||||||
|
String filetypes[] = {
|
||||||
|
"xdskfile (for bad blocks)",
|
||||||
|
"codefile",
|
||||||
|
"textfile",
|
||||||
|
"infofile",
|
||||||
|
"datafile",
|
||||||
|
"graffile",
|
||||||
|
"fotofile",
|
||||||
|
"securedir" };
|
||||||
|
int filetype = fileEntry[4] & 0x0f;
|
||||||
|
if (filetype == 0 || filetype > filetypes.length) {
|
||||||
|
return "unknown (" + filetype + ")";
|
||||||
|
} else {
|
||||||
|
return filetypes[filetype-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file is locked - not applicable in Pascal?
|
||||||
|
*/
|
||||||
|
public boolean isLocked() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of bytes used in files last block.
|
||||||
|
*/
|
||||||
|
public int getBytesUsedInLastBlock() {
|
||||||
|
return AppleUtil.getWordValue(fileEntry, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the size of this file (in bytes).
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
int blocks = getBlocksUsed() - 1;
|
||||||
|
return blocks*Disk.BLOCK_SIZE + getBytesUsedInLastBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the blocks used.
|
||||||
|
*/
|
||||||
|
public int getBlocksUsed() {
|
||||||
|
return AppleUtil.getWordValue(fileEntry, 2) - AppleUtil.getWordValue(fileEntry, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pascal does not support directories.
|
||||||
|
*/
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the list of files in this directory.
|
||||||
|
* Always returns null, as Pascal does not support directories.
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pascal file entries are removed upon deletion.
|
||||||
|
* Thus, a file entry cannot be marked as deleted.
|
||||||
|
*/
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file modification date.
|
||||||
|
*/
|
||||||
|
public Date getModificationDate() {
|
||||||
|
return AppleUtil.getPascalDate(fileEntry, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
* displayMode is specified in FormattedDisk.
|
||||||
|
*/
|
||||||
|
public List getFileColumnData(int displayMode) {
|
||||||
|
NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy");
|
||||||
|
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FormattedDisk.FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(dateFormat.format(getModificationDate()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getBlocksUsed()));
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(getFilename());
|
||||||
|
break;
|
||||||
|
case FormattedDisk.FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(dateFormat.format(getModificationDate()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getBlocksUsed()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add(numberFormat.format(getBytesUsedInLastBlock()));
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(getFilename());
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getFirstBlock()));
|
||||||
|
list.add(numberFormat.format(getLastBlock()-1));
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.add(getFilename());
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
list.add(isLocked() ? "Locked" : "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file data. This handles any operating-system specific issues.
|
||||||
|
* Currently, the disk itself handles this.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData() {
|
||||||
|
return disk.getFileData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the suggested FileFilter. This appears to be operating system
|
||||||
|
* specific, so each operating system needs to implement some manner
|
||||||
|
* of guessing the appropriate filter.
|
||||||
|
*/
|
||||||
|
public FileFilter getSuggestedFilter() {
|
||||||
|
if ("textfile".equals(getFiletype())) {
|
||||||
|
return new TextFileFilter();
|
||||||
|
}
|
||||||
|
return new BinaryFileFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
356
src/com/webcodepro/applecommander/storage/PascalFormatDisk.java
Normal file
|
@ -0,0 +1,356 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages a disk that is in the Pascal format.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 4, 2002 11:56:50 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class PascalFormatDisk extends FormattedDisk {
|
||||||
|
/**
|
||||||
|
* The size of the Pascal file entry.
|
||||||
|
*/
|
||||||
|
public static final int ENTRY_SIZE = 26;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this inner interface for managing the disk usage data.
|
||||||
|
* This offloads format-specific implementation to the implementing class.
|
||||||
|
* A BitSet is used to track all blocks, as Pascal disks do not have a
|
||||||
|
* bitmap stored on the disk. This is safe since we know the number of blocks
|
||||||
|
* that exist. (BitSet length is of last set bit - unset bits at the end are
|
||||||
|
* "lost".)
|
||||||
|
*/
|
||||||
|
private class PascalDiskUsage implements DiskUsage {
|
||||||
|
private int location = -1;
|
||||||
|
private BitSet bitmap = null;
|
||||||
|
public boolean hasNext() {
|
||||||
|
return location == -1 || location < getBlocksOnDisk() - 1;
|
||||||
|
}
|
||||||
|
public void next() {
|
||||||
|
if (bitmap == null) {
|
||||||
|
bitmap = new BitSet(getBlocksOnDisk());
|
||||||
|
// assume all blocks are unused
|
||||||
|
for (int block=6; block<getBlocksOnDisk(); block++) {
|
||||||
|
bitmap.set(block);
|
||||||
|
}
|
||||||
|
// process through all files and mark those blocks as used
|
||||||
|
Iterator files = getFiles().iterator();
|
||||||
|
while (files.hasNext()) {
|
||||||
|
PascalFileEntry entry = (PascalFileEntry) files.next();
|
||||||
|
for (int block=entry.getFirstBlock(); block<entry.getLastBlock(); block++) {
|
||||||
|
bitmap.clear(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location = 0;
|
||||||
|
} else {
|
||||||
|
location++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public boolean isFree() {
|
||||||
|
return bitmap.get(location); // true = free
|
||||||
|
}
|
||||||
|
public boolean isUsed() {
|
||||||
|
return !bitmap.get(location); // false = used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for PascalFormatDisk.
|
||||||
|
* @param filename
|
||||||
|
* @param diskImage
|
||||||
|
* @param order
|
||||||
|
*/
|
||||||
|
public PascalFormatDisk(String filename, byte[] diskImage) {
|
||||||
|
super(filename, diskImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the operating system format of this disk.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFormat()
|
||||||
|
*/
|
||||||
|
public String getFormat() {
|
||||||
|
return "Pascal";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a list of files.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFiles()
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
List list = new ArrayList();
|
||||||
|
// read directory blocks (block 2-5):
|
||||||
|
byte[] directory = new byte[4 * BLOCK_SIZE];
|
||||||
|
for (int i=0; i<4; i++) {
|
||||||
|
System.arraycopy(readBlock(2+i), 0, directory, i*BLOCK_SIZE, BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
// process directory blocks:
|
||||||
|
int entrySize = ENTRY_SIZE;
|
||||||
|
int offset = entrySize;
|
||||||
|
while (offset < directory.length) {
|
||||||
|
byte[] entry = new byte[entrySize];
|
||||||
|
System.arraycopy(directory, offset, entry, 0, entry.length);
|
||||||
|
if (entry[6] == 0) break; // at end
|
||||||
|
list.add(new PascalFileEntry(entry, this));
|
||||||
|
offset+= entrySize;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this disk format is capable of having directories.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#canHaveDirectories()
|
||||||
|
*/
|
||||||
|
public boolean canHaveDirectories() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of free space in bytes.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFreeSpace()
|
||||||
|
*/
|
||||||
|
public int getFreeSpace() {
|
||||||
|
return getFreeBlocks() * BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of free blocks.
|
||||||
|
*/
|
||||||
|
public int getFreeBlocks() {
|
||||||
|
List files = getFiles();
|
||||||
|
int blocksFree = getBlocksOnDisk() - 6;
|
||||||
|
if (files != null) {
|
||||||
|
for (int i=0; i<files.size(); i++) {
|
||||||
|
PascalFileEntry entry = (PascalFileEntry) files.get(i);
|
||||||
|
blocksFree-= entry.getBlocksUsed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blocksFree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the volume entry.
|
||||||
|
*/
|
||||||
|
protected byte[] getVolumeEntry() {
|
||||||
|
byte[] block = readBlock(2);
|
||||||
|
byte[] entry = new byte[ENTRY_SIZE];
|
||||||
|
System.arraycopy(block, 0, entry, 0, entry.length);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of blocks on disk.
|
||||||
|
*/
|
||||||
|
public int getBlocksOnDisk() {
|
||||||
|
return AppleUtil.getWordValue(getVolumeEntry(), 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of files on disk.
|
||||||
|
*/
|
||||||
|
public int getFilesOnDisk() {
|
||||||
|
return AppleUtil.getWordValue(getVolumeEntry(), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the last access date.
|
||||||
|
*/
|
||||||
|
public Date getLastAccessDate() {
|
||||||
|
return AppleUtil.getPascalDate(getVolumeEntry(), 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the most recent date setting. Huh?
|
||||||
|
*/
|
||||||
|
public Date getMostRecentDateSetting() {
|
||||||
|
return AppleUtil.getPascalDate(getVolumeEntry(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of used space in bytes.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getUsedSpace()
|
||||||
|
*/
|
||||||
|
public int getUsedSpace() {
|
||||||
|
return getUsedBlocks() * BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of used blocks.
|
||||||
|
*/
|
||||||
|
public int getUsedBlocks() {
|
||||||
|
List files = getFiles();
|
||||||
|
int blocksUsed = 6;
|
||||||
|
if (files != null) {
|
||||||
|
for (int i=0; i<files.size(); i++) {
|
||||||
|
PascalFileEntry entry = (PascalFileEntry) files.get(i);
|
||||||
|
blocksUsed+= entry.getBlocksUsed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blocksUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the disk. This is stored on block #2
|
||||||
|
* offset +6 (string[7]).
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getDiskName()
|
||||||
|
*/
|
||||||
|
public String getDiskName() {
|
||||||
|
return AppleUtil.getPascalString(readBlock(2), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get suggested dimensions for display of bitmap. Since Pascal disks are
|
||||||
|
* a block device, no suggestion is given.
|
||||||
|
*/
|
||||||
|
public int[] getBitmapDimensions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of the bitmap.
|
||||||
|
*/
|
||||||
|
public int getBitmapLength() {
|
||||||
|
return getBlocksOnDisk();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the disk usage iterator.
|
||||||
|
*/
|
||||||
|
public DiskUsage getDiskUsage() {
|
||||||
|
return new PascalDiskUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the labels to use in the bitmap.
|
||||||
|
*/
|
||||||
|
public String[] getBitmapLabels() {
|
||||||
|
return new String[] { "Block" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Pascal-specific disk information.
|
||||||
|
*/
|
||||||
|
public List getDiskInformation() {
|
||||||
|
List list = super.getDiskInformation();
|
||||||
|
list.add(new DiskInformation("Total Blocks", getBlocksOnDisk()));
|
||||||
|
list.add(new DiskInformation("Free Blocks", getFreeBlocks()));
|
||||||
|
list.add(new DiskInformation("Used Blocks", getUsedBlocks()));
|
||||||
|
list.add(new DiskInformation("Files On Disk", getFilesOnDisk()));
|
||||||
|
list.add(new DiskInformation("Last Access Date", getLastAccessDate()));
|
||||||
|
list.add(new DiskInformation("Most Recent Date Setting", getMostRecentDateSetting()));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
*/
|
||||||
|
public List getFileColumnHeaders(int displayMode) {
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(new FileColumnHeader("Modified", 8, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
break;
|
||||||
|
case FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(new FileColumnHeader("Modified", 8, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Bytes in last block", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Size (bytes)", 6, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("First Block", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Last Block", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.addAll(super.getFileColumnHeaders(displayMode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk format supports "deleted" files.
|
||||||
|
*/
|
||||||
|
public boolean supportsDeletedFiles() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can read data from a file.
|
||||||
|
*/
|
||||||
|
public boolean canReadFileData() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can write data to a file.
|
||||||
|
*/
|
||||||
|
public boolean canWriteFileData() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can create a file.
|
||||||
|
*/
|
||||||
|
public boolean canCreateFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can delete a file.
|
||||||
|
*/
|
||||||
|
public boolean canDeleteFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data associated with the specified FileEntry.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData(FileEntry fileEntry) {
|
||||||
|
if ( !(fileEntry instanceof PascalFileEntry)) {
|
||||||
|
throw new IllegalArgumentException("Most have a Pascal file entry!");
|
||||||
|
}
|
||||||
|
PascalFileEntry pascalEntry = (PascalFileEntry) fileEntry;
|
||||||
|
int firstBlock = pascalEntry.getFirstBlock();
|
||||||
|
int lastBlock = pascalEntry.getLastBlock();
|
||||||
|
byte[] fileData = new byte[pascalEntry.getSize()];
|
||||||
|
int offset = 0;
|
||||||
|
for (int block = firstBlock; block < lastBlock; block++) {
|
||||||
|
byte[] blockData = readBlock(block);
|
||||||
|
if (block == lastBlock-1) {
|
||||||
|
System.arraycopy(blockData, 0, fileData, offset, pascalEntry.getBytesUsedInLastBlock());
|
||||||
|
} else {
|
||||||
|
System.arraycopy(blockData, 0, fileData, offset, blockData.length);
|
||||||
|
}
|
||||||
|
offset+= blockData.length;
|
||||||
|
}
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide common directory attributes.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 11:17:00 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ProdosCommonDirectoryHeader extends ProdosCommonEntry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProdosCommonDirectoryHeader.
|
||||||
|
* @param fileEntry
|
||||||
|
*/
|
||||||
|
public ProdosCommonDirectoryHeader(byte[] fileEntry) {
|
||||||
|
super(fileEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of each entry. Expected to be 0x27.
|
||||||
|
*/
|
||||||
|
public int getEntryLength() {
|
||||||
|
return AppleUtil.getUnsignedByte(getFileEntry()[0x1f]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of entries per block. Expected to be 0x0d.
|
||||||
|
*/
|
||||||
|
public int getEntriesPerBlock() {
|
||||||
|
return AppleUtil.getUnsignedByte(getFileEntry()[0x20]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of active entries in the volume directory.
|
||||||
|
*/
|
||||||
|
public int getFileCount() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x21);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block number of the bit map.
|
||||||
|
*/
|
||||||
|
public int getBitMapPointer() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x23);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total number of blocks on this volume.
|
||||||
|
*/
|
||||||
|
public int getTotalBlocks() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x25);
|
||||||
|
}
|
||||||
|
}
|
153
src/com/webcodepro/applecommander/storage/ProdosCommonEntry.java
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the common Prodos entry behavior.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 10:55:41 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ProdosCommonEntry {
|
||||||
|
private byte[] fileEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProdosCommonEntry.
|
||||||
|
*/
|
||||||
|
public ProdosCommonEntry(byte[] fileEntry) {
|
||||||
|
super();
|
||||||
|
this.fileEntry = fileEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the fileEntry bytes.
|
||||||
|
*/
|
||||||
|
protected byte[] getFileEntry() {
|
||||||
|
return fileEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get storage type.
|
||||||
|
*/
|
||||||
|
protected int getStorageType() {
|
||||||
|
return AppleUtil.getUnsignedByte(getFileEntry()[0]) >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is a "seedling" file (only one data block).
|
||||||
|
*/
|
||||||
|
public boolean isSeedlingFile() {
|
||||||
|
return getStorageType() == 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is a "sapling" file (2 to 256 data blocks).
|
||||||
|
*/
|
||||||
|
public boolean isSaplingFile() {
|
||||||
|
return getStorageType() == 0x02;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is a "tree" file (257 to 32768 data blocks).
|
||||||
|
*/
|
||||||
|
public boolean isTreeFile() {
|
||||||
|
return getStorageType() == 0x03;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is a subdirectory header entry.
|
||||||
|
*/
|
||||||
|
public boolean isSubdirectoryHeader() {
|
||||||
|
return getStorageType() == 0x0e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this is a volume header entry.
|
||||||
|
*/
|
||||||
|
public boolean isVolumeHeader() {
|
||||||
|
return getStorageType() == 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the creation date.
|
||||||
|
*/
|
||||||
|
public Date getCreationDate() {
|
||||||
|
return AppleUtil.getProdosDate(getFileEntry(), 0x18);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the version of ProDOS that created this file.
|
||||||
|
*/
|
||||||
|
public int getProdosVersion() {
|
||||||
|
return AppleUtil.getUnsignedByte(getFileEntry()[0x1c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the minimum version of ProDOS which can access this file.
|
||||||
|
*/
|
||||||
|
public int getMinimumProdosVersion() {
|
||||||
|
return AppleUtil.getUnsignedByte(getFileEntry()[0x1d]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the access byte.
|
||||||
|
*/
|
||||||
|
protected byte getAccess() {
|
||||||
|
return getFileEntry()[0x1e];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this file may be destroyed.
|
||||||
|
*/
|
||||||
|
public boolean canDestroy() {
|
||||||
|
return AppleUtil.isBitSet(getAccess(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this file may be renamed.
|
||||||
|
*/
|
||||||
|
public boolean canRename() {
|
||||||
|
return AppleUtil.isBitSet(getAccess(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this file has changed since last backup.
|
||||||
|
*/
|
||||||
|
public boolean hasChanged() {
|
||||||
|
return AppleUtil.isBitSet(getAccess(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this file may be written.
|
||||||
|
*/
|
||||||
|
public boolean canWrite() {
|
||||||
|
return AppleUtil.isBitSet(getAccess(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this file may be read.
|
||||||
|
*/
|
||||||
|
public boolean canRead() {
|
||||||
|
return AppleUtil.isBitSet(getAccess(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
457
src/com/webcodepro/applecommander/storage/ProdosFileEntry.java
Normal file
|
@ -0,0 +1,457 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a ProDOS file entry on disk.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 6:01:15 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ProdosFileEntry extends ProdosCommonEntry implements FileEntry {
|
||||||
|
private List files;
|
||||||
|
private ProdosSubdirectoryHeader subdirectoryHeader;
|
||||||
|
private ProdosFormatDisk disk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProdosFileEntry.
|
||||||
|
*/
|
||||||
|
public ProdosFileEntry(byte[] fileEntry, ProdosFormatDisk disk) {
|
||||||
|
super(fileEntry);
|
||||||
|
this.disk = disk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this file.
|
||||||
|
* This handles normal files, deleted files, and AppleWorks files - which use
|
||||||
|
* the AUXTYPE attribute to indicate upper/lower-case in the filename.
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
String fileName;
|
||||||
|
if (isDeleted()) {
|
||||||
|
fileName = AppleUtil.getString(getFileEntry(), 1, 15);
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
for (int i=0; i<fileName.length(); i++) {
|
||||||
|
char ch = fileName.charAt(i);
|
||||||
|
if (ch != 0) {
|
||||||
|
buf.append(ch);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileName = buf.toString();
|
||||||
|
} else {
|
||||||
|
fileName = AppleUtil.getProdosString(getFileEntry(), 0);
|
||||||
|
}
|
||||||
|
if (isAppleWorksFile()) {
|
||||||
|
int auxtype = getAuxiliaryType();
|
||||||
|
StringBuffer mixedCase = new StringBuffer(fileName);
|
||||||
|
// the highest bit of the least significant byte is the first
|
||||||
|
// character through the 2nd bit of the most significant byte
|
||||||
|
// being the 15th character. Bit is on indicates lowercase or
|
||||||
|
// a space if a "." is present.
|
||||||
|
for (int i=0; i<16 && i<fileName.length(); i++) {
|
||||||
|
boolean lowerCase;
|
||||||
|
if (i < 8) {
|
||||||
|
lowerCase = AppleUtil.isBitSet((byte)auxtype, 7-i);
|
||||||
|
} else {
|
||||||
|
lowerCase = AppleUtil.isBitSet((byte)(auxtype >> 8), 7-(i%8));
|
||||||
|
}
|
||||||
|
if (lowerCase) {
|
||||||
|
char ch = mixedCase.charAt(i);
|
||||||
|
if (ch == '.') {
|
||||||
|
mixedCase.setCharAt(i, ' ');
|
||||||
|
} else {
|
||||||
|
mixedCase.setCharAt(i, Character.toLowerCase(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileName = mixedCase.toString();
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the filetype of this file. This will be three characters,
|
||||||
|
* according to ProDOS - a "$xx" if unknown.
|
||||||
|
* <p>
|
||||||
|
* This could be improved should specific information regarding file types
|
||||||
|
* be needed; the file type could become a separate object which works with
|
||||||
|
* the file in some manner.
|
||||||
|
* <p>
|
||||||
|
* Note: Source of information is the following url -
|
||||||
|
* http://www.apple2.org.za/gswv/gsezine/GS.WorldView/ProDOS.File.Types.v2.0.txt
|
||||||
|
*/
|
||||||
|
public String getFiletype() {
|
||||||
|
int filetype = AppleUtil.getUnsignedByte(getFileEntry()[0x10]);
|
||||||
|
switch (filetype) {
|
||||||
|
case 0x00: return "UNK";
|
||||||
|
case 0x01: return "BAD";
|
||||||
|
case 0x02: return "PCD";
|
||||||
|
case 0x03: return "PTX";
|
||||||
|
case 0x04: return "TXT";
|
||||||
|
case 0x05: return "PDA";
|
||||||
|
case 0x06: return "BIN";
|
||||||
|
case 0x07: return "FNT";
|
||||||
|
case 0x08: return "FOT";
|
||||||
|
case 0x09: return "BA3";
|
||||||
|
case 0x0a: return "DA3";
|
||||||
|
case 0x0b: return "WPF";
|
||||||
|
case 0x0c: return "SOS";
|
||||||
|
case 0x0f: return "DIR";
|
||||||
|
case 0x10: return "RPD";
|
||||||
|
case 0x11: return "RPI";
|
||||||
|
case 0x12: return "AFD";
|
||||||
|
case 0x13: return "AFM";
|
||||||
|
case 0x14: return "AFR";
|
||||||
|
case 0x15: return "SCL";
|
||||||
|
case 0x16: return "PFS";
|
||||||
|
case 0x19: return "ADB"; // AppleWorks: AUX TYPE indicates UPPER/lower case
|
||||||
|
case 0x1a: return "AWP"; // AppleWorks: AUX TYPE indicates UPPER/lower case
|
||||||
|
case 0x1b: return "ASP"; // AppleWorks: AUX TYPE indicates UPPER/lower case
|
||||||
|
case 0x20: return "TDM";
|
||||||
|
case 0x21: return "IPS";
|
||||||
|
case 0x22: return "UPV";
|
||||||
|
case 0x29: return "3SD";
|
||||||
|
case 0x2a: return "8SC";
|
||||||
|
case 0x2b: return "8OB";
|
||||||
|
case 0x2c: return "8IC";
|
||||||
|
case 0x2d: return "8LD";
|
||||||
|
case 0x2e: return "P8C"; // P8C or PTP, depending on AUX TYPE
|
||||||
|
case 0x41: return "OCR";
|
||||||
|
case 0x42: return "FTD";
|
||||||
|
case 0x50: return "GWP";
|
||||||
|
case 0x51: return "GSS";
|
||||||
|
case 0x52: return "GDB";
|
||||||
|
case 0x53: return "DRW";
|
||||||
|
case 0x54: return "GDP";
|
||||||
|
case 0x55: return "HMD";
|
||||||
|
case 0x56: return "EDU";
|
||||||
|
case 0x57: return "STN";
|
||||||
|
case 0x58: return "HLP";
|
||||||
|
case 0x59: return "COM";
|
||||||
|
case 0x5a: return "CFG"; // CFG or PTP, depending on AUX TYPE
|
||||||
|
case 0x5b: return "ANM";
|
||||||
|
case 0x5c: return "MUM";
|
||||||
|
case 0x5d: return "ENT";
|
||||||
|
case 0x5e: return "DVU";
|
||||||
|
case 0x60: return "PRE";
|
||||||
|
case 0x6b: return "BIO";
|
||||||
|
case 0x6d: return "DVR"; // DVR/TDR
|
||||||
|
case 0x6e: return "PRE";
|
||||||
|
case 0x6f: return "HDV"; // PC Volume
|
||||||
|
case 0xa0: return "WP_";
|
||||||
|
case 0xab: return "GSB";
|
||||||
|
case 0xac: return "TDF";
|
||||||
|
case 0xad: return "BDF";
|
||||||
|
case 0xb0: return "SRC";
|
||||||
|
case 0xb1: return "OBJ";
|
||||||
|
case 0xb2: return "LIB";
|
||||||
|
case 0xb3: return "S16";
|
||||||
|
case 0xb4: return "RTL";
|
||||||
|
case 0xb5: return "EXE";
|
||||||
|
case 0xb6: return "STR"; // STR/PIF
|
||||||
|
case 0xb7: return "TSF"; // TSF/TIF
|
||||||
|
case 0xb8: return "NDA";
|
||||||
|
case 0xb9: return "CDA";
|
||||||
|
case 0xba: return "TOL";
|
||||||
|
case 0xbb: return "DRV"; // DRV/DVR
|
||||||
|
case 0xbc: return "LDF";
|
||||||
|
case 0xbd: return "FST";
|
||||||
|
case 0xbf: return "DOC";
|
||||||
|
case 0xc0: return "PNT";
|
||||||
|
case 0xc1: return "PIC";
|
||||||
|
case 0xc2: return "ANI";
|
||||||
|
case 0xc3: return "PAL";
|
||||||
|
case 0xc5: return "OOG";
|
||||||
|
case 0xc6: return "SCR";
|
||||||
|
case 0xc7: return "CDV";
|
||||||
|
case 0xc8: return "FON";
|
||||||
|
case 0xc9: return "FND";
|
||||||
|
case 0xca: return "ICN";
|
||||||
|
case 0xd5: return "MUS";
|
||||||
|
case 0xd6: return "INS";
|
||||||
|
case 0xd7: return "MDI";
|
||||||
|
case 0xd8: return "SND";
|
||||||
|
case 0xdb: return "DBM";
|
||||||
|
case 0xe0: return "SHK";
|
||||||
|
case 0xe2: return "DTS"; // DTS/ATK
|
||||||
|
case 0xee: return "R16";
|
||||||
|
case 0xef: return "PAS";
|
||||||
|
case 0xf0: return "CMD";
|
||||||
|
// Left $F1 - $F8 alone as these are user-defined types
|
||||||
|
case 0xf9: return "P16";
|
||||||
|
case 0xfa: return "INT";
|
||||||
|
case 0xfb: return "IVR";
|
||||||
|
case 0xfc: return "BAS";
|
||||||
|
case 0xfd: return "VAR";
|
||||||
|
case 0xfe: return "REL";
|
||||||
|
case 0xff: return "SYS";
|
||||||
|
default :
|
||||||
|
return "$" + AppleUtil.getFormattedByte(filetype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if this is an AppleWorks file.
|
||||||
|
* Intended to force upper/lowercase into the filename.
|
||||||
|
*/
|
||||||
|
public boolean isAppleWorksFile() {
|
||||||
|
int filetype = AppleUtil.getUnsignedByte(getFileEntry()[0x10]);
|
||||||
|
return (filetype == 0x19 || filetype == 0x1a || filetype == 0x1b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key pointer. This is either the data block (seedling),
|
||||||
|
* index block (sapling), or master index block (tree).
|
||||||
|
*/
|
||||||
|
public int getKeyPointer() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x11);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of blocks used.
|
||||||
|
*/
|
||||||
|
public int getBlocksUsed() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x13);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the EOF position. This can indicate the length of a file.
|
||||||
|
*/
|
||||||
|
public int getEofPosition() {
|
||||||
|
return AppleUtil.get3ByteValue(getFileEntry(), 0x15);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the auxiliary type for this file.
|
||||||
|
* TXT - random access record length.
|
||||||
|
* BIN - load address for binary image.
|
||||||
|
* BAS - load address for program image.
|
||||||
|
* VAR - address of compressed variables image.
|
||||||
|
* SYS - load address for system program (usually 0x2000).
|
||||||
|
*/
|
||||||
|
public int getAuxiliaryType() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last modification date.
|
||||||
|
*/
|
||||||
|
public Date getLastModificationDate() {
|
||||||
|
return AppleUtil.getProdosDate(getFileEntry(), 0x21);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block number of the key block for the directory which describes this file.
|
||||||
|
*/
|
||||||
|
public int getHeaderPointer() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file is locked.
|
||||||
|
*/
|
||||||
|
public boolean isLocked() {
|
||||||
|
return !canDestroy() && !canRename() && !canWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the size of this file (in bytes).
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
return getEofPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this is a directory file.
|
||||||
|
*/
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return getStorageType() == 0x0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the list of files in this directory.
|
||||||
|
* Note that if this is not a directory, the return
|
||||||
|
* value should be null. If this a directory, the
|
||||||
|
* return value should always be a list - a directory
|
||||||
|
* with 0 entries returns an empty list.
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the list of files.
|
||||||
|
*/
|
||||||
|
public void setFiles(List files) {
|
||||||
|
this.files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file has been deleted.
|
||||||
|
*/
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return getStorageType() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the subdirectory header.
|
||||||
|
*/
|
||||||
|
public void setSubdirectoryHeader(ProdosSubdirectoryHeader subdirectoryHeader) {
|
||||||
|
this.subdirectoryHeader = subdirectoryHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the subdirectory header.
|
||||||
|
*/
|
||||||
|
public ProdosSubdirectoryHeader getSubdirectoryHeader() {
|
||||||
|
return this.subdirectoryHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
* displayMode is specified in FormattedDisk.
|
||||||
|
*/
|
||||||
|
public List getFileColumnData(int displayMode) {
|
||||||
|
NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
|
||||||
|
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FormattedDisk.FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(isLocked() ? "*" : " ");
|
||||||
|
list.add(getFilename());
|
||||||
|
list.add(getFiletype());
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getBlocksUsed()));
|
||||||
|
list.add(getLastModificationDate() == null ? "<NO DATE> " :
|
||||||
|
dateFormat.format(getLastModificationDate()));
|
||||||
|
list.add(getCreationDate() == null ? "<NO DATE> " :
|
||||||
|
dateFormat.format(getCreationDate()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add(numberFormat.format(getEofPosition()));
|
||||||
|
if ("TXT".equals(getFiletype()) && getAuxiliaryType() > 0) {
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add("L=" + numberFormat.format(getAuxiliaryType()).trim());
|
||||||
|
} else if (("BIN".equals(getFiletype()) || "BAS".equals(getFiletype())
|
||||||
|
|| "VAR".equals(getFiletype()) || "SYS".equals(getFiletype()))
|
||||||
|
&& getAuxiliaryType() > 0) {
|
||||||
|
list.add("A=$" + AppleUtil.getFormattedWord(getAuxiliaryType()));
|
||||||
|
} else {
|
||||||
|
list.add("");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FormattedDisk.FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(isLocked() ? "*" : " ");
|
||||||
|
list.add(getFilename());
|
||||||
|
list.add(isDeleted() ? "Deleted" : "");
|
||||||
|
String permissions = "";
|
||||||
|
if (canDestroy()) permissions+= "Destroy ";
|
||||||
|
if (canRead()) permissions+= "Read ";
|
||||||
|
if (canRename()) permissions+= "Rename ";
|
||||||
|
if (canWrite()) permissions+= "Write ";
|
||||||
|
list.add(permissions);
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(isDirectory() ? "Directory" : "");
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getBlocksUsed()));
|
||||||
|
list.add(getLastModificationDate() == null ? "<NO DATE> " :
|
||||||
|
dateFormat.format(getLastModificationDate()));
|
||||||
|
list.add(getCreationDate() == null ? "<NO DATE> " :
|
||||||
|
dateFormat.format(getCreationDate()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add(numberFormat.format(getEofPosition()));
|
||||||
|
if ("TXT".equals(getFiletype()) && getAuxiliaryType() > 0) {
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add("L=" + numberFormat.format(getAuxiliaryType()).trim());
|
||||||
|
} else if (("BIN".equals(getFiletype()) || "BAS".equals(getFiletype())
|
||||||
|
|| "VAR".equals(getFiletype()) || "SYS".equals(getFiletype()))
|
||||||
|
&& getAuxiliaryType() > 0) {
|
||||||
|
list.add("A=$" + AppleUtil.getFormattedWord(getAuxiliaryType()));
|
||||||
|
} else {
|
||||||
|
list.add("");
|
||||||
|
}
|
||||||
|
list.add(AppleUtil.getFormattedWord(getHeaderPointer()));
|
||||||
|
list.add(AppleUtil.getFormattedWord(getKeyPointer()));
|
||||||
|
list.add(isSaplingFile() ? "Sapling" : isSeedlingFile() ? "Seedling" :
|
||||||
|
isTreeFile() ? "Tree" : "Unknown");
|
||||||
|
list.add(hasChanged() ? "Changed" : "");
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add(numberFormat.format(getMinimumProdosVersion()));
|
||||||
|
list.add(numberFormat.format(getProdosVersion()));
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.add(getFilename());
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
list.add(isLocked() ? "Locked" : "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file data. This handles any operating-system specific issues.
|
||||||
|
* Currently, the disk itself handles this.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData() {
|
||||||
|
return disk.getFileData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the suggested FileFilter. This appears to be operating system
|
||||||
|
* specific, so each operating system needs to implement some manner
|
||||||
|
* of guessing the appropriate filter.
|
||||||
|
*/
|
||||||
|
public FileFilter getSuggestedFilter() {
|
||||||
|
if ("TXT".equals(getFiletype()) || "SRC".equals(getFiletype())) {
|
||||||
|
return new TextFileFilter();
|
||||||
|
} else if ("AWP".equals(getFiletype())) {
|
||||||
|
return new AppleWorksWordProcessorFileFilter();
|
||||||
|
} else if ("BAS".equals(getFiletype())) {
|
||||||
|
return new ApplesoftFileFilter();
|
||||||
|
} else if ("INT".equals(getFiletype())) { // supposedly not available in ProDOS, however
|
||||||
|
return new IntegerBasicFileFilter();
|
||||||
|
} else if ("BIN".equals(getFiletype())) {
|
||||||
|
int size = getSize();
|
||||||
|
// the minimum size is guessed a bit - I don't remember, but maybe there
|
||||||
|
// are 8 spare bytes at the end of the graphics screen
|
||||||
|
GraphicsFileFilter filter = new GraphicsFileFilter();
|
||||||
|
if (size >= 8185 && size <= 8192) {
|
||||||
|
filter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
|
||||||
|
return filter;
|
||||||
|
} else if (size >= 16377 && size <= 16384) {
|
||||||
|
filter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
// fall through to BinaryFileFilter...
|
||||||
|
}
|
||||||
|
return new BinaryFileFilter();
|
||||||
|
}
|
||||||
|
}
|
392
src/com/webcodepro/applecommander/storage/ProdosFormatDisk.java
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages a disk that is in the ProDOS format.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 3, 2002 11:45:25 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ProdosFormatDisk extends FormattedDisk {
|
||||||
|
/**
|
||||||
|
* The standard ProDOS file entry length.
|
||||||
|
*/
|
||||||
|
public static final int ENTRY_LENGTH = 0x27;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hold on to the volume directory header.
|
||||||
|
*/
|
||||||
|
private ProdosVolumeDirectoryHeader volumeHeader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this inner interface for managing the disk usage data.
|
||||||
|
* This offloads format-specific implementation to the implementing class.
|
||||||
|
*/
|
||||||
|
private class ProdosDiskUsage implements DiskUsage {
|
||||||
|
private int location = -1;
|
||||||
|
private transient byte[] data = null;
|
||||||
|
public boolean hasNext() {
|
||||||
|
return location == -1 || location < volumeHeader.getTotalBlocks() - 1;
|
||||||
|
}
|
||||||
|
public void next() {
|
||||||
|
location++;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the free setting for the bitmap at the current location.
|
||||||
|
*/
|
||||||
|
public boolean isFree() {
|
||||||
|
if (location == -1) {
|
||||||
|
throw new IllegalArgumentException("Invalid dimension for isFree! Did you call next first?");
|
||||||
|
}
|
||||||
|
if (data == null) {
|
||||||
|
int volumeBitmapBlock = volumeHeader.getBitMapPointer();
|
||||||
|
int volumeBitmapBlocks = volumeHeader.getTotalBlocks();
|
||||||
|
int blocksToRead = (volumeBitmapBlocks / 4096) + 1;
|
||||||
|
// Read in the entire volume bitmap:
|
||||||
|
data = new byte[blocksToRead * BLOCK_SIZE];
|
||||||
|
for (int i=0; i<blocksToRead; i++) {
|
||||||
|
System.arraycopy(readBlock(volumeBitmapBlock+i), 0, data, i*BLOCK_SIZE, BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Locate appropriate bit and check it:
|
||||||
|
int byt = location / 8;
|
||||||
|
int bit = 7 - (location % 8);
|
||||||
|
boolean free = AppleUtil.isBitSet(data[byt], bit);
|
||||||
|
return free;
|
||||||
|
}
|
||||||
|
public boolean isUsed() {
|
||||||
|
return !isFree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProdosFormatDisk.
|
||||||
|
* @param filename
|
||||||
|
* @param diskImage
|
||||||
|
*/
|
||||||
|
public ProdosFormatDisk(String filename, byte[] diskImage) {
|
||||||
|
super(filename, diskImage);
|
||||||
|
|
||||||
|
// read volume header:
|
||||||
|
byte[] block = readBlock(2);
|
||||||
|
byte[] entry = new byte[ENTRY_LENGTH];
|
||||||
|
System.arraycopy(block, 4, entry, 0, ENTRY_LENGTH);
|
||||||
|
volumeHeader = new ProdosVolumeDirectoryHeader(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the operating system format of this disk.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFormat()
|
||||||
|
*/
|
||||||
|
public String getFormat() {
|
||||||
|
return "ProDOS";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a list of files.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFiles()
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
return getFiles(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a list of files, starting in the given block number.
|
||||||
|
* This works for the master as well as the subdirectories.
|
||||||
|
*/
|
||||||
|
protected List getFiles(int blockNumber) {
|
||||||
|
List files = new ArrayList();
|
||||||
|
while (blockNumber != 0) {
|
||||||
|
byte[] block = readBlock(blockNumber);
|
||||||
|
int offset = 4;
|
||||||
|
while (offset+ENTRY_LENGTH < BLOCK_SIZE) {
|
||||||
|
byte[] entry = new byte[ENTRY_LENGTH];
|
||||||
|
System.arraycopy(block, offset, entry, 0, ENTRY_LENGTH);
|
||||||
|
int checksum = 0;
|
||||||
|
for (int i=0; i<entry.length; i++) {
|
||||||
|
checksum |= entry[i];
|
||||||
|
}
|
||||||
|
if (checksum != 0) {
|
||||||
|
ProdosCommonEntry tester = new ProdosCommonEntry(entry);
|
||||||
|
if (tester.isVolumeHeader() || tester.isSubdirectoryHeader()) {
|
||||||
|
// ignore it, we've already got it
|
||||||
|
} else {
|
||||||
|
ProdosFileEntry fileEntry = new ProdosFileEntry(entry, this);
|
||||||
|
files.add(fileEntry);
|
||||||
|
if (fileEntry.isDirectory()) {
|
||||||
|
int keyPointer = fileEntry.getKeyPointer();
|
||||||
|
byte[] subdirBlock = readBlock(keyPointer);
|
||||||
|
byte[] subdirEntry = new byte[ENTRY_LENGTH];
|
||||||
|
System.arraycopy(subdirBlock, 4, subdirEntry, 0, ENTRY_LENGTH);
|
||||||
|
fileEntry.setSubdirectoryHeader(new ProdosSubdirectoryHeader(subdirEntry));
|
||||||
|
fileEntry.setFiles(getFiles(keyPointer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset+= entry.length;
|
||||||
|
}
|
||||||
|
blockNumber = AppleUtil.getWordValue(block, 2);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of free space in bytes.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getFreeSpace()
|
||||||
|
*/
|
||||||
|
public int getFreeSpace() {
|
||||||
|
return getFreeBlocks() * BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of free blocks on the disk.
|
||||||
|
*/
|
||||||
|
public int getFreeBlocks() {
|
||||||
|
int freeBlocks = 0;
|
||||||
|
int blocksToProcess = (volumeHeader.getTotalBlocks() + 4095) / 4096;
|
||||||
|
int blockNumber = volumeHeader.getBitMapPointer();
|
||||||
|
for (int ix=0; ix<blocksToProcess; ix++) {
|
||||||
|
byte[] block = readBlock(blockNumber+ix);
|
||||||
|
for (int byt=0; byt<block.length; byt++) {
|
||||||
|
freeBlocks+= AppleUtil.getBitCount(block[byt]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return freeBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of used space in bytes.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getUsedSpace()
|
||||||
|
*/
|
||||||
|
public int getUsedSpace() {
|
||||||
|
return getUsedBlocks() * BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of used blocks on the disk.
|
||||||
|
*/
|
||||||
|
public int getUsedBlocks() {
|
||||||
|
return volumeHeader.getTotalBlocks() - getFreeBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this disk format is capable of having directories.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#hasDirectories()
|
||||||
|
*/
|
||||||
|
public boolean canHaveDirectories() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the disk.
|
||||||
|
* @see com.webcodepro.applecommander.storage.Disk#getDiskName()
|
||||||
|
*/
|
||||||
|
public String getDiskName() {
|
||||||
|
return "/" + volumeHeader.getVolumeName() + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get suggested dimensions for display of bitmap. There is no suggestion
|
||||||
|
* for a ProDOS volume - it is just a series of blocks.
|
||||||
|
*/
|
||||||
|
public int[] getBitmapDimensions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of the bitmap.
|
||||||
|
*/
|
||||||
|
public int getBitmapLength() {
|
||||||
|
return volumeHeader.getTotalBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the disk usage iterator.
|
||||||
|
*/
|
||||||
|
public DiskUsage getDiskUsage() {
|
||||||
|
return new ProdosDiskUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the labels to use in the bitmap.
|
||||||
|
*/
|
||||||
|
public String[] getBitmapLabels() {
|
||||||
|
return new String[] { "Block" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Pascal-specific disk information.
|
||||||
|
*/
|
||||||
|
public List getDiskInformation() {
|
||||||
|
List list = super.getDiskInformation();
|
||||||
|
list.add(new DiskInformation("Total Blocks", volumeHeader.getTotalBlocks()));
|
||||||
|
list.add(new DiskInformation("Free Blocks", getFreeBlocks()));
|
||||||
|
list.add(new DiskInformation("Used Blocks", getUsedBlocks()));
|
||||||
|
list.add(new DiskInformation("Volume Access",
|
||||||
|
(volumeHeader.canDestroy() ? "Destroy " : "") +
|
||||||
|
(volumeHeader.canRead() ? "Read " : "") +
|
||||||
|
(volumeHeader.canRename() ? "Rename " : "") +
|
||||||
|
(volumeHeader.canWrite() ? "Write" : "")));
|
||||||
|
list.add(new DiskInformation("Block Number of Bitmap", volumeHeader.getBitMapPointer()));
|
||||||
|
list.add(new DiskInformation("Creation Date", volumeHeader.getCreationDate()));
|
||||||
|
list.add(new DiskInformation("File Entries Per Block", volumeHeader.getEntriesPerBlock()));
|
||||||
|
list.add(new DiskInformation("File Entry Length (bytes)", volumeHeader.getEntryLength()));
|
||||||
|
list.add(new DiskInformation("Active Files in Root Directory", volumeHeader.getFileCount()));
|
||||||
|
list.add(new DiskInformation("Minimum ProDOS Version Required",
|
||||||
|
volumeHeader.getMinimumProdosVersion()));
|
||||||
|
list.add(new DiskInformation("Volume Created By ProDOS Version", volumeHeader.getProdosVersion()));
|
||||||
|
list.add(new DiskInformation("Volume Name", volumeHeader.getVolumeName()));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
*/
|
||||||
|
public List getFileColumnHeaders(int displayMode) {
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Modified", 10, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Created", 10, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Length", 10, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Aux. Type", 8, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
break;
|
||||||
|
case FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Name", 15, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Deleted?", 7, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Permissions", 8, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Filetype", 8, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Directory?", 9, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Modified", 10, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Created", 10, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Length", 10, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Aux. Type", 8, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Dir. Header", 5, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Key Block", 5, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Key Type", 8, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Changed", 5, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Min. ProDOS Ver.", 2, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("ProDOS Ver.", 2, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.addAll(super.getFileColumnHeaders(displayMode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk format supports "deleted" files.
|
||||||
|
*/
|
||||||
|
public boolean supportsDeletedFiles() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can read data from a file.
|
||||||
|
*/
|
||||||
|
public boolean canReadFileData() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can write data to a file.
|
||||||
|
*/
|
||||||
|
public boolean canWriteFileData() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can create a file.
|
||||||
|
*/
|
||||||
|
public boolean canCreateFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can delete a file.
|
||||||
|
*/
|
||||||
|
public boolean canDeleteFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data associated with the specified FileEntry.
|
||||||
|
* Note that this could return a 16MB file! Sparse files are not treated specially.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData(FileEntry fileEntry) {
|
||||||
|
if ( !(fileEntry instanceof ProdosFileEntry)) {
|
||||||
|
throw new IllegalArgumentException("Most have a ProDOS file entry!");
|
||||||
|
}
|
||||||
|
ProdosFileEntry prodosEntry = (ProdosFileEntry) fileEntry;
|
||||||
|
byte[] fileData = new byte[prodosEntry.getEofPosition()];
|
||||||
|
int indexBlocks = 0;
|
||||||
|
if (prodosEntry.isSeedlingFile()) {
|
||||||
|
byte[] blockData = readBlock(prodosEntry.getKeyPointer());
|
||||||
|
System.arraycopy(blockData, 0, fileData, 0, prodosEntry.getEofPosition());
|
||||||
|
} else if (prodosEntry.isSaplingFile()) {
|
||||||
|
byte[] indexBlock = readBlock(prodosEntry.getKeyPointer());
|
||||||
|
getIndexBlockData(fileData, indexBlock, 0);
|
||||||
|
} else if (prodosEntry.isTreeFile()) {
|
||||||
|
byte[] masterIndexBlock = readBlock(prodosEntry.getKeyPointer());
|
||||||
|
int offset = 0;
|
||||||
|
for (int i=0; i<0x100; i++) {
|
||||||
|
int blockNumber = AppleUtil.getWordValue(masterIndexBlock[i], masterIndexBlock[i+0x100]);
|
||||||
|
byte[] indexBlock = readBlock(blockNumber);
|
||||||
|
offset+= getIndexBlockData(fileData, indexBlock, offset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown ProDOS filetype!");
|
||||||
|
}
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read file data from the given index block.
|
||||||
|
* Note that block number 0 is an unused block.
|
||||||
|
* @see #getFileData()
|
||||||
|
*/
|
||||||
|
protected int getIndexBlockData(byte[] fileData, byte[] indexBlock, int offset) {
|
||||||
|
for (int i=0; i<0x100; i++) {
|
||||||
|
int blockNumber = AppleUtil.getWordValue(indexBlock[i], indexBlock[i+0x100]);
|
||||||
|
byte[] blockData = readBlock(blockNumber);
|
||||||
|
if (offset + blockData.length > fileData.length) { // end of file
|
||||||
|
int bytesToCopy = fileData.length - offset;
|
||||||
|
if (blockNumber != 0) System.arraycopy(blockData, 0, fileData, offset, bytesToCopy);
|
||||||
|
offset+= bytesToCopy;
|
||||||
|
} else {
|
||||||
|
if (blockNumber != 0) System.arraycopy(blockData, 0, fileData, offset, blockData.length);
|
||||||
|
offset+= blockData.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides commone subdirectory attributes.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 11:17:57 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ProdosSubdirectoryHeader extends ProdosCommonDirectoryHeader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProdosSubdirectoryHeader.
|
||||||
|
* @param fileEntry
|
||||||
|
*/
|
||||||
|
public ProdosSubdirectoryHeader(byte[] fileEntry) {
|
||||||
|
super(fileEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this subdirectory.
|
||||||
|
*/
|
||||||
|
public String getSubdirectoryName() {
|
||||||
|
return AppleUtil.getProdosString(getFileEntry(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the block number of the parent directory which contains the
|
||||||
|
* file entry for this subdirectory.
|
||||||
|
*/
|
||||||
|
public int getParentPointer() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x23);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of the file entry within the parent block.
|
||||||
|
*/
|
||||||
|
public int getParentEntry() {
|
||||||
|
return AppleUtil.getUnsignedByte(getFileEntry()[0x25]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the length of the parent entry.
|
||||||
|
*/
|
||||||
|
public int getParentEntryLength() {
|
||||||
|
return AppleUtil.getWordValue(getFileEntry(), 0x26);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the ProDOS volume directory header.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 5, 2002 10:58:24 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ProdosVolumeDirectoryHeader extends ProdosCommonDirectoryHeader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ProdosVolumeDirectoryHeaderEntry.
|
||||||
|
* @param fileEntry
|
||||||
|
*/
|
||||||
|
public ProdosVolumeDirectoryHeader(byte[] fileEntry) {
|
||||||
|
super(fileEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this volume.
|
||||||
|
*/
|
||||||
|
public String getVolumeName() {
|
||||||
|
return AppleUtil.getProdosString(getFileEntry(), 0);
|
||||||
|
}
|
||||||
|
}
|
239
src/com/webcodepro/applecommander/storage/RdosFileEntry.java
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle RDOS file entry format.
|
||||||
|
* <p>
|
||||||
|
* Since I was unable to locate the file entries on the internet, it is documented here:
|
||||||
|
* <tt>
|
||||||
|
* Offset Description<br>
|
||||||
|
* ====== ====================================================<br>
|
||||||
|
* $00-$17 File name; space-filled. If the first byte is $00, that is the end of the<br>
|
||||||
|
* directory. If the first byte is $80, the file is deleted.<br>
|
||||||
|
* $18 File type. Appears to be actual letter ('A'=Applesoft, etc)<br>
|
||||||
|
* $19 File length in blocks (block = sector = 256 bytes)<br>
|
||||||
|
* $1A-$1B Address of application. For Applesoft and binary; others may vary.<br>
|
||||||
|
* $1C-$1D Length in bytes of file.<br>
|
||||||
|
* $1E-$1F Starting block of application.<br>
|
||||||
|
* </tt>
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 7, 2002 1:36:56 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class RdosFileEntry implements FileEntry {
|
||||||
|
private byte[] fileEntry;
|
||||||
|
private RdosFormatDisk disk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for RdosFileEntry.
|
||||||
|
*/
|
||||||
|
public RdosFileEntry(byte[] fileEntry, RdosFormatDisk disk) {
|
||||||
|
super();
|
||||||
|
this.fileEntry = fileEntry;
|
||||||
|
this.disk = disk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of blocks this file uses.
|
||||||
|
*/
|
||||||
|
public int getSizeInBlocks() {
|
||||||
|
return AppleUtil.getUnsignedByte(fileEntry[0x19]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the starting block of this application.
|
||||||
|
*/
|
||||||
|
public int getStartingBlock() {
|
||||||
|
return AppleUtil.getWordValue(fileEntry, 0x1e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the address of application.
|
||||||
|
*/
|
||||||
|
public int getAddress() {
|
||||||
|
return AppleUtil.getWordValue(fileEntry, 0x1a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this file.
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
return isDeleted() ? "<NOT IN USE> " : AppleUtil.getString(fileEntry, 0, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the filetype of this file.
|
||||||
|
*/
|
||||||
|
public String getFiletype() {
|
||||||
|
return isDeleted() ? " " : AppleUtil.getString(fileEntry, 0x18, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locked doesn't appear to be a concept under RDOS.
|
||||||
|
*/
|
||||||
|
public boolean isLocked() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the size of this file (in bytes).
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
return AppleUtil.getWordValue(fileEntry, 0x1c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RDOS does not support directories.
|
||||||
|
*/
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the list of files in this directory.
|
||||||
|
* Since RDOS does not support directories, this will always return null.
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify if this file has been deleted.
|
||||||
|
*/
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return AppleUtil.getUnsignedByte(fileEntry[0]) == 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
* displayMode is specified in FormattedDisk.
|
||||||
|
*/
|
||||||
|
public List getFileColumnData(int displayMode) {
|
||||||
|
NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
||||||
|
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FormattedDisk.FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(getFiletype());
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getSizeInBlocks()));
|
||||||
|
list.add(getFilename());
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getStartingBlock()));
|
||||||
|
break;
|
||||||
|
case FormattedDisk.FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(getFiletype());
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getSizeInBlocks()));
|
||||||
|
list.add(getFilename());
|
||||||
|
numberFormat.setMinimumIntegerDigits(1);
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
numberFormat.setMinimumIntegerDigits(3);
|
||||||
|
list.add(numberFormat.format(getStartingBlock()));
|
||||||
|
list.add("$" + AppleUtil.getFormattedWord(getAddress()));
|
||||||
|
list.add(isDeleted() ? "Deleted" : "");
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.add(getFilename());
|
||||||
|
list.add(getFiletype());
|
||||||
|
list.add(numberFormat.format(getSize()));
|
||||||
|
list.add(isLocked() ? "Locked" : "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file data. This handles any operating-system specific issues.
|
||||||
|
* Currently, the disk itself handles this.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData() {
|
||||||
|
byte[] rawdata = disk.getFileData(this);
|
||||||
|
byte[] filedata = new byte[getSize()];
|
||||||
|
System.arraycopy(rawdata, 0, filedata, 0, filedata.length);
|
||||||
|
return filedata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the suggested FileFilter. This appears to be operating system
|
||||||
|
* specific, so each operating system needs to implement some manner
|
||||||
|
* of guessing the appropriate filter.
|
||||||
|
* FIXME - this code should be a helper class for DOS and RDOS!
|
||||||
|
*/
|
||||||
|
public FileFilter getSuggestedFilter() {
|
||||||
|
if (isApplesoftBasicFile()) {
|
||||||
|
return new ApplesoftFileFilter();
|
||||||
|
} else if (isIntegerBasicFile()) {
|
||||||
|
return new IntegerBasicFileFilter();
|
||||||
|
} else if (isTextFile()) {
|
||||||
|
return new TextFileFilter();
|
||||||
|
} else if (isBinaryFile()) {
|
||||||
|
int size = getSize();
|
||||||
|
// the minimum size is guessed a bit - I don't remember, but maybe there
|
||||||
|
// are 8 spare bytes at the end of the graphics screen
|
||||||
|
GraphicsFileFilter filter = new GraphicsFileFilter();
|
||||||
|
if (size >= 8185 && size <= 8192) {
|
||||||
|
filter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
|
||||||
|
return filter;
|
||||||
|
} else if (size >= 16377 && size <= 16384) {
|
||||||
|
filter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
// fall through to BinaryFileFilter...
|
||||||
|
}
|
||||||
|
return new BinaryFileFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is a text file.
|
||||||
|
*/
|
||||||
|
public boolean isTextFile() {
|
||||||
|
return "T".equals(getFiletype());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is an Applesoft BASIC file.
|
||||||
|
*/
|
||||||
|
public boolean isApplesoftBasicFile() {
|
||||||
|
return "A".equals(getFiletype());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is an Integer BASIC file.
|
||||||
|
*/
|
||||||
|
public boolean isIntegerBasicFile() {
|
||||||
|
return "I".equals(getFiletype());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is a binary file.
|
||||||
|
*/
|
||||||
|
public boolean isBinaryFile() {
|
||||||
|
return "B".equals(getFiletype());
|
||||||
|
}
|
||||||
|
}
|
328
src/com/webcodepro/applecommander/storage/RdosFormatDisk.java
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages a disk that is in the RDOS format.
|
||||||
|
* <p>
|
||||||
|
* Note that the RDOS block interleave is different than the standard DOS 3.3 format.
|
||||||
|
* Thus, when the image is made, the sectors are skewed differently - use readRdosBlock
|
||||||
|
* to read the appropriate block number.
|
||||||
|
* <p>
|
||||||
|
* Also note that the operating system is itself the first file. Block #0 is really
|
||||||
|
* track 0, sector 0 - meaning that the first file should not (cannot) be deleted.
|
||||||
|
* <p>
|
||||||
|
* RDOS appears to have been placed on 13 sector disks. This limits the number of blocks
|
||||||
|
* to 455. It also may also cause incompatibilities with other formats and other cracks.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 7, 2002 2:03:58 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class RdosFormatDisk extends FormattedDisk {
|
||||||
|
/**
|
||||||
|
* Specifies the length of a file entry.
|
||||||
|
*/
|
||||||
|
public static final int ENTRY_LENGTH = 0x20;
|
||||||
|
/**
|
||||||
|
* Specifies the number of blocks on the disk.
|
||||||
|
* RDOS apparantly only worked on 5.25" disks.
|
||||||
|
*/
|
||||||
|
public static final int BLOCKS_ON_DISK = 455;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this inner interface for managing the disk usage data.
|
||||||
|
* This offloads format-specific implementation to the implementing class.
|
||||||
|
* A BitSet is used to track all blocks, as RDOS disks do not have a
|
||||||
|
* bitmap stored on the disk. This is safe since we know the number of blocks
|
||||||
|
* that exist. (BitSet length is of last set bit - unset bits at the end are
|
||||||
|
* "lost".)
|
||||||
|
* <p>
|
||||||
|
* Note one really unique point about RDOS - the entire disk is mapped out
|
||||||
|
* by the file entries. There are no blocks marked off, by default, by the
|
||||||
|
* operating system. However, the first file (RDOS itself) starts on block
|
||||||
|
* 0 (track 0, sector 0) and runs for 26 blocks - which covers all of track 0
|
||||||
|
* (the operating system) and the 10 sectors used for file entries.
|
||||||
|
*/
|
||||||
|
private class RdosDiskUsage implements DiskUsage {
|
||||||
|
private int location = -1;
|
||||||
|
private BitSet bitmap = null;
|
||||||
|
public boolean hasNext() {
|
||||||
|
return location == -1 || location < BLOCKS_ON_DISK - 1;
|
||||||
|
}
|
||||||
|
public void next() {
|
||||||
|
if (bitmap == null) {
|
||||||
|
bitmap = new BitSet(BLOCKS_ON_DISK);
|
||||||
|
// mark all blocks as unused
|
||||||
|
for (int b=0; b<BLOCKS_ON_DISK; b++) {
|
||||||
|
bitmap.set(b);
|
||||||
|
}
|
||||||
|
// for each file, mark the blocks used
|
||||||
|
Iterator files = getFiles().iterator();
|
||||||
|
while (files.hasNext()) {
|
||||||
|
RdosFileEntry fileEntry = (RdosFileEntry) files.next();
|
||||||
|
if (!fileEntry.isDeleted()) {
|
||||||
|
for (int b=0; b<fileEntry.getSizeInBlocks(); b++) {
|
||||||
|
bitmap.clear(fileEntry.getStartingBlock()+b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location = 0;
|
||||||
|
} else {
|
||||||
|
location++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public boolean isFree() {
|
||||||
|
return bitmap.get(location); // true = free
|
||||||
|
}
|
||||||
|
public boolean isUsed() {
|
||||||
|
return !bitmap.get(location); // false = used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for RdosFormatDisk.
|
||||||
|
* @param filename
|
||||||
|
* @param diskImage
|
||||||
|
*/
|
||||||
|
public RdosFormatDisk(String filename, byte[] diskImage) {
|
||||||
|
super(filename, diskImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an RDOS block. The sector skewing for RDOS seems to be different.
|
||||||
|
* This routine will convert the block number to a DOS track and sector,
|
||||||
|
* handling the sector change-over. The readSector method then should
|
||||||
|
* take care of various image formats.
|
||||||
|
* <p>
|
||||||
|
* Note that sectorSkew has the full 16 sectors, even though RDOS
|
||||||
|
* itself is a 13 sector format.
|
||||||
|
*/
|
||||||
|
public byte[] readRdosBlock(int block) {
|
||||||
|
int sectorSkew[] = { 0, 7, 0x0e, 6, 0x0d, 5, 0x0c, 4,
|
||||||
|
0x0b, 3, 0x0a, 2, 9, 1, 8, 0x0f };
|
||||||
|
int track = block / 13;
|
||||||
|
int sector = sectorSkew[block % 13];
|
||||||
|
return readSector(track, sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RDOS dos not support directories.
|
||||||
|
*/
|
||||||
|
public boolean canHaveDirectories() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RDOS really does not have a disk name. Fake one.
|
||||||
|
*/
|
||||||
|
public String getDiskName() {
|
||||||
|
byte[] block = readRdosBlock(4);
|
||||||
|
return AppleUtil.getString(block, 0xe0, 0x20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a list of files.
|
||||||
|
*/
|
||||||
|
public List getFiles() {
|
||||||
|
List files = new ArrayList();
|
||||||
|
for (int b=13; b<23; b++) {
|
||||||
|
byte[] data = readRdosBlock(b);
|
||||||
|
for (int i=0; i<data.length; i+= ENTRY_LENGTH) {
|
||||||
|
byte[] entry = new byte[ENTRY_LENGTH];
|
||||||
|
System.arraycopy(data, i, entry, 0, entry.length);
|
||||||
|
if (AppleUtil.getUnsignedByte(entry[0]) != 0) {
|
||||||
|
RdosFileEntry fileEntry = new RdosFileEntry(entry, this);
|
||||||
|
files.add(fileEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the operating system format of this disk.
|
||||||
|
*/
|
||||||
|
public String getFormat() {
|
||||||
|
return "RDOS 2.1";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of free blocks.
|
||||||
|
*/
|
||||||
|
public int getFreeBlocks() {
|
||||||
|
return BLOCKS_ON_DISK - getUsedBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of used blocks.
|
||||||
|
*/
|
||||||
|
public int getUsedBlocks() {
|
||||||
|
int used = 0;
|
||||||
|
Iterator files = getFiles().iterator();
|
||||||
|
while (files.hasNext()) {
|
||||||
|
RdosFileEntry fileEntry = (RdosFileEntry) files.next();
|
||||||
|
if (!fileEntry.isDeleted()) used+= fileEntry.getSizeInBlocks();
|
||||||
|
}
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of free space in bytes.
|
||||||
|
*/
|
||||||
|
public int getFreeSpace() {
|
||||||
|
return getFreeBlocks() * SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of used space in bytes.
|
||||||
|
*/
|
||||||
|
public int getUsedSpace() {
|
||||||
|
return getUsedBlocks() * SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get suggested dimensions for display of bitmap.
|
||||||
|
* Since RDOS uses blocks, a null is returned indicating no suggsetions.
|
||||||
|
*/
|
||||||
|
public int[] getBitmapDimensions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of the bitmap.
|
||||||
|
*/
|
||||||
|
public int getBitmapLength() {
|
||||||
|
return BLOCKS_ON_DISK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the disk usage iterator.
|
||||||
|
*/
|
||||||
|
public DiskUsage getDiskUsage() {
|
||||||
|
return new RdosDiskUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the labels to use in the bitmap.
|
||||||
|
*/
|
||||||
|
public String[] getBitmapLabels() {
|
||||||
|
return new String[] { "Block" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Pascal-specific disk information.
|
||||||
|
*/
|
||||||
|
public List getDiskInformation() {
|
||||||
|
List list = super.getDiskInformation();
|
||||||
|
list.add(new DiskInformation("Total Blocks", BLOCKS_ON_DISK));
|
||||||
|
list.add(new DiskInformation("Free Blocks", getFreeBlocks()));
|
||||||
|
list.add(new DiskInformation("Used Blocks", getUsedBlocks()));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the standard file column header information.
|
||||||
|
* This default implementation is intended only for standard mode.
|
||||||
|
*/
|
||||||
|
public List getFileColumnHeaders(int displayMode) {
|
||||||
|
List list = new ArrayList();
|
||||||
|
switch (displayMode) {
|
||||||
|
case FILE_DISPLAY_NATIVE:
|
||||||
|
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Name", 24, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Size", 6, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Starting Block", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
break;
|
||||||
|
case FILE_DISPLAY_DETAIL:
|
||||||
|
list.add(new FileColumnHeader("Type", 1, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
list.add(new FileColumnHeader("Blocks", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Name", 24, FileColumnHeader.ALIGN_LEFT));
|
||||||
|
list.add(new FileColumnHeader("Size", 6, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Starting Block", 3, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Address", 5, FileColumnHeader.ALIGN_RIGHT));
|
||||||
|
list.add(new FileColumnHeader("Deleted?", 7, FileColumnHeader.ALIGN_CENTER));
|
||||||
|
break;
|
||||||
|
default: // FILE_DISPLAY_STANDARD
|
||||||
|
list.addAll(super.getFileColumnHeaders(displayMode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk format supports "deleted" files.
|
||||||
|
*/
|
||||||
|
public boolean supportsDeletedFiles() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can read data from a file.
|
||||||
|
*/
|
||||||
|
public boolean canReadFileData() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can write data to a file.
|
||||||
|
*/
|
||||||
|
public boolean canWriteFileData() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can create a file.
|
||||||
|
*/
|
||||||
|
public boolean canCreateFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this disk image can delete a file.
|
||||||
|
*/
|
||||||
|
public boolean canDeleteFile() {
|
||||||
|
return false; // FIXME - not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data associated with the specified FileEntry.
|
||||||
|
*/
|
||||||
|
public byte[] getFileData(FileEntry fileEntry) {
|
||||||
|
if ( !(fileEntry instanceof RdosFileEntry)) {
|
||||||
|
throw new IllegalArgumentException("Most have a RDOS file entry!");
|
||||||
|
}
|
||||||
|
RdosFileEntry rdosEntry = (RdosFileEntry) fileEntry;
|
||||||
|
int startingBlock = rdosEntry.getStartingBlock();
|
||||||
|
byte[] fileData = new byte[rdosEntry.getSizeInBlocks() * SECTOR_SIZE];
|
||||||
|
int offset = 0;
|
||||||
|
for (int blockOffset = 0; blockOffset < rdosEntry.getSizeInBlocks(); blockOffset++) {
|
||||||
|
byte[] blockData = readRdosBlock(startingBlock + blockOffset);
|
||||||
|
System.arraycopy(blockData, 0, fileData, offset, blockData.length);
|
||||||
|
offset+= blockData.length;
|
||||||
|
}
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the given file data for text.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 2, 2002 9:11:27 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class TextFileFilter implements FileFilter {
|
||||||
|
/**
|
||||||
|
* Constructor for TextFileFilter.
|
||||||
|
*/
|
||||||
|
public TextFileFilter() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the given FileEntry and return a byte array with filtered data.
|
||||||
|
* @see com.webcodepro.applecommander.storage.FileFilter#filter(byte[])
|
||||||
|
*/
|
||||||
|
public byte[] filter(FileEntry fileEntry) {
|
||||||
|
byte[] fileData = fileEntry.getFileData();
|
||||||
|
byte[] workingData = new byte[fileData.length];
|
||||||
|
int position = 0;
|
||||||
|
for (int i=0; i<fileData.length; i++) {
|
||||||
|
byte byt = fileData[i];
|
||||||
|
if (byt != 0) {
|
||||||
|
if ((byt & 0x80) != 0) { // high bit set
|
||||||
|
workingData[position++] = (byte)(byt & 0x7f);
|
||||||
|
} else {
|
||||||
|
workingData[position++] = byt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] filteredData = new byte[position];
|
||||||
|
System.arraycopy(workingData, 0, filteredData, 0, filteredData.length);
|
||||||
|
return filteredData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give suggested file name.
|
||||||
|
*/
|
||||||
|
public String getSuggestedFileName(FileEntry fileEntry) {
|
||||||
|
String fileName = fileEntry.getFilename().trim();
|
||||||
|
if (!fileName.toLowerCase().endsWith(".txt")) {
|
||||||
|
fileName = fileName + ".txt";
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
223
src/com/webcodepro/applecommander/test/DiskHelperTest.java
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.ApplesoftFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.BinaryFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.Disk;
|
||||||
|
import com.webcodepro.applecommander.storage.FileEntry;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||||
|
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.IntegerBasicFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.TextFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk.DiskUsage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test DiskHelper and its related Apple2 classes.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 3, 2002 11:35:26 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DiskHelperTest extends TestCase {
|
||||||
|
|
||||||
|
public DiskHelperTest(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
junit.textui.TestRunner.run(DiskHelperTest.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoadDos33() throws IOException {
|
||||||
|
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/DOS 3.3.po");
|
||||||
|
assertApplesoftFile(disk, "HELLO");
|
||||||
|
assertIntegerFile(disk, "ANIMALS");
|
||||||
|
assertTextFile(disk, "APPLE PROMS");
|
||||||
|
assertBinaryFile(disk, "BOOT13");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoadMaster() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/MASTER.DSK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoadGalacticAttack1() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/galatt.dsk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoadProdos() throws IOException {
|
||||||
|
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/Prodos.dsk");
|
||||||
|
assertApplesoftFile(disk, "COPY.ME");
|
||||||
|
assertBinaryFile(disk, "SETTINGS");
|
||||||
|
assertBinaryFile(disk, "PRODOS");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoadMarbleMadness() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/Marble Madness (1985)(Electronic Arts).2mg");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoadHd1() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/ApplePC/hd1.hdv");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRdosBoot() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/RDOSboot.dsk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSsiSave() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/SSIsave.dsk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPhan2d1() throws IOException {
|
||||||
|
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/phan2d1.dsk");
|
||||||
|
assertApplesoftFile(disk, "PHANTASIE II");
|
||||||
|
assertBinaryFile(disk, "TWN21");
|
||||||
|
assertTextFile(disk, "ITEM");
|
||||||
|
assertGraphicsFile(disk, "ICE DRAGON");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPhan2d2() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/phan2d2.dsk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPhantasie1() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/Phantasie1.dsk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPhantasie2() throws IOException {
|
||||||
|
showDirectory("C:/My Apple2/Disks/Phantasie2.dsk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCavernsOfFreitag() throws IOException {
|
||||||
|
FormattedDisk disk = showDirectory("C:/My Apple2/Disks/CavernsOfFreitag.dsk");
|
||||||
|
assertGraphicsFile(disk, "TITLE.PIC");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FormattedDisk showDirectory(String imageName) throws IOException {
|
||||||
|
Disk disk = new Disk(imageName);
|
||||||
|
FormattedDisk formattedDisk = disk.getFormattedDisk();
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(formattedDisk.getDiskName());
|
||||||
|
List files = formattedDisk.getFiles();
|
||||||
|
if (files != null) {
|
||||||
|
showFiles(files, "");
|
||||||
|
}
|
||||||
|
System.out.println(formattedDisk.getFreeSpace() + " bytes free.");
|
||||||
|
System.out.println(formattedDisk.getUsedSpace() + " bytes used.");
|
||||||
|
System.out.println("This disk " + (formattedDisk.canHaveDirectories() ? "does" : "does not") +
|
||||||
|
" support directories.");
|
||||||
|
System.out.println("This disk is formatted in the " + formattedDisk.getFormat() + " format.");
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
showDiskUsage(formattedDisk);
|
||||||
|
|
||||||
|
return formattedDisk;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showFiles(List files, String indent) {
|
||||||
|
for (int i=0; i<files.size(); i++) {
|
||||||
|
FileEntry entry = (FileEntry) files.get(i);
|
||||||
|
if (!entry.isDeleted()) {
|
||||||
|
List data = entry.getFileColumnData(FormattedDisk.FILE_DISPLAY_NATIVE);
|
||||||
|
System.out.print(indent);
|
||||||
|
for (int d=0; d<data.size(); d++) {
|
||||||
|
System.out.print(data.get(d));
|
||||||
|
System.out.print(" ");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
showFiles(entry.getFiles(), indent + " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showDiskUsage(FormattedDisk disk) {
|
||||||
|
int[] dimensions = disk.getBitmapDimensions();
|
||||||
|
DiskUsage usage = disk.getDiskUsage();
|
||||||
|
if (usage == null) {
|
||||||
|
System.out.println("A bitmap is not available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dimensions == null) {
|
||||||
|
int i=0;
|
||||||
|
while (usage.hasNext()) {
|
||||||
|
if (i > 0 && i % 80 == 0) System.out.println();
|
||||||
|
usage.next();
|
||||||
|
System.out.print(usage.isFree() ? "." : "U");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
} else {
|
||||||
|
for (int y=dimensions[0]-1; y>=0; y--) {
|
||||||
|
for (int x=0; x<dimensions[1]; x++) {
|
||||||
|
usage.next();
|
||||||
|
System.out.print(usage.isFree() ? "." : "U");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("U = used, . = free");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertApplesoftFile(FormattedDisk disk, String filename) {
|
||||||
|
assertNotNull(filename + " test: Disk should not be null", disk);
|
||||||
|
FileEntry fileEntry = disk.getFile(filename);
|
||||||
|
assertNotNull(filename + " test: File not found", disk);
|
||||||
|
assertTrue("ApplesoftFileFilter was not chosen",
|
||||||
|
fileEntry.getSuggestedFilter() instanceof ApplesoftFileFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertIntegerFile(FormattedDisk disk, String filename) {
|
||||||
|
assertNotNull(filename + " test: Disk should not be null", disk);
|
||||||
|
FileEntry fileEntry = disk.getFile(filename);
|
||||||
|
assertNotNull(filename + " test: File not found", disk);
|
||||||
|
assertTrue("IntegerBasicFileFilter was not chosen",
|
||||||
|
fileEntry.getSuggestedFilter() instanceof IntegerBasicFileFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertTextFile(FormattedDisk disk, String filename) {
|
||||||
|
assertNotNull(filename + " test: Disk should not be null", disk);
|
||||||
|
FileEntry fileEntry = disk.getFile(filename);
|
||||||
|
assertNotNull(filename + " test: File not found", disk);
|
||||||
|
assertTrue("TextFileFilter was not chosen",
|
||||||
|
fileEntry.getSuggestedFilter() instanceof TextFileFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertBinaryFile(FormattedDisk disk, String filename) {
|
||||||
|
assertNotNull(filename + " test: Disk should not be null", disk);
|
||||||
|
FileEntry fileEntry = disk.getFile(filename);
|
||||||
|
assertNotNull(filename + " test: File not found", disk);
|
||||||
|
assertTrue("BinaryFileFilter was not chosen",
|
||||||
|
fileEntry.getSuggestedFilter() instanceof BinaryFileFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertGraphicsFile(FormattedDisk disk, String filename) {
|
||||||
|
assertNotNull(filename + " test: Disk should not be null", disk);
|
||||||
|
FileEntry fileEntry = disk.getFile(filename);
|
||||||
|
assertNotNull(filename + " test: File not found", disk);
|
||||||
|
assertTrue("GraphicsFileFilter was not chosen",
|
||||||
|
fileEntry.getSuggestedFilter() instanceof GraphicsFileFilter);
|
||||||
|
}
|
||||||
|
}
|
66
src/com/webcodepro/applecommander/ui/AppleCommander.java
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.ui.swt.SwtAppleCommander;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch AppleCommander.
|
||||||
|
* This application attempts to identify which type of user-interface to
|
||||||
|
* launch. Additionally, there are some command-line interface switches
|
||||||
|
* available - see the about method.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 16, 2002 9:13:25 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class AppleCommander {
|
||||||
|
public static final String VERSION = "1.1.1";
|
||||||
|
/**
|
||||||
|
* Launch AppleCommander.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length == 0) {
|
||||||
|
try {
|
||||||
|
Class.forName("org.eclipse.swt.SWT");
|
||||||
|
SwtAppleCommander.main(args);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
System.err.println("Sorry, the SWT libraries do not appear to be available (yet).");
|
||||||
|
//SwingAppleCommander.main(args);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String[] extraArgs = new String[args.length - 1];
|
||||||
|
System.arraycopy(args, 1, extraArgs, 0, extraArgs.length);
|
||||||
|
if ("-swt".equalsIgnoreCase(args[0])) {
|
||||||
|
SwtAppleCommander.main(extraArgs);
|
||||||
|
} else if ("-swing".equalsIgnoreCase(args[0])) {
|
||||||
|
System.err.println("Sorry, the Swing GUI is not available (yet).");
|
||||||
|
//SwingAppleCommander.main(extraArgs);
|
||||||
|
} else if ("-command".equalsIgnoreCase(args[0])) {
|
||||||
|
System.err.println("Sorry, the command line user interface is not available (yet).");
|
||||||
|
//CommandLineAppleCommander.main(extraArgs);
|
||||||
|
} else {
|
||||||
|
System.err.println("Unknown user interface specified!");
|
||||||
|
System.err.println("Use -swt, -swing, or -command.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
src/com/webcodepro/applecommander/ui/UserPreferences.java
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a generalized and common mechanism to handle user preferences throughout
|
||||||
|
* all AppleCommander user interfaces.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 18, 2002 10:08:34 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class UserPreferences {
|
||||||
|
private static final String FILENAME = "AppleCommander.preferences";
|
||||||
|
private static final String IMAGE_DIRECTORY = "imageDirectory";
|
||||||
|
private static final String EXPORT_DIRECTORY = "exportDirectory";
|
||||||
|
private static UserPreferences instance;
|
||||||
|
private Properties properties = new Properties();
|
||||||
|
/**
|
||||||
|
* Hide constructor from other classes.
|
||||||
|
*/
|
||||||
|
private UserPreferences() {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the singleton UserPreferences.
|
||||||
|
*/
|
||||||
|
public static UserPreferences getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new UserPreferences();
|
||||||
|
instance.load();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initialize the user preferences from disk.
|
||||||
|
*/
|
||||||
|
private void load() {
|
||||||
|
try {
|
||||||
|
FileInputStream inputStream = new FileInputStream(FILENAME);
|
||||||
|
properties.load(inputStream);
|
||||||
|
inputStream.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Save the user preferences to disk.
|
||||||
|
*/
|
||||||
|
public void save() {
|
||||||
|
try {
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(FILENAME);
|
||||||
|
properties.store(outputStream, "AppleCommander user preferences");
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the disk image directory.
|
||||||
|
*/
|
||||||
|
public String getDiskImageDirectory() {
|
||||||
|
return properties.getProperty(IMAGE_DIRECTORY);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the export directory.
|
||||||
|
*/
|
||||||
|
public String getExportDirectory() {
|
||||||
|
return properties.getProperty(EXPORT_DIRECTORY);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the disk image directory.
|
||||||
|
*/
|
||||||
|
public void setDiskImageDirectory(String diskImageDirectory) {
|
||||||
|
properties.setProperty(IMAGE_DIRECTORY, diskImageDirectory);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the export directory.
|
||||||
|
*/
|
||||||
|
public void setExportDirectory(String exportDirectory) {
|
||||||
|
properties.setProperty(EXPORT_DIRECTORY, exportDirectory);
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 10 KiB |
BIN
src/com/webcodepro/applecommander/ui/images/ExportWizardLogo.gif
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/com/webcodepro/applecommander/ui/images/about.gif
Normal file
After Width: | Height: | Size: 169 B |
BIN
src/com/webcodepro/applecommander/ui/images/appleicon.gif
Normal file
After Width: | Height: | Size: 887 B |
BIN
src/com/webcodepro/applecommander/ui/images/deletedfiles.gif
Normal file
After Width: | Height: | Size: 915 B |
BIN
src/com/webcodepro/applecommander/ui/images/deletefile.gif
Normal file
After Width: | Height: | Size: 143 B |
BIN
src/com/webcodepro/applecommander/ui/images/detailfileview.gif
Normal file
After Width: | Height: | Size: 916 B |
BIN
src/com/webcodepro/applecommander/ui/images/diskicon.gif
Normal file
After Width: | Height: | Size: 863 B |
BIN
src/com/webcodepro/applecommander/ui/images/exportfile.gif
Normal file
After Width: | Height: | Size: 877 B |
BIN
src/com/webcodepro/applecommander/ui/images/importfile.gif
Normal file
After Width: | Height: | Size: 879 B |
BIN
src/com/webcodepro/applecommander/ui/images/nativefileview.gif
Normal file
After Width: | Height: | Size: 887 B |
BIN
src/com/webcodepro/applecommander/ui/images/newdisk.gif
Normal file
After Width: | Height: | Size: 896 B |
BIN
src/com/webcodepro/applecommander/ui/images/opendisk.gif
Normal file
After Width: | Height: | Size: 907 B |
BIN
src/com/webcodepro/applecommander/ui/images/saveimage.gif
Normal file
After Width: | Height: | Size: 187 B |
BIN
src/com/webcodepro/applecommander/ui/images/standardfileview.gif
Normal file
After Width: | Height: | Size: 878 B |
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.layout.RowLayout;
|
||||||
|
import org.eclipse.swt.widgets.Button;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.AppleWorksWordProcessorFileFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose format for AppleWorks Word Processor export.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 15, 2002 11:31:15 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class AppleWorksWordProcessorPane extends WizardPane {
|
||||||
|
private Composite parent;
|
||||||
|
private Object layoutData;
|
||||||
|
private Composite control;
|
||||||
|
private ExportWizard wizard;
|
||||||
|
/**
|
||||||
|
* Constructor for AppleWorksWordProcessorPane.
|
||||||
|
*/
|
||||||
|
public AppleWorksWordProcessorPane(Composite parent, ExportWizard exportWizard, Object layoutData) {
|
||||||
|
super();
|
||||||
|
this.parent = parent;
|
||||||
|
this.wizard = exportWizard;
|
||||||
|
this.layoutData = layoutData;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the next WizardPane.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
|
||||||
|
*/
|
||||||
|
public WizardPane getNextPane() {
|
||||||
|
return new ExportFileDestinationPane(parent, wizard, layoutData);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create and display the wizard pane.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#open()
|
||||||
|
*/
|
||||||
|
public void open() {
|
||||||
|
wizard.enableFinishButton(false);
|
||||||
|
wizard.enableNextButton(true);
|
||||||
|
control = new Composite(parent, SWT.NULL);
|
||||||
|
control.setLayoutData(layoutData);
|
||||||
|
RowLayout layout = new RowLayout(SWT.VERTICAL);
|
||||||
|
layout.justify = true;
|
||||||
|
layout.marginBottom = 5;
|
||||||
|
layout.marginLeft = 5;
|
||||||
|
layout.marginRight = 5;
|
||||||
|
layout.marginTop = 5;
|
||||||
|
layout.spacing = 3;
|
||||||
|
control.setLayout(layout);
|
||||||
|
Label label = new Label(control, SWT.WRAP);
|
||||||
|
label.setText("Please choose the appropriate format:");
|
||||||
|
RowLayout subpanelLayout = new RowLayout(SWT.VERTICAL);
|
||||||
|
subpanelLayout.justify = true;
|
||||||
|
subpanelLayout.spacing = 3;
|
||||||
|
Button button = new Button(control, SWT.RADIO);
|
||||||
|
button.setText("Text");
|
||||||
|
button.setSelection(getFilter().isTextRendering());
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
getFilter().setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_TEXT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(control, SWT.RADIO);
|
||||||
|
button.setText("HTML");
|
||||||
|
button.setSelection(getFilter().isHtmlRendering());
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
getFilter().setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_HTML);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of any resources.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
control.dispose();
|
||||||
|
control = null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the AppleWorks word processor filter.
|
||||||
|
*/
|
||||||
|
protected AppleWorksWordProcessorFileFilter getFilter() {
|
||||||
|
return (AppleWorksWordProcessorFileFilter) wizard.getFileFilter();
|
||||||
|
}
|
||||||
|
}
|
836
src/com/webcodepro/applecommander/ui/swt/DiskExplorerTab.java
Normal file
|
@ -0,0 +1,836 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.AppleWorksWordProcessorFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.ApplesoftFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.BinaryFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.FileEntry;
|
||||||
|
import com.webcodepro.applecommander.storage.FileEntryComparator;
|
||||||
|
import com.webcodepro.applecommander.storage.FileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||||
|
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.IntegerBasicFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.TextFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk.FileColumnHeader;
|
||||||
|
import com.webcodepro.applecommander.ui.UserPreferences;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.custom.CTabFolder;
|
||||||
|
import org.eclipse.swt.custom.CTabItem;
|
||||||
|
import org.eclipse.swt.custom.SashForm;
|
||||||
|
import org.eclipse.swt.events.MenuAdapter;
|
||||||
|
import org.eclipse.swt.events.MenuEvent;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.events.SelectionListener;
|
||||||
|
import org.eclipse.swt.graphics.GC;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.FileDialog;
|
||||||
|
import org.eclipse.swt.widgets.Menu;
|
||||||
|
import org.eclipse.swt.widgets.MenuItem;
|
||||||
|
import org.eclipse.swt.widgets.MessageBox;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
import org.eclipse.swt.widgets.Table;
|
||||||
|
import org.eclipse.swt.widgets.TableColumn;
|
||||||
|
import org.eclipse.swt.widgets.TableItem;
|
||||||
|
import org.eclipse.swt.widgets.ToolBar;
|
||||||
|
import org.eclipse.swt.widgets.ToolItem;
|
||||||
|
import org.eclipse.swt.widgets.Tree;
|
||||||
|
import org.eclipse.swt.widgets.TreeItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Disk File tab for the Disk Window.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 17, 2002 9:46:53 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DiskExplorerTab {
|
||||||
|
private Shell shell;
|
||||||
|
private SashForm sashForm;
|
||||||
|
private Tree directoryTree;
|
||||||
|
private Table fileTable;
|
||||||
|
private ToolBar toolBar;
|
||||||
|
private ToolItem exportToolItem;
|
||||||
|
private ToolItem importToolItem;
|
||||||
|
private ToolItem deleteToolItem;
|
||||||
|
private ImageManager imageManager;
|
||||||
|
|
||||||
|
private UserPreferences userPreferences = UserPreferences.getInstance();
|
||||||
|
private FormattedDisk disk;
|
||||||
|
private FileFilter fileFilter;
|
||||||
|
private GraphicsFileFilter graphicsFilter = new GraphicsFileFilter();
|
||||||
|
private AppleWorksWordProcessorFileFilter awpFilter = new AppleWorksWordProcessorFileFilter();
|
||||||
|
|
||||||
|
private int currentFormat = FormattedDisk.FILE_DISPLAY_STANDARD;
|
||||||
|
private boolean formatChanged;
|
||||||
|
private List currentFileList;
|
||||||
|
private Map columnWidths = new HashMap();
|
||||||
|
private boolean showDeletedFiles;
|
||||||
|
/**
|
||||||
|
* Create the DISK INFO tab.
|
||||||
|
*/
|
||||||
|
public DiskExplorerTab(CTabFolder tabFolder, FormattedDisk disk, ImageManager imageManager) {
|
||||||
|
this.disk = disk;
|
||||||
|
this.shell = tabFolder.getShell();
|
||||||
|
this.imageManager = imageManager;
|
||||||
|
|
||||||
|
createFilesTab(tabFolder);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of resources.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
sashForm.dispose();
|
||||||
|
directoryTree.dispose();
|
||||||
|
fileTable.dispose();
|
||||||
|
exportToolItem.dispose();
|
||||||
|
importToolItem.dispose();
|
||||||
|
deleteToolItem.dispose();
|
||||||
|
toolBar.dispose();
|
||||||
|
|
||||||
|
directoryTree = null;
|
||||||
|
fileTable = null;
|
||||||
|
currentFileList = null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create the FILES tab.
|
||||||
|
*/
|
||||||
|
protected void createFilesTab(CTabFolder tabFolder) {
|
||||||
|
CTabItem ctabitem = new CTabItem(tabFolder, SWT.NULL);
|
||||||
|
ctabitem.setText("Files");
|
||||||
|
|
||||||
|
Composite composite = new Composite(tabFolder, SWT.NULL);
|
||||||
|
ctabitem.setControl(composite);
|
||||||
|
GridLayout gridLayout = new GridLayout(1, false);
|
||||||
|
composite.setLayout(gridLayout);
|
||||||
|
|
||||||
|
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
|
||||||
|
createFileToolBar(composite, gridData);
|
||||||
|
|
||||||
|
sashForm = new SashForm(composite, SWT.NONE);
|
||||||
|
sashForm.setOrientation(SWT.HORIZONTAL);
|
||||||
|
gridData = new GridData(GridData.FILL_BOTH);
|
||||||
|
gridData.horizontalSpan = 2;
|
||||||
|
sashForm.setLayoutData(gridData);
|
||||||
|
|
||||||
|
directoryTree = new Tree(sashForm, SWT.SINGLE | SWT.BORDER);
|
||||||
|
directoryTree.setMenu(createDirectoryPopupMenu());
|
||||||
|
directoryTree.addSelectionListener(new SelectionListener() {
|
||||||
|
/**
|
||||||
|
* Single-click handler.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
changeCurrentFormat(currentFormat); // minor hack
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Double-click handler.
|
||||||
|
*/
|
||||||
|
public void widgetDefaultSelected(SelectionEvent event) {
|
||||||
|
Tree item = (Tree) event.getSource();
|
||||||
|
TreeItem[] treeItem = item.getSelection();
|
||||||
|
treeItem[0].setExpanded(!treeItem[0].getExpanded());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fileTable = new Table(sashForm, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
|
||||||
|
fileTable.setHeaderVisible(true);
|
||||||
|
|
||||||
|
sashForm.setWeights(new int[] {1,2});
|
||||||
|
|
||||||
|
TreeItem diskItem = new TreeItem(directoryTree, SWT.BORDER);
|
||||||
|
diskItem.setText(disk.getDiskName());
|
||||||
|
diskItem.setData(disk);
|
||||||
|
directoryTree.setSelection(new TreeItem[] { diskItem });
|
||||||
|
|
||||||
|
if (disk.canHaveDirectories()) {
|
||||||
|
Iterator files = disk.getFiles().iterator();
|
||||||
|
while (files.hasNext()) {
|
||||||
|
FileEntry entry = (FileEntry) files.next();
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
TreeItem item = new TreeItem(diskItem, SWT.BORDER);
|
||||||
|
item.setText(entry.getFilename());
|
||||||
|
item.setData(entry);
|
||||||
|
addDirectoriesToTree(item, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
computeColumnWidths(FormattedDisk.FILE_DISPLAY_STANDARD);
|
||||||
|
computeColumnWidths(FormattedDisk.FILE_DISPLAY_NATIVE);
|
||||||
|
computeColumnWidths(FormattedDisk.FILE_DISPLAY_DETAIL);
|
||||||
|
|
||||||
|
formatChanged = true;
|
||||||
|
fillFileTable(disk.getFiles());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Construct the popup menu for the directory table on the File tab.
|
||||||
|
*/
|
||||||
|
protected Menu createDirectoryPopupMenu() {
|
||||||
|
Menu menu = new Menu(shell, SWT.POP_UP);
|
||||||
|
|
||||||
|
MenuItem item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Expand");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
TreeItem[] treeItem = directoryTree.getSelection();
|
||||||
|
treeItem[0].setExpanded(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.setEnabled(disk.canHaveDirectories());
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Collapse");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
TreeItem[] treeItem = directoryTree.getSelection();
|
||||||
|
treeItem[0].setExpanded(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.setEnabled(disk.canHaveDirectories());
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Expand All");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
TreeItem[] treeItem = directoryTree.getSelection();
|
||||||
|
setDirectoryExpandedStates(treeItem[0], true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.setEnabled(disk.canHaveDirectories());
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Collapse All");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
TreeItem[] treeItem = directoryTree.getSelection();
|
||||||
|
setDirectoryExpandedStates(treeItem[0], false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.setEnabled(disk.canHaveDirectories());
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Construct the popup menu for the file table on the File tab.
|
||||||
|
*/
|
||||||
|
protected Menu createFilePopupMenu() {
|
||||||
|
Menu menu = new Menu(shell, SWT.POP_UP);
|
||||||
|
|
||||||
|
MenuItem item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Import...");
|
||||||
|
item.setEnabled(disk.canCreateFile() && disk.canWriteFileData());
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Export");
|
||||||
|
item.setEnabled(disk.canReadFileData());
|
||||||
|
item.setMenu(createFileExportMenu(SWT.DROP_DOWN));
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Delete...");
|
||||||
|
item.setEnabled(disk.canDeleteFile());
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Construct the popup menu for the export button on the toolbar.
|
||||||
|
*/
|
||||||
|
protected Menu createFileExportMenu(int style) {
|
||||||
|
Menu menu = new Menu(shell, style);
|
||||||
|
|
||||||
|
MenuItem item = new MenuItem(menu, SWT.NONE);
|
||||||
|
item.setText("Raw disk data...");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
fileFilter = null;
|
||||||
|
exportFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.NONE);
|
||||||
|
item.setText("Binary...");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
fileFilter = new BinaryFileFilter();
|
||||||
|
exportFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.NONE);
|
||||||
|
item.setText("Applesoft Basic...");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
fileFilter = new ApplesoftFileFilter();
|
||||||
|
exportFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.NONE);
|
||||||
|
item.setText("Integer Basic...");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
fileFilter = new IntegerBasicFileFilter();
|
||||||
|
exportFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.NONE);
|
||||||
|
item.setText("ASCII Text...");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
fileFilter = new TextFileFilter();
|
||||||
|
exportFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.NONE);
|
||||||
|
item.setText("AppleWorks WordProcessor File...");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
fileFilter = awpFilter;
|
||||||
|
exportFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Rendering");
|
||||||
|
Menu subMenu = new Menu(shell, SWT.DROP_DOWN);
|
||||||
|
item.setMenu(subMenu);
|
||||||
|
subMenu.addMenuListener(new MenuAdapter() {
|
||||||
|
/**
|
||||||
|
* Toggle all sub-menu MenuItems to the proper state to reflect
|
||||||
|
* the current file extension chosen.
|
||||||
|
*/
|
||||||
|
public void menuShown(MenuEvent event) {
|
||||||
|
Menu theMenu = (Menu) event.getSource();
|
||||||
|
MenuItem[] subItems = theMenu.getItems();
|
||||||
|
subItems[0].setSelection(awpFilter.isTextRendering());
|
||||||
|
subItems[1].setSelection(awpFilter.isHtmlRendering());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new MenuItem(subMenu, SWT.RADIO);
|
||||||
|
item.setText("Text");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
/**
|
||||||
|
* Set the appropriate rendering style.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
awpFilter.setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_TEXT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new MenuItem(subMenu, SWT.RADIO);
|
||||||
|
item.setText("HTML");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
/**
|
||||||
|
* Set the appropriate rendering style.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
awpFilter.setRendering(AppleWorksWordProcessorFileFilter.RENDER_AS_HTML);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new MenuItem(menu, SWT.NONE);
|
||||||
|
item.setText("Graphics...");
|
||||||
|
item.setEnabled(graphicsFilter.isCodecAvailable());
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
fileFilter = graphicsFilter;
|
||||||
|
exportFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add graphics mode
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Mode");
|
||||||
|
item.setEnabled(graphicsFilter.isCodecAvailable());
|
||||||
|
subMenu = new Menu(shell, SWT.DROP_DOWN);
|
||||||
|
item.setMenu(subMenu);
|
||||||
|
subMenu.addMenuListener(new MenuAdapter() {
|
||||||
|
/**
|
||||||
|
* Toggle all sub-menu MenuItems to the proper state to reflect
|
||||||
|
* the current file extension chosen.
|
||||||
|
*/
|
||||||
|
public void menuShown(MenuEvent event) {
|
||||||
|
Menu theMenu = (Menu) event.getSource();
|
||||||
|
MenuItem[] subItems = theMenu.getItems();
|
||||||
|
subItems[0].setSelection(graphicsFilter.isHiresBlackAndWhiteMode());
|
||||||
|
subItems[1].setSelection(graphicsFilter.isHiresColorMode());
|
||||||
|
subItems[2].setSelection(graphicsFilter.isDoubleHiresBlackAndWhiteMode());
|
||||||
|
subItems[3].setSelection(graphicsFilter.isDoubleHiresColorMode());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new MenuItem(subMenu, SWT.RADIO);
|
||||||
|
item.setText("Hi-Res B&W");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
/**
|
||||||
|
* Set the appropriate graphics mode.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
graphicsFilter.setMode(GraphicsFileFilter.MODE_HGR_BLACK_AND_WHITE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new MenuItem(subMenu, SWT.RADIO);
|
||||||
|
item.setText("Hi-Res Color");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
/**
|
||||||
|
* Set the appropriate graphics mode.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
graphicsFilter.setMode(GraphicsFileFilter.MODE_HGR_COLOR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new MenuItem(subMenu, SWT.RADIO);
|
||||||
|
item.setText("Double Hi-Res B&W");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
/**
|
||||||
|
* Set the appropriate graphics mode.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
graphicsFilter.setMode(GraphicsFileFilter.MODE_DHR_BLACK_AND_WHITE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new MenuItem(subMenu, SWT.RADIO);
|
||||||
|
item.setText("Double Hi-Res COLOR");
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
/**
|
||||||
|
* Set the appropriate graphics mode.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
graphicsFilter.setMode(GraphicsFileFilter.MODE_DHR_COLOR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add graphics formats, if any are defined.
|
||||||
|
String[] formats = graphicsFilter.getFileExtensions();
|
||||||
|
if (formats != null && formats.length > 0) {
|
||||||
|
item = new MenuItem(menu, SWT.CASCADE);
|
||||||
|
item.setText("Format");
|
||||||
|
item.setEnabled(graphicsFilter.isCodecAvailable());
|
||||||
|
subMenu = new Menu(shell, SWT.DROP_DOWN);
|
||||||
|
item.setMenu(subMenu);
|
||||||
|
subMenu.addMenuListener(new MenuAdapter() {
|
||||||
|
/**
|
||||||
|
* Toggle all sub-menu MenuItems to the proper state to reflect
|
||||||
|
* the current file extension chosen.
|
||||||
|
*/
|
||||||
|
public void menuShown(MenuEvent event) {
|
||||||
|
Menu theMenu = (Menu) event.getSource();
|
||||||
|
MenuItem[] subItems = theMenu.getItems();
|
||||||
|
for (int i=0; i<subItems.length; i++) {
|
||||||
|
subItems[i].setSelection(subItems[i].getText().
|
||||||
|
equals(graphicsFilter.getExtension()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Add all graphics formats...
|
||||||
|
for (int i=0; i<formats.length; i++) {
|
||||||
|
item = new MenuItem(subMenu, SWT.RADIO);
|
||||||
|
item.setText(formats[i]);
|
||||||
|
item.addSelectionListener(new SelectionAdapter() {
|
||||||
|
/**
|
||||||
|
* Set the file extension to use.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
MenuItem menuItem = (MenuItem) event.getSource();
|
||||||
|
graphicsFilter.setExtension(menuItem.getText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Change the "expanded" state of the node.
|
||||||
|
*/
|
||||||
|
protected void setDirectoryExpandedStates(TreeItem treeItem, boolean expand) {
|
||||||
|
treeItem.setExpanded(expand);
|
||||||
|
TreeItem[] treeItems = treeItem.getItems();
|
||||||
|
for (int i=0; i<treeItems.length; i++) {
|
||||||
|
setDirectoryExpandedStates(treeItems[i], expand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Pre-compute column widths for the file tab.
|
||||||
|
* These can and are over-ridden by user sizing.
|
||||||
|
*/
|
||||||
|
protected void computeColumnWidths(int format) {
|
||||||
|
List headers = disk.getFileColumnHeaders(format);
|
||||||
|
int[] headerWidths = new int[headers.size()];
|
||||||
|
GC gc = new GC(shell);
|
||||||
|
for (int i=0; i<headers.size(); i++) {
|
||||||
|
FileColumnHeader header = (FileColumnHeader) headers.get(i);
|
||||||
|
if (header.getTitle().length() >= header.getMaximumWidth()) {
|
||||||
|
headerWidths[i] = gc.stringExtent(header.getTitle()).x + gc.stringExtent("WW").x;
|
||||||
|
} else {
|
||||||
|
headerWidths[i] = gc.stringExtent("W").x * header.getMaximumWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gc.dispose();
|
||||||
|
gc = null;
|
||||||
|
columnWidths.put(new Integer(format), headerWidths);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Preserve the column widths.
|
||||||
|
*/
|
||||||
|
protected void preserveColumnWidths() {
|
||||||
|
TableColumn[] columns = fileTable.getColumns();
|
||||||
|
int[] widths = new int[columns.length];
|
||||||
|
for (int i=0; i<columns.length; i++) {
|
||||||
|
widths[i] = columns[i].getWidth();
|
||||||
|
}
|
||||||
|
columnWidths.put(new Integer(currentFormat), widths);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Display files in the fileTable.
|
||||||
|
*/
|
||||||
|
protected void fillFileTable(List fileList) {
|
||||||
|
int[] weights = sashForm.getWeights();
|
||||||
|
|
||||||
|
if (formatChanged) {
|
||||||
|
fileTable.dispose();
|
||||||
|
fileTable = new Table(sashForm, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
|
||||||
|
fileTable.setHeaderVisible(true);
|
||||||
|
fileTable.setMenu(createFilePopupMenu());
|
||||||
|
fileTable.addSelectionListener(new SelectionListener() {
|
||||||
|
/**
|
||||||
|
* Single-click handler.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
if (fileTable.getSelectionCount() > 0) {
|
||||||
|
exportToolItem.setEnabled(disk.canReadFileData());
|
||||||
|
importToolItem.setEnabled(disk.canCreateFile() && disk.canWriteFileData());
|
||||||
|
deleteToolItem.setEnabled(disk.canDeleteFile());
|
||||||
|
} else {
|
||||||
|
exportToolItem.setEnabled(false);
|
||||||
|
importToolItem.setEnabled(false);
|
||||||
|
deleteToolItem.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Double-click handler.
|
||||||
|
*/
|
||||||
|
public void widgetDefaultSelected(SelectionEvent event) {
|
||||||
|
// No action defined at this time
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TableColumn column = null;
|
||||||
|
List headers = disk.getFileColumnHeaders(currentFormat);
|
||||||
|
int[] widths = (int[])columnWidths.get(new Integer(currentFormat));
|
||||||
|
for (int i=0; i<headers.size(); i++) {
|
||||||
|
FileColumnHeader header = (FileColumnHeader) headers.get(i);
|
||||||
|
int align = header.isCenterAlign() ? SWT.CENTER :
|
||||||
|
header.isLeftAlign() ? SWT.LEFT : SWT.RIGHT;
|
||||||
|
column = new TableColumn(fileTable, align);
|
||||||
|
column.setText(header.getTitle());
|
||||||
|
column.setWidth(widths[i]);
|
||||||
|
final int columnIndex = i;
|
||||||
|
column.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
sortFileTable(columnIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileTable.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator files = fileList.iterator();
|
||||||
|
while (files.hasNext()) {
|
||||||
|
FileEntry entry = (FileEntry) files.next();
|
||||||
|
if (showDeletedFiles || !entry.isDeleted()) {
|
||||||
|
TableItem item = new TableItem(fileTable, 0);
|
||||||
|
List data = entry.getFileColumnData(currentFormat);
|
||||||
|
for (int i=0; i<data.size(); i++) {
|
||||||
|
item.setText(i, (String)data.get(i));
|
||||||
|
}
|
||||||
|
item.setData(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sashForm.setWeights(weights);
|
||||||
|
formatChanged = false;
|
||||||
|
currentFileList = fileList;
|
||||||
|
|
||||||
|
// disable all file-leve operations:
|
||||||
|
exportToolItem.setEnabled(false);
|
||||||
|
importToolItem.setEnabled(false);
|
||||||
|
deleteToolItem.setEnabled(false);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Export all selected files.
|
||||||
|
*/
|
||||||
|
private void exportFile(String directory) {
|
||||||
|
boolean promptForIndividualFiles = (directory == null);
|
||||||
|
TableItem[] selection = fileTable.getSelection();
|
||||||
|
for (int i=0; i<selection.length; i++) {
|
||||||
|
TableItem tableItem = selection[i];
|
||||||
|
FileEntry fileEntry = (FileEntry) tableItem.getData();
|
||||||
|
String filename = null;
|
||||||
|
if (promptForIndividualFiles) {
|
||||||
|
FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
|
||||||
|
fileDialog.setFilterPath(userPreferences.getExportDirectory());
|
||||||
|
if (fileFilter != null) {
|
||||||
|
fileDialog.setFileName(fileFilter.getSuggestedFileName(fileEntry));
|
||||||
|
} else {
|
||||||
|
fileDialog.setFileName(fileEntry.getFilename());
|
||||||
|
}
|
||||||
|
filename = fileDialog.open();
|
||||||
|
directory = fileDialog.getFilterPath();
|
||||||
|
} else {
|
||||||
|
filename = directory + File.separator + fileFilter.getSuggestedFileName(fileEntry);
|
||||||
|
}
|
||||||
|
if (filename != null) {
|
||||||
|
userPreferences.setExportDirectory(directory);
|
||||||
|
try {
|
||||||
|
File file = new File(filename);
|
||||||
|
if (file.exists()) {
|
||||||
|
Shell finalShell = shell;
|
||||||
|
MessageBox box = new MessageBox(finalShell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
|
||||||
|
box.setText("File already exists!");
|
||||||
|
box.setMessage(
|
||||||
|
"The file '" + filename + "' already exists. "
|
||||||
|
+ "Do you want to over-write it?");
|
||||||
|
if (box.open() == SWT.NO) {
|
||||||
|
return; // do not overwrite file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] data = null;
|
||||||
|
if (fileFilter != null) {
|
||||||
|
data = fileFilter.filter(fileEntry);
|
||||||
|
} else {
|
||||||
|
data = disk.getFileData(fileEntry);
|
||||||
|
}
|
||||||
|
OutputStream outputStream = new FileOutputStream(file);
|
||||||
|
outputStream.write(data);
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Shell finalShell = shell;
|
||||||
|
String errorMessage = ex.getMessage();
|
||||||
|
if (errorMessage == null) {
|
||||||
|
errorMessage = ex.getClass().getName();
|
||||||
|
}
|
||||||
|
MessageBox box = new MessageBox(finalShell,
|
||||||
|
SWT.ICON_ERROR | SWT.OK | SWT.CANCEL);
|
||||||
|
box.setText("Unable to export file data!");
|
||||||
|
box.setMessage(
|
||||||
|
"Unable to export '" + filename + "'.\n\n"
|
||||||
|
+ "AppleCommander was unable to save the disk\n"
|
||||||
|
+ "data. The system error given was '"
|
||||||
|
+ errorMessage + "'\n\n"
|
||||||
|
+ "Sorry!\n\n"
|
||||||
|
+ "Press OK to continue export or CANCEL to cancel export.");
|
||||||
|
int button = box.open();
|
||||||
|
if (button == SWT.CANCEL) break; // break out of loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sort the file table by the specified columnIndex.
|
||||||
|
*/
|
||||||
|
protected void sortFileTable(int columnIndex) {
|
||||||
|
Collections.sort(currentFileList, new FileEntryComparator(columnIndex, currentFormat));
|
||||||
|
fillFileTable(currentFileList);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Helper function for building fileTree.
|
||||||
|
*/
|
||||||
|
protected void addDirectoriesToTree(TreeItem directoryItem, FileEntry directoryEntry) {
|
||||||
|
Iterator files = directoryEntry.getFiles().iterator();
|
||||||
|
while (files.hasNext()) {
|
||||||
|
final FileEntry entry = (FileEntry) files.next();
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
TreeItem item = new TreeItem(directoryItem, SWT.BORDER);
|
||||||
|
item.setText(entry.getFilename());
|
||||||
|
item.setData(entry);
|
||||||
|
addDirectoriesToTree(item, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates the FILE tab toolbar.
|
||||||
|
*/
|
||||||
|
private void createFileToolBar(Composite composite, Object layoutData) {
|
||||||
|
toolBar = new ToolBar(composite, SWT.FLAT);
|
||||||
|
if (layoutData != null) toolBar.setLayoutData(layoutData);
|
||||||
|
|
||||||
|
ToolItem item = new ToolItem(toolBar, SWT.RADIO);
|
||||||
|
item.setImage(imageManager.getStandardFileViewIcon());
|
||||||
|
item.setText("Standard");
|
||||||
|
item.setToolTipText("Displays files in standard format");
|
||||||
|
item.setSelection(true);
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
changeCurrentFormat(FormattedDisk.FILE_DISPLAY_STANDARD);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.RADIO);
|
||||||
|
item.setImage(imageManager.getNativeFileViewIcon());
|
||||||
|
item.setText("Native");
|
||||||
|
item.setToolTipText("Displays files in native format for the operating system");
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
changeCurrentFormat(FormattedDisk.FILE_DISPLAY_NATIVE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.RADIO);
|
||||||
|
item.setImage(imageManager.getDetailFileViewIcon());
|
||||||
|
item.setText("Detail");
|
||||||
|
item.setToolTipText("Displays files in with full details");
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
changeCurrentFormat(FormattedDisk.FILE_DISPLAY_DETAIL);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new ToolItem(toolBar, SWT.CHECK);
|
||||||
|
item.setImage(imageManager.getDeletedFilesIcon());
|
||||||
|
item.setText("Deleted");
|
||||||
|
item.setToolTipText("Show deleted files");
|
||||||
|
item.setEnabled(disk.supportsDeletedFiles());
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
ToolItem button = (ToolItem) e.getSource();
|
||||||
|
showDeletedFiles = button.getSelection();
|
||||||
|
fillFileTable(currentFileList);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
importToolItem = new ToolItem(toolBar, SWT.PUSH);
|
||||||
|
importToolItem.setImage(imageManager.getImportFileIcon());
|
||||||
|
importToolItem.setText("Import...");
|
||||||
|
importToolItem.setToolTipText("Import a file");
|
||||||
|
importToolItem.setEnabled(false);
|
||||||
|
importToolItem.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exportToolItem = new ToolItem(toolBar, SWT.DROP_DOWN);
|
||||||
|
exportToolItem.setImage(imageManager.getExportFileIcon());
|
||||||
|
exportToolItem.setText("Export...");
|
||||||
|
exportToolItem.setToolTipText("Export a file");
|
||||||
|
exportToolItem.setEnabled(false);
|
||||||
|
exportToolItem.addSelectionListener(
|
||||||
|
new DropDownSelectionListener(createFileExportMenu(SWT.NONE)));
|
||||||
|
exportToolItem.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
if (event.detail != SWT.ARROW) {
|
||||||
|
ExportWizard wizard = new ExportWizard(shell,
|
||||||
|
imageManager.getExportWizardLogo(), disk);
|
||||||
|
// Get a suggseted filter, if possible:
|
||||||
|
FileEntry fileEntry = (FileEntry) fileTable.getSelection()[0].getData();
|
||||||
|
if (fileEntry != null) {
|
||||||
|
fileFilter = fileEntry.getSuggestedFilter();
|
||||||
|
}
|
||||||
|
// Start wizard:
|
||||||
|
wizard.setFileFilter(fileFilter);
|
||||||
|
wizard.setDirectory(userPreferences.getExportDirectory());
|
||||||
|
wizard.open();
|
||||||
|
if (wizard.isWizardCompleted()) {
|
||||||
|
fileFilter = wizard.getFileFilter();
|
||||||
|
String exportDirectory = wizard.getDirectory();
|
||||||
|
exportFile(exportDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
deleteToolItem = new ToolItem(toolBar, SWT.PUSH);
|
||||||
|
deleteToolItem.setImage(imageManager.getDeleteFileIcon());
|
||||||
|
deleteToolItem.setText("Delete");
|
||||||
|
deleteToolItem.setToolTipText("Delete a file");
|
||||||
|
deleteToolItem.setEnabled(false);
|
||||||
|
deleteToolItem.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new ToolItem(toolBar, SWT.PUSH);
|
||||||
|
item.setImage(imageManager.getSaveImageIcon());
|
||||||
|
item.setText("Save");
|
||||||
|
item.setToolTipText("Save disk image");
|
||||||
|
item.setEnabled(false);
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
// not available yet
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
toolBar.pack();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Change the current format and refresh the display.
|
||||||
|
*/
|
||||||
|
protected void changeCurrentFormat(int newFormat) {
|
||||||
|
TreeItem selection = directoryTree.getSelection()[0];
|
||||||
|
Object data = selection.getData();
|
||||||
|
List fileList = null;
|
||||||
|
if (data instanceof FileEntry) {
|
||||||
|
FileEntry directory = (FileEntry) data;
|
||||||
|
fileList = directory.getFiles();
|
||||||
|
} else if (data instanceof FormattedDisk) {
|
||||||
|
FormattedDisk disk = (FormattedDisk) data;
|
||||||
|
fileList = disk.getFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
formatChanged = (currentFormat != newFormat);
|
||||||
|
if (formatChanged || !fileList.equals(currentFileList)) {
|
||||||
|
preserveColumnWidths(); // must be done before assigning newFormat
|
||||||
|
currentFormat = newFormat;
|
||||||
|
fillFileTable(fileList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/com/webcodepro/applecommander/ui/swt/DiskInfoTab.java
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.custom.CTabFolder;
|
||||||
|
import org.eclipse.swt.custom.CTabItem;
|
||||||
|
import org.eclipse.swt.widgets.Table;
|
||||||
|
import org.eclipse.swt.widgets.TableColumn;
|
||||||
|
import org.eclipse.swt.widgets.TableItem;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk.DiskInformation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Disk Info tab for the Disk Window.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 17, 2002 9:15:29 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DiskInfoTab {
|
||||||
|
/**
|
||||||
|
* Create the DISK INFO tab.
|
||||||
|
*/
|
||||||
|
public DiskInfoTab(CTabFolder tabFolder, FormattedDisk disk) {
|
||||||
|
CTabItem ctabitem = new CTabItem(tabFolder, SWT.NULL);
|
||||||
|
ctabitem.setText("Disk Info");
|
||||||
|
|
||||||
|
Table table = new Table(tabFolder, SWT.FULL_SELECTION);
|
||||||
|
ctabitem.setControl(table);
|
||||||
|
table.setHeaderVisible(true);
|
||||||
|
TableColumn column = new TableColumn(table, SWT.LEFT);
|
||||||
|
column.setResizable(true);
|
||||||
|
column.setText("Label");
|
||||||
|
column.setWidth(200);
|
||||||
|
column = new TableColumn(table, SWT.LEFT);
|
||||||
|
column.setResizable(true);
|
||||||
|
column.setText("Value");
|
||||||
|
column.setWidth(400);
|
||||||
|
|
||||||
|
Iterator iterator = disk.getDiskInformation().iterator();
|
||||||
|
TableItem item = null;
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
DiskInformation diskinfo = (DiskInformation) iterator.next();
|
||||||
|
item = new TableItem(table, SWT.NULL);
|
||||||
|
item.setText(new String[] { diskinfo.getLabel(), diskinfo.getValue() });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of resources.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
}
|
||||||
|
}
|
351
src/com/webcodepro/applecommander/ui/swt/DiskMapTab.java
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.custom.CTabFolder;
|
||||||
|
import org.eclipse.swt.custom.CTabItem;
|
||||||
|
import org.eclipse.swt.events.PaintEvent;
|
||||||
|
import org.eclipse.swt.events.PaintListener;
|
||||||
|
import org.eclipse.swt.graphics.Color;
|
||||||
|
import org.eclipse.swt.graphics.GC;
|
||||||
|
import org.eclipse.swt.graphics.Image;
|
||||||
|
import org.eclipse.swt.graphics.Point;
|
||||||
|
import org.eclipse.swt.graphics.Rectangle;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.Canvas;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk.DiskUsage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Disk Map tab for the Disk Window.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 17, 2002 9:27:44 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DiskMapTab {
|
||||||
|
private FormattedDisk disk;
|
||||||
|
// used locally - not shared between windows; hopefully will
|
||||||
|
// not be a resource drain!
|
||||||
|
private Color freeFill;
|
||||||
|
private Color usedFill;
|
||||||
|
private Color black;
|
||||||
|
private Color gray;
|
||||||
|
/**
|
||||||
|
* Construct the DiskMapTab.
|
||||||
|
*/
|
||||||
|
public DiskMapTab(CTabFolder tabFolder, FormattedDisk disk) {
|
||||||
|
this.disk = disk;
|
||||||
|
|
||||||
|
// these items are reused; need to dispose of them when done!
|
||||||
|
freeFill = new Color(tabFolder.getDisplay(), 100,200,100);
|
||||||
|
usedFill = new Color(tabFolder.getDisplay(), 200,100,100);
|
||||||
|
black = new Color(tabFolder.getDisplay(), 0,0,0);
|
||||||
|
gray = new Color(tabFolder.getDisplay(), 50,50,50);
|
||||||
|
|
||||||
|
createDiskMapTab(tabFolder);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create the DISK MAP tab.
|
||||||
|
*/
|
||||||
|
protected void createDiskMapTab(CTabFolder tabFolder) {
|
||||||
|
CTabItem item = new CTabItem(tabFolder, SWT.NULL);
|
||||||
|
item.setText("Disk Map");
|
||||||
|
|
||||||
|
Canvas canvas = new Canvas(tabFolder, SWT.NULL);
|
||||||
|
GridLayout grid = new GridLayout(2, false);
|
||||||
|
canvas.setLayout(grid);
|
||||||
|
item.setControl(canvas);
|
||||||
|
|
||||||
|
String[] labels = disk.getBitmapLabels();
|
||||||
|
|
||||||
|
// ROW #1 - title
|
||||||
|
GridData data = new GridData(GridData.FILL_HORIZONTAL);
|
||||||
|
data.horizontalSpan = 2;
|
||||||
|
data.heightHint = 20;
|
||||||
|
Label title = new Label(canvas, SWT.LEFT);
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
if (labels.length == 1) {
|
||||||
|
buf.append("This disk is organized by the ");
|
||||||
|
buf.append(labels[0].toLowerCase());
|
||||||
|
buf.append(". Therefore, no organization has been forced on the ");
|
||||||
|
buf.append(labels[0].toLowerCase());
|
||||||
|
buf.append(" layout.");
|
||||||
|
} else {
|
||||||
|
buf.append("This disk is organized by ");
|
||||||
|
for (int i=0; i<labels.length; i++) {
|
||||||
|
if (i > 0) buf.append(" and ");
|
||||||
|
buf.append(labels[i].toLowerCase());
|
||||||
|
}
|
||||||
|
buf.append(", and this implies a rigid layout to the disk. ");
|
||||||
|
buf.append("This will be reflected in the disk map.");
|
||||||
|
}
|
||||||
|
title.setText(buf.toString());
|
||||||
|
title.setLayoutData(data);
|
||||||
|
|
||||||
|
// ROW #2 - blank and horizontal ruler
|
||||||
|
data = new GridData();
|
||||||
|
data.heightHint = 20;
|
||||||
|
data.widthHint = 20;
|
||||||
|
Composite blank = new Composite(canvas, SWT.NULL);
|
||||||
|
blank.setLayoutData(data);
|
||||||
|
data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
|
||||||
|
data.heightHint = 20;
|
||||||
|
Canvas ruler = new Canvas(canvas, SWT.NULL);
|
||||||
|
ruler.addPaintListener(new PaintListener() {
|
||||||
|
public void paintControl(PaintEvent event) {
|
||||||
|
paintHorizontalRuler(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ruler.setLayoutData(data);
|
||||||
|
|
||||||
|
// ROW #3 - vertical ruler and map
|
||||||
|
data = new GridData(GridData.VERTICAL_ALIGN_FILL);
|
||||||
|
data.widthHint = 20;
|
||||||
|
ruler = new Canvas(canvas, SWT.NULL);
|
||||||
|
ruler.addPaintListener(new PaintListener() {
|
||||||
|
public void paintControl(PaintEvent event) {
|
||||||
|
paintVerticalRuler(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ruler.setLayoutData(data);
|
||||||
|
data = new GridData(GridData.FILL_BOTH);
|
||||||
|
data.grabExcessHorizontalSpace = true;
|
||||||
|
data.grabExcessVerticalSpace = true;
|
||||||
|
Canvas map = new Canvas(canvas, SWT.BORDER);
|
||||||
|
map.addPaintListener(new PaintListener() {
|
||||||
|
public void paintControl(PaintEvent event) {
|
||||||
|
paintMap(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map.setLayoutData(data);
|
||||||
|
|
||||||
|
// ROW #5
|
||||||
|
data = new GridData(GridData.FILL_HORIZONTAL);
|
||||||
|
data.heightHint = 20;
|
||||||
|
data.horizontalSpan = 2;
|
||||||
|
Canvas legend = new Canvas(canvas, SWT.NULL);
|
||||||
|
legend.addPaintListener(new PaintListener() {
|
||||||
|
public void paintControl(PaintEvent event) {
|
||||||
|
paintLegend(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
legend.setLayoutData(data);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of resources.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
freeFill.dispose();
|
||||||
|
usedFill.dispose();
|
||||||
|
black.dispose();
|
||||||
|
gray.dispose();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle paint requests for horizontal ruler.
|
||||||
|
*/
|
||||||
|
private void paintHorizontalRuler(PaintEvent event) {
|
||||||
|
String label = (disk.getBitmapLabels()[0] + "s").toUpperCase();
|
||||||
|
Canvas canvas = (Canvas) event.widget;
|
||||||
|
Rectangle area = canvas.getClientArea();
|
||||||
|
event.gc.drawLine(area.x, area.y + area.height/2, area.x + area.width, area.y + area.height/2);
|
||||||
|
Point size = event.gc.textExtent(label);
|
||||||
|
event.gc.drawString(label, area.x + area.width/2 - size.x, area.y + area.height/2 - size.y/2);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle paint requests for vertical ruler.
|
||||||
|
*/
|
||||||
|
private void paintVerticalRuler(PaintEvent event) {
|
||||||
|
String label = (disk.getBitmapLabels()[0] + "s").toUpperCase();
|
||||||
|
if (disk.getBitmapLabels().length == 2) {
|
||||||
|
label = (disk.getBitmapLabels()[1] + "s").toUpperCase();
|
||||||
|
}
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
for (int i=0; i<label.length(); i++) {
|
||||||
|
if (i>0) buf.append("\n");
|
||||||
|
buf.append(label.charAt(i));
|
||||||
|
}
|
||||||
|
label = buf.toString();
|
||||||
|
Canvas canvas = (Canvas) event.widget;
|
||||||
|
Rectangle area = canvas.getClientArea();
|
||||||
|
event.gc.drawLine(area.x + area.width/2, area.y, area.x + area.width/2, area.y + area.height);
|
||||||
|
Point size = event.gc.textExtent(label);
|
||||||
|
event.gc.drawText(label, area.x + area.width/2 - size.x/2, area.y + area.height/2 - size.y/2);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle paint requests for disk map.
|
||||||
|
*/
|
||||||
|
private void paintMap(PaintEvent event) {
|
||||||
|
if (disk.getDiskUsage() == null) {
|
||||||
|
paintNoMap(event);
|
||||||
|
} else if (disk.getBitmapDimensions() == null) {
|
||||||
|
paintBlockMap(event);
|
||||||
|
} else {
|
||||||
|
paintSectorMap(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle paint requests for legend.
|
||||||
|
*/
|
||||||
|
private void paintLegend(PaintEvent event) {
|
||||||
|
Color background = event.gc.getBackground();
|
||||||
|
Canvas canvas = (Canvas) event.widget;
|
||||||
|
|
||||||
|
int height = event.gc.getFontMetrics().getHeight();
|
||||||
|
String freeText = " = Free";
|
||||||
|
String usedText = " = Used";
|
||||||
|
int padding = 50; // space between items
|
||||||
|
|
||||||
|
int totalWidth =
|
||||||
|
(height + 5) * 2 // free/used box
|
||||||
|
+ event.gc.textExtent(freeText).x
|
||||||
|
+ event.gc.textExtent(usedText).x;
|
||||||
|
|
||||||
|
int offset = canvas.getClientArea().width / 2 - totalWidth;
|
||||||
|
|
||||||
|
Rectangle box = new Rectangle(offset, 0, height, height);
|
||||||
|
drawBox(box, event.gc, freeFill, black, gray);
|
||||||
|
offset+= height;
|
||||||
|
event.gc.setBackground(background);
|
||||||
|
event.gc.drawText(freeText, offset, 0);
|
||||||
|
offset+= event.gc.textExtent(freeText).x;
|
||||||
|
|
||||||
|
offset+= padding;
|
||||||
|
box = new Rectangle(offset, 0, height, height);
|
||||||
|
drawBox(box, event.gc, usedFill, black, gray);
|
||||||
|
offset+= height;
|
||||||
|
event.gc.setBackground(background);
|
||||||
|
event.gc.drawText(" = Used", offset, 0);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Display message to user regarding no disk map being available.
|
||||||
|
*/
|
||||||
|
private void paintNoMap(PaintEvent event) {
|
||||||
|
Canvas canvas = (Canvas) event.widget;
|
||||||
|
Rectangle bounds = canvas.getClientArea();
|
||||||
|
event.gc.drawString("A disk map is unavailable.", 0, 0);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Paint a track/sector map.
|
||||||
|
*/
|
||||||
|
private void paintSectorMap(PaintEvent event) {
|
||||||
|
int[] dimensions = disk.getBitmapDimensions();
|
||||||
|
int ydim = dimensions[1];
|
||||||
|
int xdim = dimensions[0];
|
||||||
|
|
||||||
|
paintDiskMap(xdim, ydim, event);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Paint a block map.
|
||||||
|
*/
|
||||||
|
private void paintBlockMap(PaintEvent event) {
|
||||||
|
Canvas canvas = (Canvas) event.widget;
|
||||||
|
Rectangle area = canvas.getClientArea();
|
||||||
|
|
||||||
|
double blocks = disk.getBitmapLength();
|
||||||
|
double width = area.width;
|
||||||
|
double height = area.height;
|
||||||
|
double factor = Math.sqrt(blocks / (width * height));
|
||||||
|
int xdim = (int) (width * factor + 0.5);
|
||||||
|
int ydim = (int) (height * factor + 0.5);
|
||||||
|
if (xdim * ydim < blocks) {
|
||||||
|
xdim++;
|
||||||
|
}
|
||||||
|
if (xdim * ydim < blocks) {
|
||||||
|
ydim++;
|
||||||
|
}
|
||||||
|
|
||||||
|
paintDiskMap(xdim, ydim, event);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Paint a map with the given dimensions.
|
||||||
|
*/
|
||||||
|
private void paintDiskMap(int xdim, int ydim, PaintEvent event) {
|
||||||
|
int bitmapLength = disk.getBitmapLength();
|
||||||
|
Canvas canvas = (Canvas) event.widget;
|
||||||
|
Rectangle area = canvas.getClientArea();
|
||||||
|
area.width-= 2;
|
||||||
|
area.height-= 2;
|
||||||
|
|
||||||
|
int[] ypos = new int[ydim + 1];
|
||||||
|
for (int i=0; i<ydim; i++) {
|
||||||
|
ypos[i] = (i * area.height) / ydim + 1;
|
||||||
|
}
|
||||||
|
ypos[ydim] = area.height;
|
||||||
|
int[] xpos = new int[xdim + 1];
|
||||||
|
for (int i=0; i<xdim; i++) {
|
||||||
|
xpos[i] = (i * area.width) / xdim + 1;
|
||||||
|
}
|
||||||
|
xpos[xdim] = area.width;
|
||||||
|
|
||||||
|
Image image = new Image(canvas.getDisplay(), area);
|
||||||
|
GC gc = new GC(image);
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
DiskUsage usage = disk.getDiskUsage();
|
||||||
|
for (x=0; x<xdim && usage.hasNext(); x++) {
|
||||||
|
for (y=0; y<ydim && usage.hasNext(); y++) {
|
||||||
|
usage.next();
|
||||||
|
boolean free = usage.isFree();
|
||||||
|
Rectangle box = new Rectangle(xpos[x], ypos[y],
|
||||||
|
xpos[x+1]-xpos[x], ypos[y+1]-ypos[y]);
|
||||||
|
drawBox(box, gc, free ? freeFill : usedFill, black, gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event.gc.drawImage(image, 0, 0);
|
||||||
|
gc.dispose();
|
||||||
|
image.dispose();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Draw a box on the screen. The shadowed box is only drawn if there is
|
||||||
|
* enough space within the box; otherwise, the box is just filled in with
|
||||||
|
* the fill color. Additionally, drawBox ensures that a square is drawn.
|
||||||
|
*/
|
||||||
|
protected void drawBox(Rectangle box, GC gc, Color fill, Color outline, Color shadow) {
|
||||||
|
if (box.width >= 10 && box.height >= 10) {
|
||||||
|
// square the rectangle shape:
|
||||||
|
int size = Math.min(box.height, box.width);
|
||||||
|
box.height = size + ((box.height - size) / 2);
|
||||||
|
box.width = size + ((box.width - size) / 2);
|
||||||
|
// offset internal box:
|
||||||
|
box.x+= 2;
|
||||||
|
box.y+= 2;
|
||||||
|
box.width-= 5;
|
||||||
|
box.height-= 5;
|
||||||
|
// draw!
|
||||||
|
gc.setBackground(shadow);
|
||||||
|
gc.fillRectangle(box);
|
||||||
|
box.x-= 2;
|
||||||
|
box.y-= 2;
|
||||||
|
gc.setBackground(fill);
|
||||||
|
gc.fillRectangle(box);
|
||||||
|
gc.setForeground(outline);
|
||||||
|
gc.drawRectangle(box);
|
||||||
|
} else {
|
||||||
|
// just fill:
|
||||||
|
gc.setBackground(fill);
|
||||||
|
gc.fillRectangle(box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
src/com/webcodepro/applecommander/ui/swt/DiskWindow.java
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.custom.CTabFolder;
|
||||||
|
import org.eclipse.swt.events.DisposeEvent;
|
||||||
|
import org.eclipse.swt.events.DisposeListener;
|
||||||
|
import org.eclipse.swt.layout.FillLayout;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays disk information on the screen.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 12, 2002 3:28:41 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DiskWindow {
|
||||||
|
private Shell parentShell;
|
||||||
|
private ImageManager imageManager;
|
||||||
|
|
||||||
|
private Shell shell;
|
||||||
|
private FormattedDisk disk;
|
||||||
|
|
||||||
|
private DiskInfoTab diskInfoTab;
|
||||||
|
private DiskMapTab diskMapTab;
|
||||||
|
private DiskExplorerTab diskExplorerTab;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the disk window.
|
||||||
|
*/
|
||||||
|
public DiskWindow(Shell parentShell, FormattedDisk disk, ImageManager imageManager) {
|
||||||
|
this.parentShell = shell;
|
||||||
|
this.disk = disk;
|
||||||
|
this.imageManager = imageManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the Disk window and display (open) it.
|
||||||
|
*/
|
||||||
|
public void open() {
|
||||||
|
shell = new Shell(parentShell, SWT.SHELL_TRIM);
|
||||||
|
shell.setLayout(new FillLayout());
|
||||||
|
shell.setImage(imageManager.getDiskIcon());
|
||||||
|
shell.setText("AppleCommander - " + disk.getFilename());
|
||||||
|
shell.addDisposeListener(new DisposeListener() {
|
||||||
|
public void widgetDisposed(DisposeEvent event) {
|
||||||
|
dispose(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CTabFolder tabFolder = new CTabFolder(shell, SWT.BOTTOM);
|
||||||
|
diskExplorerTab = new DiskExplorerTab(tabFolder, disk, imageManager);
|
||||||
|
diskMapTab = new DiskMapTab(tabFolder, disk);
|
||||||
|
diskInfoTab = new DiskInfoTab(tabFolder, disk);
|
||||||
|
tabFolder.setSelection(tabFolder.getItems()[0]);
|
||||||
|
|
||||||
|
shell.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispose of all shared resources.
|
||||||
|
*/
|
||||||
|
private void dispose(DisposeEvent event) {
|
||||||
|
diskMapTab.dispose();
|
||||||
|
diskInfoTab.dispose();
|
||||||
|
|
||||||
|
disk = null;
|
||||||
|
diskMapTab = null;
|
||||||
|
diskInfoTab = null;
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.graphics.Point;
|
||||||
|
import org.eclipse.swt.graphics.Rectangle;
|
||||||
|
import org.eclipse.swt.widgets.Menu;
|
||||||
|
import org.eclipse.swt.widgets.MenuItem;
|
||||||
|
import org.eclipse.swt.widgets.ToolBar;
|
||||||
|
import org.eclipse.swt.widgets.ToolItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens to widgetSelected() events on SWT.DROP_DOWN type ToolItems
|
||||||
|
* and opens/closes a menu when appropriate.
|
||||||
|
* Code taken and modified from SWT examples to be more generic.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 2, 2002 8:25:11 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class DropDownSelectionListener extends SelectionAdapter {
|
||||||
|
private Menu menu = null;
|
||||||
|
private boolean visible = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the DropDownSelectionListener with the specific menu to be used.
|
||||||
|
*/
|
||||||
|
public DropDownSelectionListener(Menu menu) {
|
||||||
|
this.menu = menu;
|
||||||
|
|
||||||
|
MenuItem[] menuItems = menu.getItems();
|
||||||
|
for (int i=0; i<menuItems.length; i++) {
|
||||||
|
/*
|
||||||
|
* Add a menu selection listener so that the menu is hidden
|
||||||
|
* when the user selects an item from the drop down menu.
|
||||||
|
*/
|
||||||
|
menuItems[i].addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
setMenuVisible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle selection events.
|
||||||
|
*/
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
/**
|
||||||
|
* A selection event will be fired when a drop down tool
|
||||||
|
* item is selected in the main area and in the drop
|
||||||
|
* down arrow. Examine the event detail to determine
|
||||||
|
* where the widget was selected.
|
||||||
|
*/
|
||||||
|
if (event.detail == SWT.ARROW) {
|
||||||
|
/*
|
||||||
|
* The drop down arrow was selected.
|
||||||
|
*/
|
||||||
|
if (visible) {
|
||||||
|
// Hide the menu to give the Arrow the appearance of being a toggle button.
|
||||||
|
setMenuVisible(false);
|
||||||
|
} else {
|
||||||
|
// Position the menu below and vertically aligned with the the drop down tool button.
|
||||||
|
final ToolItem toolItem = (ToolItem) event.widget;
|
||||||
|
final ToolBar toolBar = toolItem.getParent();
|
||||||
|
|
||||||
|
Rectangle toolItemBounds = toolItem.getBounds();
|
||||||
|
Point point = toolBar.toDisplay(new Point(toolItemBounds.x, toolItemBounds.y));
|
||||||
|
menu.setLocation(point.x, point.y + toolItemBounds.height);
|
||||||
|
setMenuVisible(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Main area of drop down tool item selected.
|
||||||
|
* An application would invoke the code to perform the action for the tool item.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set menu visibility and track state.
|
||||||
|
*/
|
||||||
|
private void setMenuVisible(boolean visible) {
|
||||||
|
menu.setVisible(visible);
|
||||||
|
this.visible = visible;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.ModifyEvent;
|
||||||
|
import org.eclipse.swt.events.ModifyListener;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.graphics.Color;
|
||||||
|
import org.eclipse.swt.layout.RowData;
|
||||||
|
import org.eclipse.swt.layout.RowLayout;
|
||||||
|
import org.eclipse.swt.widgets.Button;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.DirectoryDialog;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
import org.eclipse.swt.widgets.Text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set locations and file names for the export.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 8, 2002 11:18:47 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ExportFileDestinationPane extends WizardPane {
|
||||||
|
private Composite parent;
|
||||||
|
private Object layoutData;
|
||||||
|
private Composite control;
|
||||||
|
private ExportWizard wizard;
|
||||||
|
private Text directoryText;
|
||||||
|
/**
|
||||||
|
* Constructor for ExportFileDestinationPane.
|
||||||
|
*/
|
||||||
|
public ExportFileDestinationPane(Composite parent, ExportWizard exportWizard, Object layoutData) {
|
||||||
|
super();
|
||||||
|
this.parent = parent;
|
||||||
|
this.wizard = exportWizard;
|
||||||
|
this.layoutData = layoutData;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This is the last pane in the wizard, so a null is returned to indicate no
|
||||||
|
* more pages.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
|
||||||
|
*/
|
||||||
|
public WizardPane getNextPane() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create and display the wizard pane.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#open()
|
||||||
|
*/
|
||||||
|
public void open() {
|
||||||
|
control = new Composite(parent, SWT.NULL);
|
||||||
|
control.setLayoutData(layoutData);
|
||||||
|
wizard.enableNextButton(false);
|
||||||
|
wizard.enableFinishButton(true);
|
||||||
|
RowLayout layout = new RowLayout(SWT.VERTICAL);
|
||||||
|
layout.justify = true;
|
||||||
|
layout.marginBottom = 5;
|
||||||
|
layout.marginLeft = 5;
|
||||||
|
layout.marginRight = 5;
|
||||||
|
layout.marginTop = 5;
|
||||||
|
layout.spacing = 3;
|
||||||
|
control.setLayout(layout);
|
||||||
|
Label label = new Label(control, SWT.WRAP);
|
||||||
|
label.setText("Please indicate the destination for the exported files:");
|
||||||
|
|
||||||
|
directoryText = new Text(control, SWT.WRAP | SWT.BORDER);
|
||||||
|
if (wizard.getDirectory() != null) directoryText.setText(wizard.getDirectory());
|
||||||
|
directoryText.setLayoutData(new RowData(parent.getSize().x - 30, -1));
|
||||||
|
directoryText.setBackground(new Color(control.getDisplay(), 255,255,255));
|
||||||
|
directoryText.setFocus();
|
||||||
|
directoryText.addModifyListener(new ModifyListener() {
|
||||||
|
public void modifyText(ModifyEvent event) {
|
||||||
|
Text text = (Text) event.getSource();
|
||||||
|
wizard.setDirectory(text.getText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button button = new Button(control, SWT.PUSH);
|
||||||
|
button.setText("Browse...");
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
DirectoryDialog directoryDialog = new DirectoryDialog(control.getShell());
|
||||||
|
directoryDialog.setFilterPath(directoryText.getText());
|
||||||
|
directoryDialog.setMessage(
|
||||||
|
"Please choose the directory to which exported files will be written");
|
||||||
|
String directory = directoryDialog.open();
|
||||||
|
if (directory != null) {
|
||||||
|
directoryText.setText(directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of any resources.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
directoryText.dispose();
|
||||||
|
control.dispose();
|
||||||
|
control = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.layout.RowLayout;
|
||||||
|
import org.eclipse.swt.widgets.Button;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.AppleWorksWordProcessorFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.ApplesoftFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.BinaryFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.IntegerBasicFileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.TextFileFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the wizard pane which gets the export filter.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 7, 2002 8:43:27 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ExportFileStartPane extends WizardPane {
|
||||||
|
private Composite parent;
|
||||||
|
private Object layoutData;
|
||||||
|
private Composite control;
|
||||||
|
private ExportWizard wizard;
|
||||||
|
private GraphicsFileFilter graphicsFileFilter = new GraphicsFileFilter();
|
||||||
|
private AppleWorksWordProcessorFileFilter awpFilter = new AppleWorksWordProcessorFileFilter();
|
||||||
|
/**
|
||||||
|
* Constructor for ExportFileStartPane.
|
||||||
|
*/
|
||||||
|
public ExportFileStartPane(Composite parent, ExportWizard exportWizard, Object layoutData) {
|
||||||
|
super();
|
||||||
|
this.parent = parent;
|
||||||
|
this.wizard = exportWizard;
|
||||||
|
this.layoutData = layoutData;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Open up and configure the wizard pane.
|
||||||
|
*/
|
||||||
|
public void open() {
|
||||||
|
control = new Composite(parent, SWT.NULL);
|
||||||
|
control.setLayoutData(layoutData);
|
||||||
|
wizard.enableNextButton(true);
|
||||||
|
wizard.enableFinishButton(false);
|
||||||
|
RowLayout layout = new RowLayout(SWT.VERTICAL);
|
||||||
|
layout.justify = true;
|
||||||
|
layout.marginBottom = 5;
|
||||||
|
layout.marginLeft = 5;
|
||||||
|
layout.marginRight = 5;
|
||||||
|
layout.marginTop = 5;
|
||||||
|
layout.spacing = 3;
|
||||||
|
control.setLayout(layout);
|
||||||
|
Label label = new Label(control, SWT.WRAP);
|
||||||
|
label.setText("Please choose the type of file that is being exported.");
|
||||||
|
RowLayout subpanelLayout = new RowLayout(SWT.VERTICAL);
|
||||||
|
subpanelLayout.justify = true;
|
||||||
|
subpanelLayout.spacing = 3;
|
||||||
|
Composite buttonSubpanel = new Composite(control, SWT.NULL);
|
||||||
|
buttonSubpanel.setLayout(subpanelLayout);
|
||||||
|
Button button = new Button(buttonSubpanel, SWT.RADIO);
|
||||||
|
button.setText("Raw disk data");
|
||||||
|
button.setSelection(wizard.getFileFilter() == null);
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizard.setFileFilter(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(buttonSubpanel, SWT.RADIO);
|
||||||
|
button.setText("Binary file");
|
||||||
|
button.setSelection(wizard.getFileFilter() instanceof BinaryFileFilter);
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizard.setFileFilter(new BinaryFileFilter());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(buttonSubpanel, SWT.RADIO);
|
||||||
|
button.setText("ASCII text file");
|
||||||
|
button.setSelection(wizard.getFileFilter() instanceof TextFileFilter);
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizard.setFileFilter(new TextFileFilter());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(buttonSubpanel, SWT.RADIO);
|
||||||
|
button.setText("Applesoft BASIC file");
|
||||||
|
button.setSelection(wizard.getFileFilter() instanceof ApplesoftFileFilter);
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizard.setFileFilter(new ApplesoftFileFilter());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(buttonSubpanel, SWT.RADIO);
|
||||||
|
button.setText("Integer BASIC file");
|
||||||
|
button.setSelection(wizard.getFileFilter() instanceof IntegerBasicFileFilter);
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizard.setFileFilter(new IntegerBasicFileFilter());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(buttonSubpanel, SWT.RADIO);
|
||||||
|
button.setText("AppleWorks Word Processor file");
|
||||||
|
button.setSelection(wizard.getFileFilter() instanceof AppleWorksWordProcessorFileFilter);
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizard.setFileFilter(new AppleWorksWordProcessorFileFilter());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(buttonSubpanel, SWT.RADIO);
|
||||||
|
button.setText("Graphic file...");
|
||||||
|
button.setEnabled(graphicsFileFilter.isCodecAvailable());
|
||||||
|
button.setSelection(wizard.getFileFilter() instanceof GraphicsFileFilter);
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizard.setFileFilter(graphicsFileFilter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the next pane. A null return indicates the end of the wizard.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
|
||||||
|
*/
|
||||||
|
public WizardPane getNextPane() {
|
||||||
|
if (wizard.getFileFilter() instanceof GraphicsFileFilter) {
|
||||||
|
return new ExportGraphicsTypePane(parent, wizard, layoutData);
|
||||||
|
} else if (wizard.getFileFilter() instanceof AppleWorksWordProcessorFileFilter) {
|
||||||
|
return new AppleWorksWordProcessorPane(parent, wizard, layoutData);
|
||||||
|
}
|
||||||
|
return new ExportFileDestinationPane(parent, wizard, layoutData);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of resources.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
control.dispose();
|
||||||
|
control = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.layout.RowLayout;
|
||||||
|
import org.eclipse.swt.widgets.Button;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.GraphicsFileFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose graphics options for file export.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 7, 2002 10:25:43 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ExportGraphicsTypePane extends WizardPane {
|
||||||
|
private Composite parent;
|
||||||
|
private Object layoutData;
|
||||||
|
private Composite control;
|
||||||
|
private ExportWizard wizard;
|
||||||
|
/**
|
||||||
|
* Constructor for ExportGraphicsTypePane.
|
||||||
|
*/
|
||||||
|
public ExportGraphicsTypePane(Composite parent, ExportWizard exportWizard, Object layoutData) {
|
||||||
|
super();
|
||||||
|
this.parent = parent;
|
||||||
|
this.wizard = exportWizard;
|
||||||
|
this.layoutData = layoutData;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Determine the next wizard pane and return an instance.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#getNextPane()
|
||||||
|
*/
|
||||||
|
public WizardPane getNextPane() {
|
||||||
|
return new ExportFileDestinationPane(parent, wizard, layoutData);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Open up and configure the wizard pane.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#open()
|
||||||
|
*/
|
||||||
|
public void open() {
|
||||||
|
wizard.enableFinishButton(false);
|
||||||
|
wizard.enableNextButton(true);
|
||||||
|
control = new Composite(parent, SWT.NULL);
|
||||||
|
control.setLayoutData(layoutData);
|
||||||
|
RowLayout layout = new RowLayout(SWT.VERTICAL);
|
||||||
|
layout.justify = true;
|
||||||
|
layout.marginBottom = 5;
|
||||||
|
layout.marginLeft = 5;
|
||||||
|
layout.marginRight = 5;
|
||||||
|
layout.marginTop = 5;
|
||||||
|
layout.spacing = 3;
|
||||||
|
control.setLayout(layout);
|
||||||
|
Label label = new Label(control, SWT.WRAP);
|
||||||
|
label.setText("Please choose the appropriate graphics mode:");
|
||||||
|
RowLayout subpanelLayout = new RowLayout(SWT.VERTICAL);
|
||||||
|
subpanelLayout.justify = true;
|
||||||
|
subpanelLayout.spacing = 3;
|
||||||
|
Composite graphicsModeGroup = new Composite(control, SWT.NULL);
|
||||||
|
graphicsModeGroup.setLayout(subpanelLayout);
|
||||||
|
Button button = new Button(graphicsModeGroup, SWT.RADIO);
|
||||||
|
button.setText("Hires black and white (280x192)");
|
||||||
|
button.setSelection(getGraphicsFilter().isHiresBlackAndWhiteMode());
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_HGR_BLACK_AND_WHITE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(graphicsModeGroup, SWT.RADIO);
|
||||||
|
button.setText("Hires color (280x192)");
|
||||||
|
button.setSelection(getGraphicsFilter().isHiresColorMode());
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_HGR_COLOR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(graphicsModeGroup, SWT.RADIO);
|
||||||
|
button.setText("Double hires black and white (560x384)");
|
||||||
|
button.setSelection(getGraphicsFilter().isDoubleHiresBlackAndWhiteMode());
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_DHR_BLACK_AND_WHITE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button = new Button(graphicsModeGroup, SWT.RADIO);
|
||||||
|
button.setText("Double hires color (560x384)");
|
||||||
|
button.setSelection(getGraphicsFilter().isDoubleHiresColorMode());
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
getGraphicsFilter().setMode(GraphicsFileFilter.MODE_DHR_COLOR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
label = new Label(control, SWT.WRAP);
|
||||||
|
label.setText("Please choose the appropriate file format to save image as:");
|
||||||
|
Composite graphicsFormatGroup = new Composite(control, SWT.NULL);
|
||||||
|
graphicsFormatGroup.setLayout(subpanelLayout);
|
||||||
|
String[] formats = getGraphicsFilter().getFileExtensions();
|
||||||
|
for (int i=0; i<formats.length; i++) {
|
||||||
|
button = new Button(graphicsFormatGroup, SWT.RADIO);
|
||||||
|
button.setText(formats[i]);
|
||||||
|
button.setSelection(formats[i].equals(getGraphicsFilter().getExtension()));
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
Button source = (Button) e.getSource();
|
||||||
|
getGraphicsFilter().setExtension(source.getText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of widgets.
|
||||||
|
* @see com.webcodepro.applecommander.gui.WizardPane#dispose()
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
control.dispose();
|
||||||
|
control = null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the graphics file filter.
|
||||||
|
*/
|
||||||
|
protected GraphicsFileFilter getGraphicsFilter() {
|
||||||
|
return (GraphicsFileFilter) wizard.getFileFilter();
|
||||||
|
}
|
||||||
|
}
|
225
src/com/webcodepro/applecommander/ui/swt/ExportWizard.java
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.graphics.Image;
|
||||||
|
import org.eclipse.swt.layout.FillLayout;
|
||||||
|
import org.eclipse.swt.layout.RowData;
|
||||||
|
import org.eclipse.swt.layout.RowLayout;
|
||||||
|
import org.eclipse.swt.widgets.Button;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.FileFilter;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File export wizard.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 7, 2002 9:22:35 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ExportWizard {
|
||||||
|
private FormattedDisk disk;
|
||||||
|
private Shell parent;
|
||||||
|
private Shell dialog;
|
||||||
|
private Image logo; // managed by SwtAppleCommander
|
||||||
|
private Stack wizardPanes = new Stack();
|
||||||
|
private FileFilter fileFilter;
|
||||||
|
private String directory;
|
||||||
|
private boolean wizardCompleted;
|
||||||
|
private Button backButton;
|
||||||
|
private Button nextButton;
|
||||||
|
private Button finishButton;
|
||||||
|
private Composite contentPane;
|
||||||
|
/**
|
||||||
|
* Constructor for ExportWizard.
|
||||||
|
*/
|
||||||
|
public ExportWizard(Shell parent, Image logo, FormattedDisk disk) {
|
||||||
|
super();
|
||||||
|
this.parent = parent;
|
||||||
|
this.logo = logo;
|
||||||
|
this.disk = disk;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create the dialog.
|
||||||
|
*/
|
||||||
|
private void createDialog() {
|
||||||
|
dialog = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
|
||||||
|
dialog.setText("Export Wizard");
|
||||||
|
RowLayout layout = new RowLayout(SWT.VERTICAL);
|
||||||
|
layout.justify = true;
|
||||||
|
layout.marginBottom = 5;
|
||||||
|
layout.marginLeft = 5;
|
||||||
|
layout.marginRight = 5;
|
||||||
|
layout.marginTop = 5;
|
||||||
|
layout.spacing = 3;
|
||||||
|
dialog.setLayout(layout);
|
||||||
|
|
||||||
|
// Wizard logo
|
||||||
|
RowData rowData = new RowData();
|
||||||
|
rowData.width = logo.getImageData().width;
|
||||||
|
rowData.height = logo.getImageData().height;
|
||||||
|
ImageCanvas imageCanvas = new ImageCanvas(dialog, SWT.BORDER, logo, rowData);
|
||||||
|
|
||||||
|
// Starting pane
|
||||||
|
rowData = new RowData();
|
||||||
|
rowData.width = logo.getImageData().width;
|
||||||
|
contentPane = new Composite(dialog, SWT.BORDER);
|
||||||
|
contentPane.setLayoutData(rowData);
|
||||||
|
contentPane.setLayout(new FillLayout());
|
||||||
|
|
||||||
|
// Bottom row of buttons
|
||||||
|
Composite composite = new Composite(dialog, SWT.NONE);
|
||||||
|
composite.setLayoutData(rowData);
|
||||||
|
composite.setLayout(new FillLayout(SWT.HORIZONTAL));
|
||||||
|
Button button = new Button(composite, SWT.PUSH);
|
||||||
|
button.setText("Cancel");
|
||||||
|
button.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizardCompleted = false;
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
backButton = new Button(composite, SWT.PUSH);
|
||||||
|
backButton.setEnabled(false);
|
||||||
|
backButton.setText("< Back");
|
||||||
|
backButton.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
WizardPane current = (WizardPane) wizardPanes.pop();
|
||||||
|
WizardPane previous = (WizardPane) wizardPanes.peek();
|
||||||
|
backButton.setEnabled(wizardPanes.size() > 1);
|
||||||
|
current.dispose();
|
||||||
|
previous.open();
|
||||||
|
dialog.pack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nextButton = new Button(composite, SWT.PUSH);
|
||||||
|
nextButton.setText("Next >");
|
||||||
|
nextButton.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
WizardPane current = (WizardPane) wizardPanes.peek();
|
||||||
|
WizardPane next = current.getNextPane();
|
||||||
|
wizardPanes.add(next);
|
||||||
|
backButton.setEnabled(wizardPanes.size() > 1);
|
||||||
|
current.dispose();
|
||||||
|
next.open();
|
||||||
|
dialog.pack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
finishButton = new Button(composite, SWT.PUSH);
|
||||||
|
finishButton.setEnabled(false);
|
||||||
|
finishButton.setText("Finish");
|
||||||
|
finishButton.addSelectionListener(new SelectionAdapter() {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
wizardCompleted = true;
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
WizardPane wizardPane = new ExportFileStartPane(contentPane, this, null);
|
||||||
|
wizardPanes.add(wizardPane);
|
||||||
|
wizardPane.open();
|
||||||
|
|
||||||
|
dialog.pack();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Open and display the dialog.
|
||||||
|
*/
|
||||||
|
public void open() {
|
||||||
|
createDialog();
|
||||||
|
dialog.open();
|
||||||
|
Display display = dialog.getDisplay();
|
||||||
|
while (!dialog.isDisposed()) {
|
||||||
|
if (!display.readAndDispatch()) display.sleep ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of all panels and resources.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
while (!wizardPanes.empty()) {
|
||||||
|
WizardPane pane = (WizardPane) wizardPanes.pop();
|
||||||
|
pane.dispose();
|
||||||
|
pane = null;
|
||||||
|
}
|
||||||
|
dialog.dispose();
|
||||||
|
backButton.dispose();
|
||||||
|
nextButton.dispose();
|
||||||
|
finishButton.dispose();
|
||||||
|
contentPane.dispose();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the FileFilter.
|
||||||
|
*/
|
||||||
|
public FileFilter getFileFilter() {
|
||||||
|
return fileFilter;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the FileFilter.
|
||||||
|
*/
|
||||||
|
public void setFileFilter(FileFilter fileFilter) {
|
||||||
|
this.fileFilter = fileFilter;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Indicates if the wizard was completed.
|
||||||
|
*/
|
||||||
|
public boolean isWizardCompleted() {
|
||||||
|
return wizardCompleted;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enable/disable the next button.
|
||||||
|
*/
|
||||||
|
public void enableNextButton(boolean state) {
|
||||||
|
nextButton.setEnabled(state);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enable/disable the finish button.
|
||||||
|
*/
|
||||||
|
public void enableFinishButton(boolean state) {
|
||||||
|
finishButton.setEnabled(state);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the disk that is being worked on.
|
||||||
|
*/
|
||||||
|
public FormattedDisk getDisk() {
|
||||||
|
return disk;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the directory.
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public String getDirectory() {
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the directory.
|
||||||
|
* @param directory The directory to set
|
||||||
|
*/
|
||||||
|
public void setDirectory(String directory) {
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
}
|
61
src/com/webcodepro/applecommander/ui/swt/ImageCanvas.java
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import org.eclipse.swt.events.PaintEvent;
|
||||||
|
import org.eclipse.swt.events.PaintListener;
|
||||||
|
import org.eclipse.swt.graphics.Image;
|
||||||
|
import org.eclipse.swt.widgets.Canvas;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays an image.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 7, 2002 9:28:21 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ImageCanvas extends Canvas implements PaintListener {
|
||||||
|
private Image image;
|
||||||
|
/**
|
||||||
|
* Constructor for ImageCanvas.
|
||||||
|
*/
|
||||||
|
public ImageCanvas(Composite parent, int style, Image image, Object layoutData) {
|
||||||
|
super(parent, style);
|
||||||
|
this.image = image;
|
||||||
|
setLayoutData(layoutData);
|
||||||
|
setSize(image.getImageData().width, image.getImageData().height);
|
||||||
|
addPaintListener(this);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle paint events.
|
||||||
|
*/
|
||||||
|
public void paintControl(PaintEvent event) {
|
||||||
|
event.gc.drawImage(
|
||||||
|
image,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
image.getImageData().width,
|
||||||
|
image.getImageData().height,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
image.getImageData().width,
|
||||||
|
image.getImageData().height);
|
||||||
|
}
|
||||||
|
}
|
214
src/com/webcodepro/applecommander/ui/swt/ImageManager.java
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.eclipse.swt.graphics.Image;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage image for the SWT-base AppleCommander.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 17, 2002 6:53:08 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class ImageManager {
|
||||||
|
private Image logoImage;
|
||||||
|
private Image diskIcon;
|
||||||
|
private Image standardFileViewIcon;
|
||||||
|
private Image nativeFileViewIcon;
|
||||||
|
private Image detailFileViewIcon;
|
||||||
|
private Image importFileIcon;
|
||||||
|
private Image exportFileIcon;
|
||||||
|
private Image saveImageIcon;
|
||||||
|
private Image deleteFileIcon;
|
||||||
|
private Image deletedFilesIcon;
|
||||||
|
private Image exportWizardLogo;
|
||||||
|
private Image openDiskIcon;
|
||||||
|
private Image newDiskIcon;
|
||||||
|
private Image aboutIcon;
|
||||||
|
/**
|
||||||
|
* Construct the ImageManager.
|
||||||
|
*/
|
||||||
|
protected ImageManager(Display display) {
|
||||||
|
diskIcon = createImage(display, "diskicon.gif");
|
||||||
|
standardFileViewIcon = createImage(display, "standardfileview.gif");
|
||||||
|
nativeFileViewIcon = createImage(display, "nativefileview.gif");
|
||||||
|
detailFileViewIcon = createImage(display, "detailfileview.gif");
|
||||||
|
importFileIcon = createImage(display, "importfile.gif");
|
||||||
|
exportFileIcon = createImage(display, "exportfile.gif");
|
||||||
|
saveImageIcon = createImage(display, "saveimage.gif");
|
||||||
|
deleteFileIcon = createImage(display, "deletefile.gif");
|
||||||
|
deletedFilesIcon = createImage(display, "deletedfiles.gif");
|
||||||
|
exportWizardLogo = createImage(display, "ExportWizardLogo.gif");
|
||||||
|
logoImage = createImage(display, "AppleCommanderLogo.gif");
|
||||||
|
openDiskIcon = createImage(display, "opendisk.gif");
|
||||||
|
newDiskIcon = createImage(display, "newdisk.gif");
|
||||||
|
aboutIcon = createImage(display, "about.gif");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispose of resources.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
diskIcon.dispose();
|
||||||
|
standardFileViewIcon.dispose();
|
||||||
|
nativeFileViewIcon.dispose();
|
||||||
|
detailFileViewIcon.dispose();
|
||||||
|
importFileIcon.dispose();
|
||||||
|
exportFileIcon.dispose();
|
||||||
|
saveImageIcon.dispose();
|
||||||
|
deleteFileIcon.dispose();
|
||||||
|
deletedFilesIcon.dispose();
|
||||||
|
logoImage.dispose();
|
||||||
|
exportWizardLogo.dispose();
|
||||||
|
openDiskIcon.dispose();
|
||||||
|
newDiskIcon.dispose();
|
||||||
|
aboutIcon.dispose();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates an image.
|
||||||
|
*/
|
||||||
|
private Image createImage(Display display, String path) {
|
||||||
|
try {
|
||||||
|
InputStream stream = getClass().getResourceAsStream(
|
||||||
|
"/com/webcodepro/applecommander/ui/images/" + path);
|
||||||
|
if (stream != null) {
|
||||||
|
Image image = new Image(display, stream);
|
||||||
|
stream.close();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the deletedFilesIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getDeletedFilesIcon() {
|
||||||
|
return deletedFilesIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the deleteFileIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getDeleteFileIcon() {
|
||||||
|
return deleteFileIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the detailFileViewIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getDetailFileViewIcon() {
|
||||||
|
return detailFileViewIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the diskIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getDiskIcon() {
|
||||||
|
return diskIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exportFileIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getExportFileIcon() {
|
||||||
|
return exportFileIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exportWizardLogo.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getExportWizardLogo() {
|
||||||
|
return exportWizardLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the importFileIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getImportFileIcon() {
|
||||||
|
return importFileIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the logoImage.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getLogoImage() {
|
||||||
|
return logoImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the nativeFileViewIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getNativeFileViewIcon() {
|
||||||
|
return nativeFileViewIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the saveImageIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getSaveImageIcon() {
|
||||||
|
return saveImageIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the standardFileViewIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getStandardFileViewIcon() {
|
||||||
|
return standardFileViewIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the aboutIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getAboutIcon() {
|
||||||
|
return aboutIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the newDiskIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getNewDiskIcon() {
|
||||||
|
return newDiskIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the openDiskIcon.
|
||||||
|
* @return Image
|
||||||
|
*/
|
||||||
|
public Image getOpenDiskIcon() {
|
||||||
|
return openDiskIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
229
src/com/webcodepro/applecommander/ui/swt/SwtAppleCommander.java
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.DisposeEvent;
|
||||||
|
import org.eclipse.swt.events.DisposeListener;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.graphics.Image;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.eclipse.swt.widgets.FileDialog;
|
||||||
|
import org.eclipse.swt.widgets.MessageBox;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
import org.eclipse.swt.widgets.ToolBar;
|
||||||
|
import org.eclipse.swt.widgets.ToolItem;
|
||||||
|
|
||||||
|
import com.webcodepro.applecommander.storage.Disk;
|
||||||
|
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||||
|
import com.webcodepro.applecommander.storage.Disk.FilenameFilter;
|
||||||
|
import com.webcodepro.applecommander.ui.AppleCommander;
|
||||||
|
import com.webcodepro.applecommander.ui.UserPreferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main class for the SwtAppleCommander interface.
|
||||||
|
* <p>
|
||||||
|
* Date created: Oct 7, 2002 9:43:37 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public class SwtAppleCommander {
|
||||||
|
private Display display;
|
||||||
|
private Shell shell;
|
||||||
|
private ToolBar toolBar;
|
||||||
|
private UserPreferences userPreferences = UserPreferences.getInstance();
|
||||||
|
private static ImageManager imageManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch SwtAppleCommander.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Display display = new Display();
|
||||||
|
imageManager = new ImageManager(display);
|
||||||
|
SwtAppleCommander application = new SwtAppleCommander();
|
||||||
|
Shell shell = application.open(display);
|
||||||
|
|
||||||
|
while (!shell.isDisposed()) {
|
||||||
|
if (!display.readAndDispatch()) display.sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserPreferences.getInstance().save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for SwtAppleCommander.
|
||||||
|
*/
|
||||||
|
public SwtAppleCommander() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the main program.
|
||||||
|
*/
|
||||||
|
private Shell open(Display display) {
|
||||||
|
this.display = display;
|
||||||
|
display.setAppName("AppleCommander");
|
||||||
|
shell = new Shell(display, SWT.BORDER | SWT.CLOSE | SWT.MIN | SWT.TITLE);
|
||||||
|
shell.setText("AppleCommander");
|
||||||
|
shell.setImage(imageManager.getDiskIcon());
|
||||||
|
shell.addDisposeListener(new DisposeListener() {
|
||||||
|
public void widgetDisposed(DisposeEvent event) {
|
||||||
|
dispose(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
GridLayout gridLayout = new GridLayout();
|
||||||
|
gridLayout.marginHeight = 5;
|
||||||
|
gridLayout.marginWidth = 5;
|
||||||
|
shell.setLayout(gridLayout);
|
||||||
|
|
||||||
|
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
|
||||||
|
gridData.grabExcessHorizontalSpace = true;
|
||||||
|
createToolBar(shell, gridData);
|
||||||
|
|
||||||
|
gridData = new GridData();
|
||||||
|
Image logoImage = imageManager.getLogoImage();
|
||||||
|
gridData.widthHint = logoImage.getImageData().width;
|
||||||
|
gridData.heightHint = logoImage.getImageData().height;
|
||||||
|
ImageCanvas imageCanvas = new ImageCanvas(shell, SWT.BORDER, logoImage, gridData);
|
||||||
|
|
||||||
|
shell.pack();
|
||||||
|
shell.open();
|
||||||
|
return shell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispose of all shared resources.
|
||||||
|
*/
|
||||||
|
private void dispose(DisposeEvent event) {
|
||||||
|
toolBar.dispose();
|
||||||
|
imageManager.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exits the main program.
|
||||||
|
*/
|
||||||
|
private void exit() {
|
||||||
|
shell.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a file.
|
||||||
|
*/
|
||||||
|
private void openFile() {
|
||||||
|
FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
|
||||||
|
FilenameFilter[] fileFilters = Disk.getFilenameFilters();
|
||||||
|
String[] names = new String[fileFilters.length];
|
||||||
|
String[] extensions = new String[fileFilters.length];
|
||||||
|
for (int i=0; i<fileFilters.length; i++) {
|
||||||
|
names[i] = fileFilters[i].getNames();
|
||||||
|
extensions[i] = fileFilters[i].getExtensions();
|
||||||
|
}
|
||||||
|
fileDialog.setFilterNames(names);
|
||||||
|
fileDialog.setFilterExtensions(extensions);
|
||||||
|
fileDialog.setFilterPath(userPreferences.getDiskImageDirectory());
|
||||||
|
String fullpath = fileDialog.open();
|
||||||
|
|
||||||
|
if (fullpath != null) {
|
||||||
|
userPreferences.setDiskImageDirectory(fileDialog.getFilterPath());
|
||||||
|
try {
|
||||||
|
Disk disk = new Disk(fullpath);
|
||||||
|
FormattedDisk formattedDisk = disk.getFormattedDisk();
|
||||||
|
if (formattedDisk != null) {
|
||||||
|
DiskWindow window = new DiskWindow(shell, formattedDisk, imageManager);
|
||||||
|
window.open();
|
||||||
|
} else {
|
||||||
|
Shell finalShell = shell;
|
||||||
|
MessageBox box = new MessageBox(finalShell, SWT.ICON_ERROR | SWT.OK);
|
||||||
|
box.setText("Unrecognized Disk Format");
|
||||||
|
box.setMessage(
|
||||||
|
"Unable to load '" + fullpath + "'.\n\n"
|
||||||
|
+ "AppleCommander did not recognize the format\n"
|
||||||
|
+ "of that disk. Either this is a new format\n"
|
||||||
|
+ "or a protected disk.\n\n"
|
||||||
|
+ "Sorry!");
|
||||||
|
box.open();
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the toolbar.
|
||||||
|
*/
|
||||||
|
private void createToolBar(Shell shell, Object layoutData) {
|
||||||
|
toolBar = new ToolBar(shell, SWT.FLAT);
|
||||||
|
if (layoutData != null) toolBar.setLayoutData(layoutData);
|
||||||
|
|
||||||
|
ToolItem item = new ToolItem(toolBar, SWT.PUSH);
|
||||||
|
item.setImage(imageManager.getOpenDiskIcon());
|
||||||
|
item.setText("Open...");
|
||||||
|
item.setToolTipText("Open a disk image");
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
openFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new ToolItem(toolBar, SWT.PUSH);
|
||||||
|
item.setImage(imageManager.getNewDiskIcon());
|
||||||
|
item.setText("Create...");
|
||||||
|
item.setToolTipText("Create a disk image");
|
||||||
|
item.setEnabled(false);
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
// not available yet
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
item = new ToolItem(toolBar, SWT.PUSH);
|
||||||
|
item.setImage(imageManager.getAboutIcon());
|
||||||
|
item.setText("About");
|
||||||
|
item.setToolTipText("About AppleCommander");
|
||||||
|
final Shell finalShell = shell;
|
||||||
|
item.addSelectionListener(new SelectionAdapter () {
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
MessageBox box = new MessageBox(finalShell, SWT.ICON_INFORMATION | SWT.OK);
|
||||||
|
box.setText("About AppleCommander");
|
||||||
|
box.setMessage(
|
||||||
|
"AppleCommander\n"
|
||||||
|
+ "Version " + AppleCommander.VERSION + "\n"
|
||||||
|
+ "Copyright (c) 2002\n\n"
|
||||||
|
+ "AppleCommander was created for the express\n"
|
||||||
|
+ "purpose of assisting those-who-remember.\n\n"
|
||||||
|
+ "I wish you many hours of vintage pleasure!\n"
|
||||||
|
+ "-Rob");
|
||||||
|
box.open();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item = new ToolItem(toolBar, SWT.SEPARATOR);
|
||||||
|
|
||||||
|
toolBar.pack();
|
||||||
|
}
|
||||||
|
}
|
47
src/com/webcodepro/applecommander/ui/swt/WizardPane.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* AppleCommander - An Apple ][ image utility.
|
||||||
|
* Copyright (C) 2002 by Robert Greene
|
||||||
|
* robgreene at users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.webcodepro.applecommander.ui.swt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a pane of a wizard.
|
||||||
|
* <p>
|
||||||
|
* Date created: Nov 7, 2002 8:40:44 PM
|
||||||
|
* @author: Rob Greene
|
||||||
|
*/
|
||||||
|
public abstract class WizardPane {
|
||||||
|
/**
|
||||||
|
* Constructor for WizardPane.
|
||||||
|
*/
|
||||||
|
public WizardPane() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the next WizardPane.
|
||||||
|
*/
|
||||||
|
public abstract WizardPane getNextPane();
|
||||||
|
/**
|
||||||
|
* Create and display the wizard pane.
|
||||||
|
*/
|
||||||
|
public abstract void open();
|
||||||
|
/**
|
||||||
|
* Dispose of any resources.
|
||||||
|
*/
|
||||||
|
public abstract void dispose();
|
||||||
|
}
|