Compare commits
315 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2bc330e377 | ||
|
6c81bcc93b | ||
|
171fd66ba1 | ||
|
aa77ec1cd4 | ||
|
8f95988596 | ||
|
8cb682f068 | ||
|
dfc7a68580 | ||
|
4b476457d3 | ||
|
3bf972a761 | ||
|
fd5bee6e5c | ||
|
0af6714119 | ||
|
22dfaf65c6 | ||
|
f4a6465b04 | ||
|
64d5ce9ee2 | ||
|
4e48437adc | ||
|
e2abdcabc9 | ||
|
bc0993e9d3 | ||
|
9fba3893ba | ||
|
09c4542118 | ||
|
af3ead0441 | ||
|
c12392200b | ||
|
794fef8610 | ||
|
2ec0e10c5e | ||
|
668ed719fa | ||
|
23b95675cf | ||
|
b98ecbed8d | ||
|
82381a6a47 | ||
|
3f2f1bfbca | ||
|
a7119528a6 | ||
|
6f943ae8c2 | ||
|
5add6c0729 | ||
|
921c946ab5 | ||
|
898587b23b | ||
|
37c489f252 | ||
|
036e47b9b1 | ||
|
250c1a9bd9 | ||
|
b19e89d7cf | ||
|
d71cabb754 | ||
|
848b2469ae | ||
|
989b1d5ab9 | ||
|
e0a2c50d5b | ||
|
8765299cf4 | ||
|
97fe58d94d | ||
|
7d4d8c75e6 | ||
|
f5664a9ce9 | ||
|
9d8cdcd67b | ||
|
12b74f9d21 | ||
|
1b405c6f38 | ||
|
66d6910f91 | ||
|
572ee8c3a1 | ||
|
2a14b7baad | ||
|
a633e28ed3 | ||
|
ca11e8ee68 | ||
|
ce52253f5d | ||
|
e408aa3d62 | ||
|
c4b4d17f52 | ||
|
0b72f1a37d | ||
|
1b86b31233 | ||
|
1b9e9d47ac | ||
|
decc781572 | ||
|
16ccc2632a | ||
|
40c2afbd48 | ||
|
f9038810d2 | ||
|
3435e825df | ||
|
ff7c6fd126 | ||
|
91846d8563 | ||
|
19b6339ac5 | ||
|
db4953618c | ||
|
c1deee56a0 | ||
|
b1ed1a74ba | ||
|
cf0cfd278b | ||
|
48aca6e1eb | ||
|
d83ac3afda | ||
|
2270b1f6db | ||
|
da67dbe0d7 | ||
|
133352ba31 | ||
|
761c43dc3d | ||
|
d03de97247 | ||
|
e67bbeaf8b | ||
|
6dfc72b2d2 | ||
|
fb748df4ae | ||
|
4a03c411fc | ||
|
54db7d95d6 | ||
|
59b22be2bd | ||
|
47a07f5cfe | ||
|
bca12f0738 | ||
|
27100ad38e | ||
|
2a6fd74013 | ||
|
4767c317d0 | ||
|
068f382c87 | ||
|
e7d8c4ebc2 | ||
|
9ef0f82dea | ||
|
fba8c07142 | ||
|
4aefc8b695 | ||
|
a8574d24f8 | ||
|
449e6c0e9a | ||
|
09b6f66855 | ||
|
f30fe1a78a | ||
|
0e40e25710 | ||
|
f02c932d91 | ||
|
c18cfe9ed7 | ||
|
dcb3e32845 | ||
|
08d6d1b136 | ||
|
954b0c0bd8 | ||
|
f22b4dcd46 | ||
|
c42481637e | ||
|
7ca8160d13 | ||
|
26316a82a9 | ||
|
5f29cfcd09 | ||
|
7f2e963689 | ||
|
8ea18c8cc1 | ||
|
f92fe5b57e | ||
|
bb4dcbdd7c | ||
|
8aec65449e | ||
|
ff273e7df9 | ||
|
1a1f9df93f | ||
|
80a07c0496 | ||
|
8369e4a788 | ||
|
bfeab1477c | ||
|
ad8464c6c2 | ||
|
b1ff2f7740 | ||
|
4040238bbf | ||
|
70f21070d5 | ||
|
91693bbc37 | ||
|
8a9b824302 | ||
|
06f4713baa | ||
|
e7ded68b03 | ||
|
58cec66535 | ||
|
c105d07e60 | ||
|
3e32b3caf0 | ||
|
7aa80b8b5e | ||
|
5e053872a9 | ||
|
7d162a4baf | ||
|
83d5be3f9f | ||
|
ea0a827331 | ||
|
dc551285bb | ||
|
95538f5282 | ||
|
9d706d62ef | ||
|
63fc59accc | ||
|
beb0830c25 | ||
|
ae1188ae27 | ||
|
1b27ea02e7 | ||
|
c2f2277717 | ||
|
79ccd6793c | ||
|
5be0236845 | ||
|
59bf768d14 | ||
|
566479450a | ||
|
b45e7d7e54 | ||
|
9dddd2be3e | ||
|
d3f21e0d49 | ||
|
af706e03c7 | ||
|
b6496e9c87 | ||
|
a33aedd750 | ||
|
13647213c6 | ||
|
5ea96a260d | ||
|
438dd9dd4c | ||
|
45a97d2959 | ||
|
a8de9099b1 | ||
|
adf77ba603 | ||
|
62c09af14f | ||
|
ddadfd3198 | ||
|
4f95871d83 | ||
|
fe2df5247c | ||
|
3bd836a828 | ||
|
a72bdff81d | ||
|
90f8657722 | ||
|
b7106787d1 | ||
|
3c2816baf0 | ||
|
c6443ff89e | ||
|
269a1bbd83 | ||
|
768111571e | ||
|
d6d0b1672e | ||
|
76b2d4a291 | ||
|
c3ee00673d | ||
|
7ec277e0a3 | ||
|
33b58e8d79 | ||
|
73e964fe76 | ||
|
7d79f8f365 | ||
|
2dff5a966c | ||
|
ccbfc47a66 | ||
|
032da321c3 | ||
|
39269d3b27 | ||
|
a006053297 | ||
|
4353d0e0ca | ||
|
f7b455a7f1 | ||
|
bb1a7af6c8 | ||
|
0032b0c2bb | ||
|
113917e278 | ||
|
8db6489649 | ||
|
e135b65948 | ||
|
d0bfde2e80 | ||
|
8cdda92146 | ||
|
20121431af | ||
|
59290f7d25 | ||
|
4ab834300d | ||
|
43ff3c203e | ||
|
373e8d29a3 | ||
|
e6100d247a | ||
|
838791ad05 | ||
|
5681aa4270 | ||
|
a51a1251c6 | ||
|
e9b0bcf218 | ||
|
25d2ccf950 | ||
|
a9ce1d8d73 | ||
|
e3ac682fc0 | ||
|
49fa2903fc | ||
|
1c9cbc4942 | ||
|
2cb40b84e4 | ||
|
6aa882ecbd | ||
|
b0b00b4029 | ||
|
645c6a4a36 | ||
|
2168025e80 | ||
|
ce16479742 | ||
|
0637f526ca | ||
|
1b7be1189d | ||
|
9fece93d31 | ||
|
62bf355ca3 | ||
|
5e133715cd | ||
|
df955b4885 | ||
|
a290327a3e | ||
|
34c8c16aba | ||
|
a4aa18330c | ||
|
d4d568ac78 | ||
|
d71075a16c | ||
|
a20f026cf2 | ||
|
e3eb32d493 | ||
|
903ec192e2 | ||
|
5901ea87ce | ||
|
30c44770d3 | ||
|
d1afecbfa0 | ||
|
228c70e1ea | ||
|
5507271a74 | ||
|
a8030c469c | ||
|
20ad92fbd5 | ||
|
1aaee043a3 | ||
|
ba6c067fb3 | ||
|
506742bbac | ||
|
6a1f7043cb | ||
|
78350ac7ac | ||
|
a8c83642d9 | ||
|
135a91c985 | ||
|
4791366b1e | ||
|
2ef764ea5c | ||
|
1062a18004 | ||
|
10041dd35a | ||
|
dacfb130cb | ||
|
112fbc086b | ||
|
ca64401dde | ||
|
301b6c27c4 | ||
|
7d8c1641e9 | ||
|
42524619af | ||
|
85f0f17e61 | ||
|
4a435e64e7 | ||
|
488b1cfeb2 | ||
|
5a6c3841f9 | ||
|
486180e423 | ||
|
8633d082b3 | ||
|
4cf1c43fee | ||
|
18289f382c | ||
|
fdc15f7eb3 | ||
|
ba7c178a5c | ||
|
f5ca23da26 | ||
|
e7554f7856 | ||
|
dbeedff1a2 | ||
|
ce0cabc8e2 | ||
|
21f7da6fc5 | ||
|
d9a43577f7 | ||
|
99fb63d30d | ||
|
94a5753fea | ||
|
4071baff77 | ||
|
6e70142f59 | ||
|
d801e85736 | ||
|
0ffff61265 | ||
|
dd547ac58b | ||
|
f32be5d55a | ||
|
bc2c0b0abd | ||
|
a0d702be73 | ||
|
bda2e3358a | ||
|
16c677b83d | ||
|
dcecfb1399 | ||
|
5815bf5048 | ||
|
0dddd3c32e | ||
|
c8e57055fb | ||
|
dd89855d64 | ||
|
51a98398ca | ||
|
f0244793a7 | ||
|
f84e0b0ce7 | ||
|
331cff059f | ||
|
1e27aafde0 | ||
|
14888c22e8 | ||
|
8f019f7e8c | ||
|
c07b8795bf | ||
|
3deda1c06a | ||
|
77276ab156 | ||
|
0b13adbee9 | ||
|
e7c4423ebf | ||
|
651674d1ed | ||
|
fe648c7e2c | ||
|
0fb9abf6c7 | ||
|
697c164aee | ||
|
26d218621b | ||
|
7c51072a10 | ||
|
3a33c4c3e0 | ||
|
de59d9ae29 | ||
|
75a43ee2e2 | ||
|
a9c51331b2 | ||
|
e8d9af5fa4 | ||
|
13a45cd9bf | ||
|
145bcb38de | ||
|
e2e4b554e7 | ||
|
54d04ea575 | ||
|
9fdbdf740c | ||
|
678c49f540 | ||
|
9b1d09defe | ||
|
c0dc5e8875 |
4
.gitignore
vendored
|
@ -1,4 +0,0 @@
|
|||
/bin/
|
||||
.project
|
||||
.classpath
|
||||
build.xml
|
19
README.md
|
@ -1,5 +1,6 @@
|
|||
# Apple II Disk Browser
|
||||
|
||||
### Alternative
|
||||
There is a new release of [DiskBrowser2](https://github.com/dmolony/diskbrowser2) available.
|
||||
### Features
|
||||
- Cross-platform (Windows, MacOS, Linux)
|
||||
- Disk formats
|
||||
|
@ -30,7 +31,7 @@
|
|||
* [Usage](resources/usage.md)
|
||||
|
||||
### Installation
|
||||
* Install the **latest** version of the [JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html).
|
||||
* Install the **latest** version of the [JDK](https://www.oracle.com/java/technologies/downloads/).
|
||||
* Download DiskBrowser.jar from the [releases](https://github.com/dmolony/diskbrowser/releases) page.
|
||||
* Double-click the jar file, or enter 'java -jar DiskBrowser.jar' in the terminal.
|
||||
* Set your root folder (the top-level folder where you keep your disk images).
|
||||
|
@ -51,13 +52,25 @@ Java runs on Windows, MacOS and Linux.
|
|||
![Windows](resources/windows.png?raw=true "Windows")
|
||||
#### Graphics
|
||||
![Graphics](resources/graphics.png?raw=true "Graphics")
|
||||
#### Applesoft Formatting and Analysis
|
||||
Applesoft programs are displayed in a modern, easily-readable format. A comprehensive cross-listing of variables, strings, calls and jumps is available. Easily find duplicate variable names.
|
||||
For the truly retro look, programs can be displayed in the [original 40-column line wrap](resources/basic.md) mode.
|
||||
![Applesoft](resources/basic.png?raw=true "Applesoft")
|
||||
#### Pascal code
|
||||
![Pascal](resources/pascal.png?raw=true "Pascal")
|
||||
![Pascal](resources/pascal1.png?raw=true "Pascal text")
|
||||
![Pascal](resources/pascal2.png?raw=true "Pascal internals")
|
||||
#### Infocom
|
||||
![Infocom](resources/zork.png?raw=true "Infocom")
|
||||
#### Wizardry
|
||||
Wizardry scenarios 1 to 3 are reasonably complete, Wizardry IV and V are partially done. For a dedicated Wizardry application see [WizardryApp](https://github.com/dmolony/WizardryApp).
|
||||
![Wizardry](resources/wizardry.png?raw=true "Wizardry")
|
||||
Scenarios 4 and 5 come on multiple disks, and they need to be named so that the only difference between disk names is the identifier before the '.dsk' suffix.
|
||||
![Wizardry](resources/wizardry4.png?raw=true "Wizardry IV")
|
||||
#### Visicalc
|
||||
DiskBrowser has an inbuilt Visicalc processor which will evaluate the sheet and display the results.
|
||||
![Visicalc](resources/visicalc.png?raw=true "Visicalc")
|
||||
#### Complete Disk List
|
||||
Generates a list of all the disks in your collection. The list can be sorted by any of the table headings. Checksums can be generated as needed, or for the whole collection.
|
||||
![Disk List](resources/disklist.png?raw=true "Disk List")
|
||||
Select a disk and click the duplicates button to see all of the disks that share the same checksum.
|
||||
![Duplicates](resources/duplicates.png?raw=true "Duplicates")
|
||||
|
|
40
build.xml
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project name="DiskBrowser" default="jar" basedir=".">
|
||||
|
||||
<property name="srcDir" location="src" />
|
||||
<property name="binDir" location="bin" />
|
||||
<property name="jarDir" location="${user.home}/Dropbox/Java" />
|
||||
<property name="jarFile" location="${jarDir}/DiskBrowser.jar" />
|
||||
|
||||
<target name="version">
|
||||
<echo>DiskBrowser.jar</echo>
|
||||
<echo>${ant.version}</echo>
|
||||
<echo>Java/JVM version: ${ant.java.version}</echo>
|
||||
<echo>Java/JVM detail version: ${java.version}</echo>
|
||||
</target>
|
||||
|
||||
<target name="init" depends="version">
|
||||
<delete file="${binDir}/*.class" />
|
||||
<delete file="${jarFile}" />
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="init">
|
||||
<javac debug="on" srcdir="${srcDir}" destdir="${binDir}" includeantruntime="false">
|
||||
<classpath>
|
||||
<pathelement location="." />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile">
|
||||
<jar destfile="${jarFile}">
|
||||
<fileset dir="${binDir}" />
|
||||
<zipfileset src="${jarDir}/InputPanel.jar" />
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="com.bytezone.diskbrowser.gui.DiskBrowser" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
</project>
|
13
resources/basic.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
### Applesoft program listing
|
||||
|
||||
#### State of the art circa 1980
|
||||
<img src="basic0.png" alt="terrible" width="700"/>
|
||||
|
||||
#### Original (using Print Char 21)
|
||||
<img src="basic5.png" alt="truly awful" width="400"/>
|
||||
|
||||
#### No formatting
|
||||
<img src="basic2.png" alt="better" width="850"/>
|
||||
|
||||
#### Formatted
|
||||
<img src="basic3.png" alt="best" width="550"/>
|
BIN
resources/basic.png
Normal file
After Width: | Height: | Size: 1016 KiB |
BIN
resources/basic0.png
Normal file
After Width: | Height: | Size: 730 KiB |
BIN
resources/basic1.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
resources/basic2.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
resources/basic3.png
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
resources/basic4.png
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
resources/basic5.png
Normal file
After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 724 KiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 784 KiB After Width: | Height: | Size: 1.1 MiB |
BIN
resources/duplicates.png
Normal file
After Width: | Height: | Size: 373 KiB |
Before Width: | Height: | Size: 788 KiB |
BIN
resources/pascal1.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/pascal2.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 642 KiB After Width: | Height: | Size: 1.0 MiB |
BIN
resources/wizardry4.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
|
@ -6,17 +6,21 @@ import javax.swing.JComponent;
|
|||
import javax.swing.JPanel;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.DataSource;
|
||||
import com.bytezone.diskbrowser.prodos.ResourceFork;
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public abstract class AbstractFile implements DataSource
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
static boolean showDebugText;
|
||||
|
||||
protected String name;
|
||||
public byte[] buffer;
|
||||
protected AssemblerProgram assembler;
|
||||
AssemblerProgram assembler;
|
||||
protected BufferedImage image;
|
||||
protected int loadAddress;
|
||||
int loadAddress;
|
||||
ResourceFork resourceFork;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AbstractFile (String name, byte[] buffer)
|
||||
|
@ -26,12 +30,58 @@ public abstract class AbstractFile implements DataSource
|
|||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void setName (String name)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.name = name; // Infocom ZObject uses this - but it sucks
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String getText () // Override this to get a tailored text representation
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return "Name : " + name + "\n\nNo text description";
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append ("Name : " + name + "\n\nNo text description");
|
||||
|
||||
if (resourceFork != null)
|
||||
{
|
||||
text.append ("\n\nResource Fork:\n\n");
|
||||
text.append (resourceFork);
|
||||
}
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public byte[] getBuffer ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static void setDefaultDebug (boolean value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
showDebugText = value;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void setResourceFork (ResourceFork resourceFork)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.resourceFork = resourceFork;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static void setDebug (boolean value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
showDebugText = value;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -90,7 +140,6 @@ public abstract class AbstractFile implements DataSource
|
|||
public JComponent getComponent ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
JPanel panel = new JPanel ();
|
||||
return panel;
|
||||
return new JPanel ();
|
||||
}
|
||||
}
|
82
src/com/bytezone/diskbrowser/applefile/Alignment.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
class Alignment implements ApplesoftConstants
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int equalsPosition;
|
||||
int targetLength;
|
||||
SubLine firstSubLine;
|
||||
SubLine lastSubLine;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void reset ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
equalsPosition = 0;
|
||||
targetLength = 0;
|
||||
firstSubLine = null;
|
||||
lastSubLine = null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void setFirst (SubLine subline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
reset ();
|
||||
firstSubLine = subline;
|
||||
check (subline);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void check (SubLine subline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (equalsPosition < subline.equalsPosition)
|
||||
equalsPosition = subline.equalsPosition;
|
||||
|
||||
int temp = subline.endPosition - subline.equalsPosition;
|
||||
if (targetLength < temp)
|
||||
targetLength = temp;
|
||||
|
||||
lastSubLine = subline;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public String getAlignedText (SubLine subline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder line = subline.toStringBuilder (); // get line
|
||||
|
||||
if (equalsPosition == 0 || subline.is (TOKEN_REM))
|
||||
return line.toString ();
|
||||
|
||||
int alignEqualsPos = equalsPosition;
|
||||
int targetLength = subline.endPosition - equalsPosition;
|
||||
|
||||
// insert spaces before '=' until it lines up with the other assignment lines
|
||||
while (alignEqualsPos-- > subline.equalsPosition)
|
||||
line.insert (subline.equalsPosition, ' ');
|
||||
|
||||
if (line.charAt (line.length () - 1) == ':')
|
||||
while (targetLength++ <= this.targetLength)
|
||||
line.append (" ");
|
||||
|
||||
return line.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String toString ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append (String.format ("Equals position ..... %d%n", equalsPosition));
|
||||
text.append (String.format ("Target length ....... %d%n", targetLength));
|
||||
text.append (String.format ("First subline ....... %s%n", firstSubLine));
|
||||
text.append (String.format ("Last subline ........ %s", lastSubLine));
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
}
|
177
src/com/bytezone/diskbrowser/applefile/AppleBasicFormatter.java
Normal file
|
@ -0,0 +1,177 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_BACKSPACE;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_CR;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LF;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.getIndent;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.BasicPreferences;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class AppleBasicFormatter extends BasicFormatter
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private final LineFormatter flatFormatter = new FlatLine ();
|
||||
private final LineFormatter wrapFormatter = new WrapLine ();
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AppleBasicFormatter (ApplesoftBasicProgram program,
|
||||
BasicPreferences basicPreferences)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (program, basicPreferences);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void append (StringBuilder fullText)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int loadAddress = getLoadAddress ();
|
||||
int ptr = 0;
|
||||
int linkField;
|
||||
|
||||
StringBuilder currentLine = new StringBuilder ();
|
||||
LineFormatter formatter =
|
||||
basicPreferences.appleLineWrap ? wrapFormatter : flatFormatter;
|
||||
|
||||
while ((linkField = getShort (buffer, ptr)) != 0)
|
||||
{
|
||||
int lineNumber = getShort (buffer, ptr + 2);
|
||||
currentLine.append (String.format (" %d ", lineNumber));
|
||||
ptr += 4;
|
||||
|
||||
ptr = formatter.formatLine (currentLine, ptr);
|
||||
|
||||
if (ptr != (linkField - loadAddress))
|
||||
System.out.printf ("%s: ptr: %04X, nextLine: %04X%n", program.name,
|
||||
ptr + loadAddress, linkField);
|
||||
|
||||
currentLine.append (NEWLINE);
|
||||
fullText.append (currentLine);
|
||||
currentLine.setLength (0);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
interface LineFormatter
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
abstract int formatLine (StringBuilder currentLine, int ptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
class FlatLine implements LineFormatter
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// -------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public int formatLine (StringBuilder currentLine, int ptr)
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
byte b;
|
||||
|
||||
while ((b = buffer[ptr++]) != 0)
|
||||
if (isHighBitSet (b))
|
||||
{
|
||||
String token = String.format (" %s ", ApplesoftConstants.tokens[b & 0x7F]);
|
||||
currentLine.append (token);
|
||||
}
|
||||
else
|
||||
switch (b)
|
||||
{
|
||||
case ASCII_CR:
|
||||
currentLine.append (NEWLINE);
|
||||
break;
|
||||
|
||||
case ASCII_BACKSPACE:
|
||||
if (currentLine.length () > 0)
|
||||
currentLine.deleteCharAt (currentLine.length () - 1);
|
||||
break;
|
||||
|
||||
case ASCII_LF:
|
||||
int indent = getIndent (currentLine);
|
||||
currentLine.append ("\n");
|
||||
for (int i = 0; i < indent; i++)
|
||||
currentLine.append (" ");
|
||||
break;
|
||||
|
||||
default:
|
||||
currentLine.append ((char) b);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
class WrapLine implements LineFormatter
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final int LEFT_MARGIN = 5;
|
||||
private static final int RIGHT_MARGIN = 33;
|
||||
|
||||
// -------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public int formatLine (StringBuilder currentLine, int ptr)
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
byte b;
|
||||
int cursor = currentLine.length ();
|
||||
|
||||
while ((b = buffer[ptr++]) != 0)
|
||||
if (isHighBitSet (b))
|
||||
{
|
||||
String token = String.format (" %s ", ApplesoftConstants.tokens[b & 0x7F]);
|
||||
currentLine.append (token);
|
||||
cursor = incrementCursor (currentLine, cursor, token.length ());
|
||||
}
|
||||
else
|
||||
switch (b)
|
||||
{
|
||||
case ASCII_CR:
|
||||
currentLine.append (NEWLINE);
|
||||
cursor = 0;
|
||||
break;
|
||||
|
||||
case ASCII_BACKSPACE:
|
||||
if (cursor > 0)
|
||||
{
|
||||
currentLine.deleteCharAt (currentLine.length () - 1);
|
||||
--cursor;
|
||||
}
|
||||
break;
|
||||
|
||||
case ASCII_LF:
|
||||
currentLine.append ("\n");
|
||||
for (int i = 0; i < cursor; i++)
|
||||
currentLine.append (" ");
|
||||
break;
|
||||
|
||||
default:
|
||||
currentLine.append ((char) b);
|
||||
cursor = incrementCursor (currentLine, cursor, 1);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------//
|
||||
private int incrementCursor (StringBuilder currentLine, int cursor, int size)
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
assert size <= 9; // longest token possible (7 plus 2 spaces)
|
||||
cursor += size;
|
||||
|
||||
if ((cursor) >= RIGHT_MARGIN)
|
||||
{
|
||||
cursor = cursor >= 40 ? cursor - 40 : LEFT_MARGIN;
|
||||
currentLine.append ("\n ".substring (0, cursor + 1));
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +1,22 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class ApplesoftBasicProgram extends BasicProgram
|
||||
public class ApplesoftBasicProgram extends BasicProgram implements ApplesoftConstants
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final byte TOKEN_FOR = (byte) 0x81;
|
||||
private static final byte TOKEN_NEXT = (byte) 0x82;
|
||||
private static final byte TOKEN_INPUT = (byte) 0x84;
|
||||
private static final byte TOKEN_LET = (byte) 0xAA;
|
||||
private static final byte TOKEN_GOTO = (byte) 0xAB;
|
||||
private static final byte TOKEN_IF = (byte) 0xAD;
|
||||
private static final byte TOKEN_GOSUB = (byte) 0xB0;
|
||||
private static final byte TOKEN_REM = (byte) 0xB2;
|
||||
private static final byte TOKEN_PRINT = (byte) 0xBA;
|
||||
private static final byte TOKEN_THEN = (byte) 0xC4;
|
||||
private static final byte TOKEN_EQUALS = (byte) 0xD0;
|
||||
|
||||
private final List<SourceLine> sourceLines = new ArrayList<> ();
|
||||
private final int endPtr;
|
||||
private final Set<Integer> gotoLines = new HashSet<> ();
|
||||
private final Set<Integer> gosubLines = new HashSet<> ();
|
||||
private int ptr = 0; // end-of-program marker
|
||||
|
||||
private final UserBasicFormatter userBasicFormatter;
|
||||
private final AppleBasicFormatter appleBasicFormatter;
|
||||
private final DebugBasicFormatter debugBasicFormatter;
|
||||
private final XrefFormatter xrefFormatter;
|
||||
private final HeaderFormatter headerFormatter;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public ApplesoftBasicProgram (String name, byte[] buffer)
|
||||
|
@ -36,22 +24,18 @@ public class ApplesoftBasicProgram extends BasicProgram
|
|||
{
|
||||
super (name, buffer);
|
||||
|
||||
int ptr = 0;
|
||||
int prevOffset = 0;
|
||||
|
||||
int max = buffer.length - 4; // need at least 4 bytes to make a SourceLine
|
||||
while (ptr < max)
|
||||
while (buffer[ptr + 1] != 0) // msb of link field
|
||||
{
|
||||
int offset = Utility.unsignedShort (buffer, ptr);
|
||||
if (offset <= prevOffset)
|
||||
break;
|
||||
|
||||
SourceLine line = new SourceLine (ptr);
|
||||
SourceLine line = new SourceLine (this, buffer, ptr);
|
||||
sourceLines.add (line);
|
||||
ptr += line.length;
|
||||
prevOffset = offset;
|
||||
ptr += line.length; // assumes lines are contiguous
|
||||
}
|
||||
endPtr = ptr;
|
||||
|
||||
userBasicFormatter = new UserBasicFormatter (this, basicPreferences);
|
||||
appleBasicFormatter = new AppleBasicFormatter (this, basicPreferences);
|
||||
debugBasicFormatter = new DebugBasicFormatter (this, basicPreferences);
|
||||
xrefFormatter = new XrefFormatter (this, basicPreferences);
|
||||
headerFormatter = new HeaderFormatter (this, basicPreferences);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -59,731 +43,45 @@ public class ApplesoftBasicProgram extends BasicProgram
|
|||
public String getText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder fullText = new StringBuilder ();
|
||||
Stack<String> loopVariables = new Stack<String> ();
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
if (basicPreferences.showHeader)
|
||||
addHeader (fullText);
|
||||
int alignPos = 0;
|
||||
StringBuilder text;
|
||||
int baseOffset = basicPreferences.showTargets ? 12 : 8;
|
||||
headerFormatter.append (text);
|
||||
|
||||
for (SourceLine line : sourceLines)
|
||||
if (showDebugText)
|
||||
{
|
||||
text = new StringBuilder (getBase (line) + " ");
|
||||
|
||||
int indent = loopVariables.size (); // each full line starts at the loop indent
|
||||
int ifIndent = 0; // IF statement(s) limit back indentation by NEXT
|
||||
|
||||
for (SubLine subline : line.sublines)
|
||||
{
|
||||
// Allow empty statements (caused by a single colon)
|
||||
if (subline.isEmpty ())
|
||||
continue;
|
||||
|
||||
// A REM statement might conceal an assembler routine
|
||||
// - see P.CREATE on Diags2E.DSK
|
||||
if (subline.is (TOKEN_REM) && subline.containsToken ())
|
||||
{
|
||||
int address = subline.getAddress () + 1; // skip the REM token
|
||||
fullText.append (text + String.format ("REM - Inline assembler @ $%02X (%d)%n",
|
||||
address, address));
|
||||
String padding = " ".substring (0, text.length () + 2);
|
||||
for (String asm : subline.getAssembler ())
|
||||
fullText.append (padding + asm + "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reduce the indent by each NEXT, but only as far as the IF indent allows
|
||||
if (subline.is (TOKEN_NEXT))
|
||||
{
|
||||
popLoopVariables (loopVariables, subline);
|
||||
indent = Math.max (ifIndent, loopVariables.size ());
|
||||
}
|
||||
|
||||
// Are we joining REM lines with the previous subline?
|
||||
if (!basicPreferences.splitRem && subline.isJoinableRem ())
|
||||
{
|
||||
// Join this REM statement to the previous line, so no indenting
|
||||
fullText.deleteCharAt (fullText.length () - 1); // remove newline
|
||||
fullText.append (" ");
|
||||
}
|
||||
else // ... otherwise do all the indenting and showing of targets etc.
|
||||
{
|
||||
// Prepare target indicators for subsequent sublines (ie no line number)
|
||||
if (basicPreferences.showTargets && !subline.isFirst ())
|
||||
if (subline.is (TOKEN_GOSUB))
|
||||
text.append ("<<--");
|
||||
else if (subline.is (TOKEN_GOTO) || subline.isImpliedGoto ())
|
||||
text.append (" <--");
|
||||
|
||||
// Align assign statements if required
|
||||
if (basicPreferences.alignAssign)
|
||||
alignPos = alignEqualsPosition (subline, alignPos);
|
||||
|
||||
int column = indent * 2 + baseOffset;
|
||||
while (text.length () < column)
|
||||
text.append (" ");
|
||||
}
|
||||
|
||||
// Add the current text, then reset it
|
||||
int pos = subline.is (TOKEN_REM) ? 0 : alignPos;
|
||||
String lineText = subline.getAlignedText (pos);
|
||||
|
||||
// Check for a wrappable REM statement
|
||||
// (see SEA BATTLE on DISK283.DSK)
|
||||
if (subline.is (TOKEN_REM) && lineText.length () > basicPreferences.wrapRemAt + 4)
|
||||
{
|
||||
// System.out.println (lineText.length ());
|
||||
String copy = lineText.substring (4);
|
||||
text.append ("REM ");
|
||||
int inset = text.length () + 1;
|
||||
List<String> remarks = splitRemark (copy, basicPreferences.wrapRemAt);
|
||||
boolean first = true;
|
||||
for (String remark : remarks)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
text.append (remark);
|
||||
}
|
||||
else
|
||||
text.append ("\n ".substring (0, inset) + remark);
|
||||
}
|
||||
}
|
||||
else
|
||||
text.append (lineText);
|
||||
|
||||
// Check for a wrappable PRINT statement
|
||||
// (see FROM MACHINE LANGUAGE TO BASIC on DOSToolkit2eB.dsk)
|
||||
if (basicPreferences.wrapPrintAt > 0 //
|
||||
&& (subline.is (TOKEN_PRINT) || subline.is (TOKEN_INPUT))
|
||||
&& countChars (text, ASCII_QUOTE) == 2 // just start and end quotes
|
||||
&& countChars (text, ASCII_CARET) == 0) // no control characters
|
||||
// && countChars (text, ASCII_SEMI_COLON) == 0)
|
||||
{
|
||||
if (true) // new method
|
||||
{
|
||||
List<String> lines = splitPrint (lineText);
|
||||
if (lines != null)
|
||||
{
|
||||
int offset = text.indexOf ("PRINT");
|
||||
if (offset < 0)
|
||||
offset = text.indexOf ("INPUT");
|
||||
String fmt = "%-" + offset + "." + offset + "s%s%n";
|
||||
String padding = text.substring (0, offset);
|
||||
for (String s : lines)
|
||||
{
|
||||
fullText.append (String.format (fmt, padding, s));
|
||||
padding = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
fullText.append (text + "\n");
|
||||
}
|
||||
else // old method
|
||||
{
|
||||
int first = text.indexOf ("\"") + 1;
|
||||
int last = text.indexOf ("\"", first + 1) - 1;
|
||||
if ((last - first) > basicPreferences.wrapPrintAt)
|
||||
{
|
||||
int ptr = first + basicPreferences.wrapPrintAt;
|
||||
do
|
||||
{
|
||||
fullText.append (text.substring (0, ptr)
|
||||
+ "\n ".substring (0, first + 1));
|
||||
text.delete (0, ptr);
|
||||
ptr = basicPreferences.wrapPrintAt;
|
||||
} while (text.length () > basicPreferences.wrapPrintAt);
|
||||
}
|
||||
fullText.append (text + "\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
fullText.append (text + "\n");
|
||||
|
||||
text.setLength (0);
|
||||
|
||||
// Calculate indent changes that take effect after the current subline
|
||||
if (subline.is (TOKEN_IF))
|
||||
ifIndent = ++indent;
|
||||
else if (subline.is (TOKEN_FOR))
|
||||
{
|
||||
loopVariables.push (subline.forVariable);
|
||||
++indent;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset alignment value if we just left an IF - the indentation will be different now.
|
||||
if (ifIndent > 0)
|
||||
alignPos = 0;
|
||||
debugBasicFormatter.append (text);
|
||||
return Utility.rtrim (text);
|
||||
}
|
||||
|
||||
int ptr = endPtr + 2;
|
||||
if (ptr < buffer.length - 1) // sometimes there's an extra byte on the end
|
||||
if (sourceLines.size () == 0)
|
||||
{
|
||||
int offset = Utility.unsignedShort (buffer, 0);
|
||||
int programLoadAddress = offset - getLineLength (0);
|
||||
fullText.append ("\nExtra data:\n\n");
|
||||
fullText.append (HexFormatter.formatNoHeader (buffer, ptr, buffer.length - ptr,
|
||||
programLoadAddress + ptr));
|
||||
text.append ("\n\nThis page intentionally left blank");
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
if (fullText.length () > 0)
|
||||
fullText.deleteCharAt (fullText.length () - 1); // remove last newline
|
||||
|
||||
return fullText.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private List<String> splitPrint (String line)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int first = line.indexOf ("\"") + 1;
|
||||
int last = line.indexOf ("\"", first + 1) - 1;
|
||||
|
||||
if (first != 7 || (last - first) <= basicPreferences.wrapPrintAt)
|
||||
return null;
|
||||
|
||||
int charsLeft = last - first + 1;
|
||||
|
||||
List<String> lines = new ArrayList<> ();
|
||||
String padding = line.substring (0, 7);
|
||||
line = line.substring (7);
|
||||
String sub;
|
||||
while (true)
|
||||
{
|
||||
if (line.length () >= basicPreferences.wrapPrintAt)
|
||||
{
|
||||
sub = line.substring (0, basicPreferences.wrapPrintAt);
|
||||
line = line.substring (basicPreferences.wrapPrintAt);
|
||||
}
|
||||
else
|
||||
{
|
||||
sub = line;
|
||||
line = "";
|
||||
}
|
||||
|
||||
String subline = padding + sub;
|
||||
charsLeft -= basicPreferences.wrapPrintAt;
|
||||
|
||||
if (charsLeft > 0)
|
||||
lines.add (subline);
|
||||
else
|
||||
{
|
||||
lines.add (subline + line);
|
||||
break;
|
||||
}
|
||||
padding = " ";
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private List<String> splitRemark (String remark, int wrapLength)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<String> remarks = new ArrayList<> ();
|
||||
while (remark.length () > wrapLength)
|
||||
{
|
||||
int max = Math.min (wrapLength, remark.length () - 1);
|
||||
while (max > 0 && remark.charAt (max) != ' ')
|
||||
--max;
|
||||
if (max == 0)
|
||||
break;
|
||||
remarks.add (remark.substring (0, max));
|
||||
remark = remark.substring (max);
|
||||
}
|
||||
remarks.add (remark);
|
||||
return remarks;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int countChars (StringBuilder text, byte ch)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int total = 0;
|
||||
for (int i = 0; i < text.length (); i++)
|
||||
if (text.charAt (i) == ch)
|
||||
total++;
|
||||
return total;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getBase (SourceLine line)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (!basicPreferences.showTargets)
|
||||
return String.format (" %5d", line.lineNumber);
|
||||
|
||||
String lineNumberText = String.format ("%5d", line.lineNumber);
|
||||
SubLine subline = line.sublines.get (0);
|
||||
String c1 = " ", c2 = " ";
|
||||
if (subline.is (TOKEN_GOSUB))
|
||||
c1 = "<<";
|
||||
if (subline.is (TOKEN_GOTO))
|
||||
c1 = " <";
|
||||
if (gotoLines.contains (line.lineNumber))
|
||||
c2 = "> ";
|
||||
if (gosubLines.contains (line.lineNumber))
|
||||
c2 = ">>";
|
||||
if (c1.equals (" ") && !c2.equals (" "))
|
||||
c1 = "--";
|
||||
if (!c1.equals (" ") && c2.equals (" "))
|
||||
c2 = "--";
|
||||
if (basicPreferences.onlyShowTargetLineNumbers && !c2.startsWith (">"))
|
||||
lineNumberText = "";
|
||||
return String.format ("%s%s %s", c1, c2, lineNumberText);
|
||||
}
|
||||
|
||||
// Decide whether the current subline needs to be aligned on its equals sign. If so,
|
||||
// and the column hasn't been calculated, read ahead to find the highest position.
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int alignEqualsPosition (SubLine subline, int currentAlignPosition)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (subline.assignEqualPos > 0) // does the line have an equals sign?
|
||||
{
|
||||
if (currentAlignPosition == 0)
|
||||
currentAlignPosition = findHighest (subline); // examine following sublines for alignment
|
||||
return currentAlignPosition;
|
||||
}
|
||||
return 0; // reset it
|
||||
}
|
||||
|
||||
// The IF processing is so that any assignment that is being aligned doesn't continue
|
||||
// to the next full line (because the indentation has changed).
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int findHighest (SubLine startSubline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
boolean started = false;
|
||||
int highestAssign = startSubline.assignEqualPos;
|
||||
fast: for (SourceLine line : sourceLines)
|
||||
{
|
||||
boolean inIf = false;
|
||||
for (SubLine subline : line.sublines)
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
// Stop when we come to a line without an equals sign (except for non-split REMs).
|
||||
// Lines that start with a REM always break.
|
||||
if (subline.assignEqualPos == 0
|
||||
// && (splitRem || !subline.is (TOKEN_REM) || subline.isFirst ()))
|
||||
&& (basicPreferences.splitRem || !subline.isJoinableRem ()))
|
||||
break fast; // of champions
|
||||
|
||||
if (subline.assignEqualPos > highestAssign)
|
||||
highestAssign = subline.assignEqualPos;
|
||||
}
|
||||
else if (subline == startSubline)
|
||||
started = true;
|
||||
else if (subline.is (TOKEN_IF))
|
||||
inIf = true;
|
||||
}
|
||||
if (started && inIf)
|
||||
break;
|
||||
}
|
||||
return highestAssign;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String getHexDump ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (buffer.length < 2)
|
||||
return super.getHexDump ();
|
||||
|
||||
StringBuilder pgm = new StringBuilder ();
|
||||
if (basicPreferences.showHeader)
|
||||
addHeader (pgm);
|
||||
|
||||
int ptr = 0;
|
||||
int offset = Utility.unsignedShort (buffer, 0);
|
||||
int programLoadAddress = offset - getLineLength (0);
|
||||
|
||||
while (ptr <= endPtr) // stop at the same place as the source listing
|
||||
{
|
||||
int length = getLineLength (ptr);
|
||||
if (length == 0)
|
||||
{
|
||||
pgm.append (
|
||||
HexFormatter.formatNoHeader (buffer, ptr, 2, programLoadAddress + ptr));
|
||||
ptr += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ptr + length < buffer.length)
|
||||
pgm.append (
|
||||
HexFormatter.formatNoHeader (buffer, ptr, length, programLoadAddress + ptr)
|
||||
+ "\n\n");
|
||||
ptr += length;
|
||||
}
|
||||
|
||||
if (ptr < buffer.length)
|
||||
{
|
||||
int length = buffer.length - ptr;
|
||||
pgm.append ("\n\n");
|
||||
pgm.append (
|
||||
HexFormatter.formatNoHeader (buffer, ptr, length, programLoadAddress + ptr));
|
||||
}
|
||||
|
||||
return pgm.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addHeader (StringBuilder pgm)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
pgm.append ("Name : " + name + "\n");
|
||||
pgm.append (String.format ("Length : $%04X (%<,d)%n", buffer.length));
|
||||
pgm.append (String.format ("Load at : $%04X (%<,d)%n%n", getLoadAddress ()));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getLoadAddress ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int programLoadAddress = 0;
|
||||
if (buffer.length > 1)
|
||||
{
|
||||
int offset = Utility.intValue (buffer[0], buffer[1]);
|
||||
programLoadAddress = offset - getLineLength (0);
|
||||
}
|
||||
return programLoadAddress;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getLineLength (int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int offset = Utility.unsignedShort (buffer, ptr);
|
||||
if (offset == 0)
|
||||
return 0;
|
||||
ptr += 4; // skip offset and line number
|
||||
int length = 5;
|
||||
|
||||
while (ptr < buffer.length && buffer[ptr++] != 0)
|
||||
length++;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void popLoopVariables (Stack<String> loopVariables, SubLine subline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (subline.nextVariables.length == 0) // naked NEXT
|
||||
{
|
||||
if (loopVariables.size () > 0)
|
||||
loopVariables.pop ();
|
||||
}
|
||||
if (basicPreferences.userFormat)
|
||||
userBasicFormatter.append (text);
|
||||
else
|
||||
for (String variable : subline.nextVariables)
|
||||
// e.g. NEXT X,Y,Z
|
||||
while (loopVariables.size () > 0)
|
||||
if (sameVariable (variable, loopVariables.pop ()))
|
||||
break;
|
||||
appleBasicFormatter.append (text);
|
||||
|
||||
if (basicPreferences.showAllXref)
|
||||
xrefFormatter.append (text);
|
||||
|
||||
return Utility.rtrim (text);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean sameVariable (String v1, String v2)
|
||||
List<SourceLine> getSourceLines ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (v1.equals (v2))
|
||||
return true;
|
||||
if (v1.length () >= 2 && v2.length () >= 2 && v1.charAt (0) == v2.charAt (0)
|
||||
&& v1.charAt (1) == v2.charAt (1))
|
||||
return true;
|
||||
return false;
|
||||
return sourceLines;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private class SourceLine
|
||||
int getEndPtr ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<SubLine> sublines = new ArrayList<> ();
|
||||
int lineNumber;
|
||||
int linePtr;
|
||||
int length;
|
||||
|
||||
public SourceLine (int ptr)
|
||||
{
|
||||
linePtr = ptr;
|
||||
lineNumber = Utility.intValue (buffer[ptr + 2], buffer[ptr + 3]);
|
||||
|
||||
int startPtr = ptr += 4;
|
||||
boolean inString = false; // can toggle
|
||||
boolean inRemark = false; // can only go false -> true
|
||||
byte b;
|
||||
|
||||
while ((b = buffer[ptr++]) != 0)
|
||||
{
|
||||
if (inRemark) // cannot terminate a REM
|
||||
continue;
|
||||
|
||||
if (inString)
|
||||
{
|
||||
if (b == ASCII_QUOTE) // terminate string
|
||||
inString = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (b)
|
||||
{
|
||||
// break IF statements into two sublines (allows for easier line indenting)
|
||||
case TOKEN_IF:
|
||||
// skip to THEN or GOTO - if not found then it's an error
|
||||
while (buffer[ptr] != TOKEN_THEN && buffer[ptr] != TOKEN_GOTO
|
||||
&& buffer[ptr] != 0)
|
||||
ptr++;
|
||||
|
||||
// keep THEN with the IF
|
||||
if (buffer[ptr] == TOKEN_THEN)
|
||||
++ptr;
|
||||
|
||||
// create subline from the condition (and THEN if it exists)
|
||||
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
|
||||
startPtr = ptr;
|
||||
|
||||
break;
|
||||
|
||||
// end of subline, so add it, advance startPtr and continue
|
||||
case ASCII_COLON:
|
||||
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
|
||||
startPtr = ptr;
|
||||
break;
|
||||
|
||||
case TOKEN_REM:
|
||||
if (ptr != startPtr + 1) // REM appears mid-line (should follow a colon)
|
||||
{
|
||||
System.out.println ("mid-line REM token");
|
||||
// System.out.println (HexFormatter.format (buffer, startPtr, 10));
|
||||
sublines.add (new SubLine (this, startPtr, (ptr - startPtr) - 1));
|
||||
startPtr = ptr - 1;
|
||||
}
|
||||
else
|
||||
inRemark = true;
|
||||
|
||||
break;
|
||||
|
||||
case ASCII_QUOTE:
|
||||
inString = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add whatever is left
|
||||
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
|
||||
this.length = ptr - linePtr;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private class SubLine
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
SourceLine parent;
|
||||
int startPtr;
|
||||
int length;
|
||||
String[] nextVariables;
|
||||
String forVariable = "";
|
||||
int targetLine = -1;
|
||||
|
||||
// used for aligning the equals sign
|
||||
int assignEqualPos;
|
||||
|
||||
public SubLine (SourceLine parent, int startPtr, int length)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.startPtr = startPtr;
|
||||
this.length = length;
|
||||
|
||||
byte b = buffer[startPtr];
|
||||
if (isHighBitSet (b))
|
||||
{
|
||||
switch (b)
|
||||
{
|
||||
case TOKEN_FOR:
|
||||
int p = startPtr + 1;
|
||||
while (buffer[p] != TOKEN_EQUALS)
|
||||
forVariable += (char) buffer[p++];
|
||||
break;
|
||||
|
||||
case TOKEN_NEXT:
|
||||
if (length == 2) // no variables
|
||||
nextVariables = new String[0];
|
||||
else
|
||||
{
|
||||
String varList = new String (buffer, startPtr + 1, length - 2);
|
||||
nextVariables = varList.split (",");
|
||||
}
|
||||
break;
|
||||
|
||||
case TOKEN_LET:
|
||||
recordEqualsPosition ();
|
||||
break;
|
||||
|
||||
case TOKEN_GOTO:
|
||||
String target = new String (buffer, startPtr + 1, length - 2);
|
||||
try
|
||||
{
|
||||
gotoLines.add (Integer.parseInt (target));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
System.out.println (
|
||||
"Error parsing : GOTO " + target + " in " + parent.lineNumber);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOKEN_GOSUB:
|
||||
String target2 = new String (buffer, startPtr + 1, length - 2);
|
||||
try
|
||||
{
|
||||
gosubLines.add (Integer.parseInt (target2));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
System.out.println (HexFormatter.format (buffer, startPtr + 1, length - 2));
|
||||
System.out.println (
|
||||
"Error parsing : GOSUB " + target2 + " in " + parent.lineNumber);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDigit (b)) // numeric, so must be a line number
|
||||
{
|
||||
String target = new String (buffer, startPtr, length - 1);
|
||||
try
|
||||
{
|
||||
targetLine = Integer.parseInt (target);
|
||||
gotoLines.add (targetLine);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
System.out.printf ("b: %d, start: %d, length: %d%n", b, startPtr,
|
||||
(length - 1));
|
||||
System.out.println (target);
|
||||
System.out.println (HexFormatter.format (buffer, startPtr, length - 1));
|
||||
System.out.println (e);
|
||||
}
|
||||
}
|
||||
// else if (basicPreferences.alignAssign)
|
||||
else
|
||||
recordEqualsPosition ();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isImpliedGoto ()
|
||||
{
|
||||
byte b = buffer[startPtr];
|
||||
if (isHighBitSet (b))
|
||||
return false;
|
||||
return (isDigit (b));
|
||||
}
|
||||
|
||||
// Record the position of the equals sign so it can be aligned with adjacent lines.
|
||||
private void recordEqualsPosition ()
|
||||
{
|
||||
int p = startPtr + 1;
|
||||
int max = startPtr + length;
|
||||
while (buffer[p] != TOKEN_EQUALS && p < max)
|
||||
p++;
|
||||
if (buffer[p] == TOKEN_EQUALS)
|
||||
assignEqualPos = toString ().indexOf ('='); // use expanded line
|
||||
}
|
||||
|
||||
private boolean isJoinableRem ()
|
||||
{
|
||||
return is (TOKEN_REM) && !isFirst ();
|
||||
}
|
||||
|
||||
public boolean isFirst ()
|
||||
{
|
||||
return (parent.linePtr + 4) == startPtr;
|
||||
}
|
||||
|
||||
public boolean is (byte token)
|
||||
{
|
||||
return buffer[startPtr] == token;
|
||||
}
|
||||
|
||||
public boolean isEmpty ()
|
||||
{
|
||||
return length == 1 && buffer[startPtr] == 0;
|
||||
}
|
||||
|
||||
public boolean containsToken ()
|
||||
{
|
||||
// ignore first byte, check the rest for tokens
|
||||
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
|
||||
if (isHighBitSet (buffer[p]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getAddress ()
|
||||
{
|
||||
return getLoadAddress () + startPtr;
|
||||
}
|
||||
|
||||
public String getAlignedText (int alignPosition)
|
||||
{
|
||||
StringBuilder line = toStringBuilder ();
|
||||
|
||||
while (alignPosition-- > assignEqualPos)
|
||||
line.insert (assignEqualPos, ' ');
|
||||
|
||||
return line.toString ();
|
||||
}
|
||||
|
||||
// A REM statement might conceal an assembler routine
|
||||
public String[] getAssembler ()
|
||||
{
|
||||
byte[] buffer2 = new byte[length - 1];
|
||||
System.arraycopy (buffer, startPtr + 1, buffer2, 0, buffer2.length);
|
||||
AssemblerProgram program =
|
||||
new AssemblerProgram ("REM assembler", buffer2, getAddress () + 1);
|
||||
return program.getAssembler ().split ("\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return toStringBuilder ().toString ();
|
||||
}
|
||||
|
||||
public StringBuilder toStringBuilder ()
|
||||
{
|
||||
StringBuilder line = new StringBuilder ();
|
||||
|
||||
// All sublines end with 0 or : except IF lines that are split into two
|
||||
int max = startPtr + length - 1;
|
||||
if (buffer[max] == 0)
|
||||
--max;
|
||||
|
||||
for (int p = startPtr; p <= max; p++)
|
||||
{
|
||||
byte b = buffer[p];
|
||||
if (isHighBitSet (b))
|
||||
{
|
||||
if (line.length () > 0 && line.charAt (line.length () - 1) != ' ')
|
||||
line.append (' ');
|
||||
int val = b & 0x7F;
|
||||
if (val < ApplesoftConstants.tokens.length)
|
||||
line.append (ApplesoftConstants.tokens[val]);
|
||||
}
|
||||
else if (isControlCharacter (b))
|
||||
line.append (basicPreferences.showCaret ? "^" + (char) (b + 64) : "");
|
||||
else
|
||||
line.append ((char) b);
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
}
|
|
@ -5,40 +5,62 @@ public interface ApplesoftConstants
|
|||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
String[] tokens = { //
|
||||
"END ", "FOR ", "NEXT ", "DATA ", // 0x80 - 0x83
|
||||
"INPUT ", "DEL ", "DIM ", "READ ", // 0x84 - 0x87
|
||||
"GR ", "TEXT ", "PR# ", "IN# ", // 0x88 - 0x8B
|
||||
"CALL ", "PLOT ", "HLIN ", "VLIN ", // 0x8C - 0x8F
|
||||
"HGR2", "HGR", "HCOLOR=", "HPLOT ", // 0x90
|
||||
"DRAW ", "XDRAW ", "HTAB ", "HOME ", // 0x94
|
||||
"ROT=", "SCALE=", "SHLOAD ", "TRACE ", // 0x98
|
||||
"NOTRACE ", "NORMAL ", "INVERSE ", "FLASH ", // 0x9C
|
||||
"COLOR=", "POP ", "VTAB ", "HIMEM:", // 0xA0
|
||||
"LOMEM:", "ONERR ", "RESUME", "RECALL ", // 0xA4 - 0xA7
|
||||
"STORE ", "SPEED=", "LET ", "GOTO ", // 0xA8
|
||||
"RUN ", "IF ", "RESTORE ", "&", // 0xAC
|
||||
"GOSUB ", "RETURN ", "REM ", "STOP ", // 0xB0
|
||||
"ON ", "WAIT ", "LOAD ", "SAVE ", // 0xB4
|
||||
"DEF", "POKE ", "PRINT ", "CONT", // 0xB8
|
||||
"LIST ", "CLEAR ", "GET ", "NEW ", // 0xBC
|
||||
"TAB(", "TO ", "FN ", "SPC(", // 0xC0
|
||||
"THEN ", "AT ", "NOT ", "STEP ", // 0xC4
|
||||
"+ ", "- ", "* ", "/ ", // 0xC8
|
||||
"^ ", "AND ", "OR ", "> ", // 0xCC
|
||||
"= ", "< ", "SGN ", "INT ", // 0xD0
|
||||
"ABS ", "USR", "FRE ", "SCRN(", // 0xD4
|
||||
"PDL ", "POS ", "SQR ", "RND ", // 0xD8
|
||||
"LOG ", "EXP ", "COS ", "SIN ", // 0xDC
|
||||
"TAN ", "ATN ", "PEEK ", "LEN ", // 0xE0 - 0xE3
|
||||
"STR$ ", "VAL ", "ASC ", "CHR$ ", // 0xE4 - 0xE7
|
||||
"LEFT$ ", "RIGHT$ ", "MID$ ", "", // 0xE8 - 0xEB
|
||||
"", "", "", "", // 0xEC - 0xEF
|
||||
"ELSE", "MOD", "INC", "DEC", // 0xF0 - 0xF3
|
||||
"DEEK", "DOKE", "REPEAT", "UNTIL", // 0xF4 - 0xF7
|
||||
"", "", "", "", // 0xF8 - 0xFB
|
||||
"", "", "", "", // 0xFC - 0xFF
|
||||
"END", "FOR", "NEXT", "DATA", // 0x80 - 0x83
|
||||
"INPUT", "DEL", "DIM", "READ", // 0x84 - 0x87
|
||||
"GR", "TEXT", "PR#", "IN#", // 0x88 - 0x8B
|
||||
"CALL", "PLOT", "HLIN", "VLIN", // 0x8C - 0x8F
|
||||
"HGR2", "HGR", "HCOLOR=", "HPLOT", // 0x90
|
||||
"DRAW", "XDRAW", "HTAB", "HOME", // 0x94
|
||||
"ROT=", "SCALE=", "SHLOAD", "TRACE", // 0x98
|
||||
"NOTRACE", "NORMAL", "INVERSE", "FLASH", // 0x9C
|
||||
"COLOR=", "POP", "VTAB", "HIMEM:", // 0xA0
|
||||
"LOMEM:", "ONERR", "RESUME", "RECALL", // 0xA4 - 0xA7
|
||||
"STORE", "SPEED=", "LET", "GOTO", // 0xA8
|
||||
"RUN", "IF", "RESTORE", "&", // 0xAC
|
||||
"GOSUB", "RETURN", "REM", "STOP", // 0xB0
|
||||
"ON", "WAIT", "LOAD", "SAVE", // 0xB4
|
||||
"DEF", "POKE", "PRINT", "CONT", // 0xB8
|
||||
"LIST", "CLEAR", "GET", "NEW", // 0xBC
|
||||
"TAB(", "TO", "FN", "SPC(", // 0xC0
|
||||
"THEN", "AT", "NOT", "STEP", // 0xC4
|
||||
"+", "-", "*", "/", // 0xC8
|
||||
"^", "AND", "OR", ">", // 0xCC
|
||||
"=", "<", "SGN", "INT", // 0xD0
|
||||
"ABS", "USR", "FRE", "SCRN(", // 0xD4
|
||||
"PDL", "POS", "SQR", "RND", // 0xD8
|
||||
"LOG", "EXP", "COS", "SIN", // 0xDC
|
||||
"TAN", "ATN", "PEEK", "LEN", // 0xE0 - 0xE3
|
||||
"STR$", "VAL", "ASC", "CHR$", // 0xE4 - 0xE7
|
||||
"LEFT$", "RIGHT$", "MID$", "", // 0xE8 - 0xEB
|
||||
"", "", "", "", // 0xEC - 0xEF
|
||||
"", "", "", "", // 0xF0 - 0xF3
|
||||
"", "", "", "", // 0xF4 - 0xF7
|
||||
"", "", "", "", // 0xF8 - 0xFB
|
||||
"", "", "", "", // 0xFC - 0xFF
|
||||
};
|
||||
|
||||
static final byte TOKEN_FOR = (byte) 0x81;
|
||||
static final byte TOKEN_NEXT = (byte) 0x82;
|
||||
static final byte TOKEN_DATA = (byte) 0x83;
|
||||
static final byte TOKEN_INPUT = (byte) 0x84;
|
||||
static final byte TOKEN_DIM = (byte) 0x86;
|
||||
static final byte TOKEN_CALL = (byte) 0x8C;
|
||||
static final byte TOKEN_ONERR = (byte) 0xA5;
|
||||
static final byte TOKEN_LET = (byte) 0xAA;
|
||||
static final byte TOKEN_GOTO = (byte) 0xAB;
|
||||
static final byte TOKEN_IF = (byte) 0xAD;
|
||||
static final byte TOKEN_AMPERSAND = (byte) 0xAF;
|
||||
static final byte TOKEN_GOSUB = (byte) 0xB0;
|
||||
static final byte TOKEN_RETURN = (byte) 0xB1;
|
||||
static final byte TOKEN_REM = (byte) 0xB2;
|
||||
static final byte TOKEN_ON = (byte) 0xB4;
|
||||
static final byte TOKEN_DEF = (byte) 0xB8;
|
||||
static final byte TOKEN_PRINT = (byte) 0xBA;
|
||||
static final byte TOKEN_FN = (byte) 0xC2;
|
||||
static final byte TOKEN_THEN = (byte) 0xC4;
|
||||
static final byte TOKEN_MINUS = (byte) 0xC9;
|
||||
static final byte TOKEN_EQUALS = (byte) 0xD0;
|
||||
|
||||
int[] tokenAddresses =
|
||||
{ 0xD870, 0xD766, 0xDCF9, 0xD995, 0xDBB2, 0xF331, 0xDFD9, 0xDBE2, 0xF390, 0xF399,
|
||||
0xF1E5, 0xF1DE, 0xF1D5, 0xF225, 0xF232, 0xF241, 0xF3D8, 0xF3E2, 0xF6E9, 0xF6FE,
|
||||
|
|
56
src/com/bytezone/diskbrowser/applefile/BasicFormatter.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.BasicPreferences;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public abstract class BasicFormatter implements ApplesoftConstants
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
static final String NEWLINE = "\n";
|
||||
|
||||
ApplesoftBasicProgram program;
|
||||
BasicPreferences basicPreferences;
|
||||
byte[] buffer;
|
||||
List<SourceLine> sourceLines;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public BasicFormatter (ApplesoftBasicProgram program, BasicPreferences basicPreferences)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.program = program;
|
||||
this.basicPreferences = basicPreferences;
|
||||
this.buffer = program.getBuffer ();
|
||||
this.sourceLines = program.getSourceLines ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public abstract void append (StringBuilder fullText);
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
int getLoadAddress ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return (buffer.length > 3) ? getShort (buffer, 0) - getFirstLineLength () : 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getFirstLineLength ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int linkField = getShort (buffer, 0);
|
||||
if (linkField == 0)
|
||||
return 2;
|
||||
|
||||
int ptr = 4; // skip link field and line number
|
||||
|
||||
while (ptr < buffer.length && buffer[ptr++] != 0)
|
||||
;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
}
|
|
@ -6,11 +6,6 @@ import com.bytezone.diskbrowser.gui.BasicPreferences;
|
|||
public abstract class BasicProgram extends AbstractFile
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
static final byte ASCII_QUOTE = 0x22;
|
||||
static final byte ASCII_COLON = 0x3A;
|
||||
static final byte ASCII_SEMI_COLON = 0x3B;
|
||||
static final byte ASCII_CARET = 0x5E;
|
||||
|
||||
static BasicPreferences basicPreferences; // set by MenuHandler
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -26,26 +21,4 @@ public abstract class BasicProgram extends AbstractFile
|
|||
{
|
||||
super (name, buffer);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isHighBitSet (byte value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return (value & 0x80) != 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isControlCharacter (byte value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int val = value & 0xFF;
|
||||
return val > 0 && val < 32;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isDigit (byte value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return value >= 48 && value <= 57;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class BasicProgramGS extends BasicProgram
|
||||
|
@ -134,7 +136,7 @@ public class BasicProgramGS extends BasicProgram
|
|||
ptr += labelLength;
|
||||
|
||||
int lineLength = buffer[ptr] & 0xFF;
|
||||
lineNumber = Utility.intValue (buffer[ptr + 1], buffer[ptr + 2]);
|
||||
lineNumber = Utility.getShort (buffer, ptr + 1);
|
||||
length = labelLength + lineLength;
|
||||
|
||||
if (lineNumber == 0)
|
||||
|
|
|
@ -8,6 +8,9 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
|
|||
public class BasicTextFile extends TextFile
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static String underline =
|
||||
"------------------------------------------" + "------------------------------------\n";
|
||||
private static String fullUnderline = "---------- ------- " + underline;
|
||||
private int recordLength; // prodos aux
|
||||
private List<TextBuffer> buffers; // only used if it is a Prodos text file
|
||||
private int eof;
|
||||
|
@ -27,6 +30,7 @@ public class BasicTextFile extends TextFile
|
|||
super (name, buffer);
|
||||
|
||||
this.eof = eof;
|
||||
|
||||
recordLength = auxType;
|
||||
prodosFile = true;
|
||||
}
|
||||
|
@ -39,6 +43,7 @@ public class BasicTextFile extends TextFile
|
|||
|
||||
this.buffers = buffers;
|
||||
this.eof = eof;
|
||||
|
||||
recordLength = auxType;
|
||||
prodosFile = true;
|
||||
}
|
||||
|
@ -48,18 +53,17 @@ public class BasicTextFile extends TextFile
|
|||
public String getHexDump ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (buffers == null)
|
||||
if (buffers == null || recordLength <= 1)
|
||||
return (super.getHexDump ());
|
||||
|
||||
StringBuilder text = new StringBuilder ();
|
||||
for (TextBuffer tb : buffers)
|
||||
{
|
||||
for (int i = 0, rec = 0; i < tb.buffer.length; i += tb.reclen, rec++)
|
||||
{
|
||||
text.append ("\nRecord #" + (tb.firstRecNo + rec) + "\n");
|
||||
text.append (HexFormatter.format (tb.buffer, i, tb.reclen) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
|
@ -86,15 +90,23 @@ public class BasicTextFile extends TextFile
|
|||
|
||||
// check whether file is spread over multiple buffers
|
||||
if (buffers != null)
|
||||
return treeFileText (text); // calls knownLength()
|
||||
return treeFileText (text); // usually calls knownLength()
|
||||
|
||||
// check whether the record length is known
|
||||
if (recordLength == 0)
|
||||
return unknownLength (text);
|
||||
if (recordLength <= 0)
|
||||
return unknownLength (text).toString ();
|
||||
|
||||
if (textPreferences.showTextOffsets && recordLength > 1)
|
||||
{
|
||||
text.append (" Offset Record# Text values\n");
|
||||
text.append (fullUnderline);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.append ("Text values\n");
|
||||
text.append (underline);
|
||||
}
|
||||
|
||||
text.append ("Offset Record# Text values\n");
|
||||
text.append (
|
||||
"------ ------- -------------------------------------------------------\n");
|
||||
return knownLength (text, 0).toString ();
|
||||
}
|
||||
|
||||
|
@ -102,13 +114,31 @@ public class BasicTextFile extends TextFile
|
|||
private String treeFileText (StringBuilder text)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
text.append (" Offset Record# Text values\n");
|
||||
text.append (
|
||||
"---------- ------- -------------------------------------------------------\n");
|
||||
for (TextBuffer tb : buffers)
|
||||
if (recordLength == 1) // stupid length
|
||||
{
|
||||
buffer = tb.buffer;
|
||||
text = knownLength (text, tb.firstRecNo);
|
||||
for (TextBuffer textBuffer : buffers)
|
||||
{
|
||||
buffer = textBuffer.buffer;
|
||||
text = unknownLength (text);
|
||||
}
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
if (textPreferences.showTextOffsets)
|
||||
{
|
||||
text.append (" Offset Record# Text values\n");
|
||||
text.append (fullUnderline);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.append ("Text values\n");
|
||||
text.append (underline);
|
||||
}
|
||||
|
||||
for (TextBuffer textBuffer : buffers)
|
||||
{
|
||||
buffer = textBuffer.buffer;
|
||||
text = knownLength (text, textBuffer.firstRecNo);
|
||||
}
|
||||
return text.toString ();
|
||||
}
|
||||
|
@ -126,7 +156,7 @@ public class BasicTextFile extends TextFile
|
|||
}
|
||||
|
||||
int len = buffer.length - ptr;
|
||||
int bytes = len < recordLength ? len : recordLength;
|
||||
int bytes = Math.min (len, recordLength);
|
||||
|
||||
while (buffer[ptr + bytes - 1] == 0)
|
||||
bytes--;
|
||||
|
@ -135,38 +165,43 @@ public class BasicTextFile extends TextFile
|
|||
bytes--;
|
||||
|
||||
String line = HexFormatter.getString (buffer, ptr, bytes);
|
||||
line = line.replaceAll ("\\n", "\n ");
|
||||
text.append (
|
||||
String.format ("%,10d %,8d %s%n", recNo * recordLength, recNo++, line));
|
||||
|
||||
if (textPreferences.showTextOffsets)
|
||||
{
|
||||
line = line.replaceAll ("\\n", "\n ");
|
||||
text.append (String.format ("%,10d %,8d %s%n", recNo * recordLength, recNo, line));
|
||||
}
|
||||
else
|
||||
text.append (String.format ("%s%n", line));
|
||||
|
||||
recNo++;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String unknownLength (StringBuilder text)
|
||||
private StringBuilder unknownLength (StringBuilder text)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int nulls = 0;
|
||||
int ptr = 0;
|
||||
int size = buffer.length;
|
||||
int lastVal = 0;
|
||||
// boolean showAllOffsets = true;
|
||||
|
||||
if (textPreferences.showTextOffsets)
|
||||
{
|
||||
text.append (" Offset Text values\n");
|
||||
text.append ("---------- -------------------------------------------------------"
|
||||
+ "-------------------\n");
|
||||
text.append ("---------- " + underline);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.append (" Text values\n");
|
||||
text.append ("-------------------------------------------------------"
|
||||
+ "-------------------------------\n");
|
||||
text.append (underline);
|
||||
}
|
||||
|
||||
if (buffer.length == 0)
|
||||
return text.toString ();
|
||||
return text;
|
||||
|
||||
if (buffer[0] != 0 && textPreferences.showTextOffsets)
|
||||
text.append (String.format ("%,10d ", ptr));
|
||||
|
@ -204,7 +239,7 @@ public class BasicTextFile extends TextFile
|
|||
else if (text.length () > 0 && text.charAt (text.length () - 1) == '\n')
|
||||
text.deleteCharAt (text.length () - 1);
|
||||
|
||||
return text.toString ();
|
||||
return text;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
229
src/com/bytezone/diskbrowser/applefile/CPMBasicFile.java
Normal file
|
@ -0,0 +1,229 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class CPMBasicFile extends BasicProgram
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
String[] tokens = { //
|
||||
"", "END", "FOR", "NEXT", "DATA", "INPUT", "DIM", "READ", // 0x80
|
||||
"LET", "GOTO", "RUN", "IF", "RESTORE", "GOSUB", "RETURN", "REM", // 0x88
|
||||
"STOP", "PRINT", "CLEAR", "LIST", "NEW", "ON", "DEF", "POKE", // 0x90
|
||||
"", "", "", "LPRINT", "LLIST", "WIDTH", "ELSE", "", // 0x98
|
||||
"", "SWAP", "ERASE", "", "ERROR", "RESUME", "DELETE", "", // 0xA0
|
||||
"RENUM", "DEFSTR", "DEFINT", "", "DEFDBL", "LINE", "", "WHILE", // 0xA8
|
||||
"WEND", "CALL", "WRITE", "COMMON", "CHAIN", // 0xB0
|
||||
"OPTION", "RANDOMIZE", "SYSTEM", // 0xB5
|
||||
"OPEN", "FIELD", "GET", "PUT", "CLOSE", "LOAD", "MERGE", "", // 0xB8
|
||||
"NAME", "KILL", "LSET", "RSET", "SAVE", "RESET", "TEXT", "HOME", // 0xC0
|
||||
"VTAB", "HTAB", "INVERSE", "NORMAL", "", "", "", "", // 0xC8
|
||||
"", "", "", "", "", "WAIT", "", "", // 0xD0
|
||||
"", "", "", "", "", "TO", "THEN", "TAB(", // 0xD8
|
||||
"STEP", "USR", "FN", "SPC(", "", "ERL", "ERR", "STRING$", // 0xE0
|
||||
"USING", "INSTR", "'", "VARPTR", "", "", "INKEY$", ">", // 0xE8
|
||||
"=", "<", "+", "-", "*", "/", "", "AND", // 0xF0
|
||||
"OR", "", "", "", "MOD", "/", "", "", // 0xF8
|
||||
};
|
||||
|
||||
String[] functions = { //
|
||||
"", "LEFT$", "RIGHT$", "MID$", "SGN", "INT", "ABS", "SQR", // 0x80
|
||||
"RND", "SIN", "LOG", "EXP", "COS", "TAN", "ATN", "FRE", // 0x88
|
||||
"POS", "LEN", "STR$", "VAL", "ASC", "CHR$", "PEEK", "SPACE$", // 0x90
|
||||
"OCT$", "HEX$", "LPOS", "CINT", "CSNG", "CDBL", "FIX", "", // 0x98
|
||||
"", "", "", "", "", "", "", "", // 0xA0
|
||||
"", "", "CVI", "CVS", "CVD", "", "EOF", "LOC", // 0xA8
|
||||
"", "MKI$", "MKS$", "MKD$", // 0xB0
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public CPMBasicFile (String name, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (name, buffer);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String getText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
if (basicPreferences.showHeader)
|
||||
text.append ("Name : " + name + "\n\n");
|
||||
|
||||
int ptr = 5;
|
||||
while (buffer[ptr] != 0)
|
||||
ptr++;
|
||||
|
||||
if (showDebugText)
|
||||
return debugText ();
|
||||
|
||||
ptr = 1;
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
int nextAddress = getShort (buffer, ptr);
|
||||
|
||||
if (nextAddress == 0)
|
||||
break;
|
||||
|
||||
int lineNumber = getShort (buffer, ptr + 2);
|
||||
|
||||
text.append (String.format (" %d ", lineNumber));
|
||||
ptr += 4;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int val = buffer[ptr++] & 0xFF;
|
||||
|
||||
if (val == 0)
|
||||
break;
|
||||
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
if (val == 0xFF)
|
||||
{
|
||||
val = buffer[ptr++] & 0xFF;
|
||||
String token = functions[val & 0x7F];
|
||||
if (token.length () == 0)
|
||||
token = String.format ("<FF %02X>", val);
|
||||
text.append (token);
|
||||
}
|
||||
else
|
||||
{
|
||||
String token = tokens[val & 0x7F];
|
||||
if (token.length () == 0)
|
||||
token = String.format ("<%02X>", val);
|
||||
text.append (token);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (val >= 0x20 && val <= 0x7E) // printable
|
||||
{
|
||||
// check for stupid apostrophe comment
|
||||
if (val == 0x3A && ptr + 1 < buffer.length && buffer[ptr] == (byte) 0x8F
|
||||
&& buffer[ptr + 1] == (byte) 0xEA)
|
||||
{
|
||||
text.append ("'");
|
||||
ptr += 2;
|
||||
}
|
||||
else if (val == 0x3A && ptr < buffer.length && buffer[ptr] == (byte) 0x9E)
|
||||
{
|
||||
// ignore colon before ELSE
|
||||
}
|
||||
else
|
||||
text.append (String.format ("%s", (char) val));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (val >= 0x11 && val <= 0x1A) // inline numbers
|
||||
{
|
||||
text.append (val - 0x11);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case 0x07:
|
||||
text.append ("<BELL>");
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
text.append (" ");
|
||||
break;
|
||||
|
||||
case 0x0A:
|
||||
text.append ("\n ");
|
||||
break;
|
||||
|
||||
case 0x0C:
|
||||
text.append ("&H" + String.format ("%X", Utility.getShort (buffer, ptr)));
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case 0x0E: // same as 0x1C ??
|
||||
text.append (Utility.getShort (buffer, ptr));
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case 0x0F:
|
||||
text.append (buffer[ptr++] & 0xFF);
|
||||
break;
|
||||
|
||||
case 0x1C: // same as 0x0E ??
|
||||
text.append (Utility.getShort (buffer, ptr));
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case 0x1D:
|
||||
String d4 = Utility.floatValueMS4 (buffer, ptr) + "";
|
||||
if (d4.endsWith (".0"))
|
||||
d4 = d4.substring (0, d4.length () - 2);
|
||||
text.append (d4 + "!");
|
||||
ptr += 4;
|
||||
break;
|
||||
|
||||
case 0x1F:
|
||||
String d8 = Utility.floatValueMS8 (buffer, ptr) + "";
|
||||
if (d8.endsWith (".0"))
|
||||
d8 = d8.substring (0, d8.length () - 2);
|
||||
text.append (d8 + "#");
|
||||
ptr += 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
text.append (String.format ("<%02X>", val));
|
||||
}
|
||||
}
|
||||
|
||||
text.append ("\n");
|
||||
}
|
||||
|
||||
return Utility.rtrim (text);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String debugText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
int ptr = 1;
|
||||
int lastPtr;
|
||||
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
int nextAddress = getShort (buffer, ptr);
|
||||
if (nextAddress == 0)
|
||||
break;
|
||||
|
||||
int lineNumber = getShort (buffer, ptr + 2);
|
||||
lastPtr = ptr;
|
||||
|
||||
ptr += 4;
|
||||
|
||||
int val;
|
||||
while ((val = buffer[ptr++]) != 0)
|
||||
{
|
||||
ptr += switch (val)
|
||||
{
|
||||
case 0x0C, 0x0E, 0x1C -> 2; // 2 byte numeric
|
||||
case 0x1D -> 4; // 4 byte single precision
|
||||
case 0x1F -> 8; // 8 byte double precision
|
||||
case 0x0F, 0xFF -> 1; // 1 byte numeric, function table entry
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
text.append (String.format (" %d %s%n", lineNumber,
|
||||
HexFormatter.getHexString (buffer, lastPtr + 4, ptr - lastPtr - 4)));
|
||||
}
|
||||
|
||||
return Utility.rtrim (text);
|
||||
}
|
||||
}
|
|
@ -26,7 +26,10 @@ public class CPMTextFile extends TextFile
|
|||
{
|
||||
String line = getLine (ptr);
|
||||
text.append (line + "\n");
|
||||
ptr += line.length () + 2;
|
||||
ptr += line.length () + 1;
|
||||
if (ptr < buffer.length && buffer[ptr - 1] == 0x0D && buffer[ptr] == 0x0A)
|
||||
++ptr;
|
||||
|
||||
while (ptr < buffer.length && buffer[ptr] == 0)
|
||||
++ptr;
|
||||
}
|
||||
|
@ -43,8 +46,8 @@ public class CPMTextFile extends TextFile
|
|||
{
|
||||
StringBuilder line = new StringBuilder ();
|
||||
|
||||
int max = buffer.length - 1;
|
||||
while (ptr < max && buffer[ptr] != 0x0D && buffer[ptr + 1] != 0x0A)
|
||||
// int max = buffer.length - 1;
|
||||
while (ptr < buffer.length && buffer[ptr] != 0x0D && buffer[ptr] != 0x0A)
|
||||
line.append ((char) (buffer[ptr++] & 0x7F));
|
||||
|
||||
return line.toString ();
|
||||
|
|
103
src/com/bytezone/diskbrowser/applefile/DebugBasicFormatter.java
Normal file
|
@ -0,0 +1,103 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COLON;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isDigit;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isLetter;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.BasicPreferences;
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class DebugBasicFormatter extends BasicFormatter
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
int endPtr;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public DebugBasicFormatter (ApplesoftBasicProgram program,
|
||||
BasicPreferences basicPreferences)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (program, basicPreferences);
|
||||
|
||||
endPtr = program.getEndPtr ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void append (StringBuilder text)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int loadAddress = getLoadAddress ();
|
||||
|
||||
for (SourceLine sourceLine : sourceLines)
|
||||
{
|
||||
text.append (String.format ("%5d %s%n", sourceLine.lineNumber,
|
||||
HexFormatter.formatNoHeader (buffer, sourceLine.linePtr, 4,
|
||||
loadAddress + sourceLine.linePtr)));
|
||||
for (SubLine subline : sourceLine.sublines)
|
||||
{
|
||||
String token = getDisplayToken (buffer[subline.startPtr]);
|
||||
String formattedHex = HexFormatter.formatNoHeader (buffer, subline.startPtr,
|
||||
subline.length, loadAddress + subline.startPtr);
|
||||
|
||||
for (String bytes : formattedHex.split (NEWLINE))
|
||||
{
|
||||
text.append (String.format (" %-8s %s%n", token, bytes));
|
||||
token = "";
|
||||
}
|
||||
}
|
||||
text.append (NEWLINE);
|
||||
}
|
||||
|
||||
// check for assembler routines after the basic code
|
||||
if (endPtr < buffer.length)
|
||||
{
|
||||
int length = buffer.length - endPtr;
|
||||
int ptr = endPtr;
|
||||
|
||||
if (length >= 2)
|
||||
{
|
||||
text.append (" ");
|
||||
text.append (HexFormatter.formatNoHeader (buffer, endPtr, 2, loadAddress + ptr));
|
||||
text.append ("\n\n");
|
||||
ptr += 2;
|
||||
length -= 2;
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
// show the extra bytes as a hex dump
|
||||
String formattedHex = HexFormatter.formatNoHeader (buffer, ptr,
|
||||
buffer.length - ptr, loadAddress + ptr);
|
||||
for (String bytes : formattedHex.split (NEWLINE))
|
||||
text.append (String.format (" %s%n", bytes));
|
||||
}
|
||||
|
||||
if (length > 1)
|
||||
{
|
||||
// show the extra bytes as a disassembly
|
||||
byte[] extraBuffer = new byte[length];
|
||||
System.arraycopy (buffer, ptr, extraBuffer, 0, extraBuffer.length);
|
||||
AssemblerProgram assemblerProgram =
|
||||
new AssemblerProgram ("extra", extraBuffer, loadAddress + ptr);
|
||||
text.append ("\n");
|
||||
text.append (assemblerProgram.getText ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getDisplayToken (byte b)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (isHighBitSet (b))
|
||||
return ApplesoftConstants.tokens[b & 0x7F];
|
||||
|
||||
if (isDigit (b) || isLetter (b) || b == ASCII_COLON || b == 0)
|
||||
return "";
|
||||
|
||||
return "*******";
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ public class DefaultAppleFile extends AbstractFile
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (name, buffer);
|
||||
|
||||
this.text = "Name : " + name + "\n\n" + text;
|
||||
}
|
||||
|
||||
|
@ -35,8 +36,10 @@ public class DefaultAppleFile extends AbstractFile
|
|||
{
|
||||
if (text != null)
|
||||
return text;
|
||||
|
||||
if (buffer == null)
|
||||
return "Invalid file : " + name;
|
||||
|
||||
return super.getText ();
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@ public class DosMasterFile extends AbstractFile
|
|||
public static boolean isDos33 (ProdosDisk parentDisk, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// System.out.println (HexFormatter.format (buffer, 0x38, 0x30, 0x38));
|
||||
System.out.printf ("%nHighest Block: %04X (%<,d)%n",
|
||||
parentDisk.getDisk ().getTotalBlocks () - 1);
|
||||
|
||||
|
@ -45,22 +44,22 @@ public class DosMasterFile extends AbstractFile
|
|||
System.out.print ("\nFirst Block : ");
|
||||
for (int i = 0; i < 16; i += 2)
|
||||
{
|
||||
System.out.printf ("%04X ", Utility.unsignedShort (buffer, 0x40 + i));
|
||||
System.out.printf ("%04X ", Utility.getShort (buffer, 0x40 + i));
|
||||
if (i % 4 == 2)
|
||||
System.out.print (": ");
|
||||
}
|
||||
|
||||
System.out.print ("\nLast Block : ");
|
||||
for (int i = 0; i < 8; i += 2)
|
||||
System.out.printf ("%04X : ", Utility.unsignedShort (buffer, 0x50 + i));
|
||||
System.out.printf ("%04X : ", Utility.getShort (buffer, 0x50 + i));
|
||||
|
||||
System.out.print ("\nImage Size : ");
|
||||
for (int i = 0; i < 8; i += 2)
|
||||
System.out.printf ("%04X : ", Utility.unsignedShort (buffer, 0x58 + i));
|
||||
System.out.printf ("%04X : ", Utility.getShort (buffer, 0x58 + i));
|
||||
|
||||
System.out.print ("\nAddress : ");
|
||||
for (int i = 0; i < 8; i += 2)
|
||||
System.out.printf ("%04X : ", Utility.unsignedShort (buffer, 0x60 + i));
|
||||
System.out.printf ("%04X : ", Utility.getShort (buffer, 0x60 + i));
|
||||
|
||||
System.out.println ();
|
||||
System.out.println ();
|
||||
|
@ -76,12 +75,12 @@ public class DosMasterFile extends AbstractFile
|
|||
|
||||
int slot = (slotDrive & 0x70) >>> 4;
|
||||
int drive = ((slotDrive & 0x80) >>> 7) + 1;
|
||||
int firstBlock = Utility.unsignedShort (buffer, 0x40 + i * 2); // of first volume
|
||||
int firstBlock = Utility.getShort (buffer, 0x40 + i * 2); // of first volume
|
||||
|
||||
int skip = i / 2 * 2; // 0, 0, 2, 2, 4, 4, 6, 6 - same for both drives
|
||||
|
||||
int lastBlock = Utility.unsignedShort (buffer, 0x50 + skip); // of last volume
|
||||
int volSize = Utility.unsignedShort (buffer, 0x58 + skip);
|
||||
int lastBlock = Utility.getShort (buffer, 0x50 + skip); // of last volume
|
||||
int volSize = Utility.getShort (buffer, 0x58 + skip);
|
||||
|
||||
int originalFirstBlock = firstBlock;
|
||||
if (firstBlock > lastBlock) // WTF?
|
||||
|
@ -106,9 +105,6 @@ public class DosMasterFile extends AbstractFile
|
|||
daList.add (disk.getDiskAddress (block));
|
||||
|
||||
byte[] diskBuffer = disk.readBlocks (daList);
|
||||
// System.out.println (HexFormatter.format (diskBuffer));
|
||||
// System.out.printf ("Buffer: %,d%n", diskBuffer.length);
|
||||
// System.out.printf ("Blocks: %,d x 2 = %,d%n", daList.size (), daList.size () * 2);
|
||||
|
||||
if (false)
|
||||
createDisk (String.format ("%sVol%03d.dsk", base, volNo), diskBuffer);
|
||||
|
@ -180,9 +176,9 @@ public class DosMasterFile extends AbstractFile
|
|||
text.append (String.format ("Slot %d, Drive %d has", s / 16, dr + 1));
|
||||
|
||||
int ptr = v0 + 2 * d0 + 2 * dr;
|
||||
int st = Utility.unsignedShort (buffer, ptr); // start block of first volume
|
||||
int v = Utility.unsignedShort (buffer, size + d0); // end block of last volume
|
||||
int sz = Utility.unsignedShort (buffer, vsiz + d0); // blocks per volume
|
||||
int st = Utility.getShort (buffer, ptr); // start block of first volume
|
||||
int v = Utility.getShort (buffer, size + d0); // end block of last volume
|
||||
int sz = Utility.getShort (buffer, vsiz + d0); // blocks per volume
|
||||
|
||||
if (st > v)
|
||||
st -= 16 * 4096;
|
||||
|
|
|
@ -84,7 +84,7 @@ public class ExoBuffer
|
|||
return false;
|
||||
}
|
||||
|
||||
int address = Utility.unsignedShort (buffer, buffer.length - 2);
|
||||
int address = Utility.getShort (buffer, buffer.length - 2);
|
||||
|
||||
if (address != 0x6000 && address != 0x8000 && address != 0xA000)
|
||||
{
|
||||
|
|
|
@ -28,11 +28,11 @@ public class FileTypeDescriptorTable extends AbstractFile
|
|||
|
||||
versionMajor = buffer[0] & 0xFF;
|
||||
versionMinor = buffer[1] & 0xFF;
|
||||
flags = Utility.unsignedShort (buffer, 2);
|
||||
numEntries = Utility.unsignedShort (buffer, 4);
|
||||
spareWord = Utility.unsignedShort (buffer, 6);
|
||||
indexRecordSize = Utility.unsignedShort (buffer, 8);
|
||||
offsetToIdx = Utility.unsignedShort (buffer, 10);
|
||||
flags = Utility.getShort (buffer, 2);
|
||||
numEntries = Utility.getShort (buffer, 4);
|
||||
spareWord = Utility.getShort (buffer, 6);
|
||||
indexRecordSize = Utility.getShort (buffer, 8);
|
||||
offsetToIdx = Utility.getShort (buffer, 10);
|
||||
|
||||
int ptr = offsetToIdx;
|
||||
for (int i = 0; i < numEntries; i++)
|
||||
|
@ -80,10 +80,10 @@ public class FileTypeDescriptorTable extends AbstractFile
|
|||
|
||||
public IndexRecord (byte[] buffer, int offset)
|
||||
{
|
||||
fileType = Utility.unsignedShort (buffer, offset);
|
||||
auxType = Utility.unsignedLong (buffer, offset + 2);
|
||||
flags = Utility.unsignedShort (buffer, offset + 6);
|
||||
this.offset = Utility.unsignedShort (buffer, offset + 8);
|
||||
fileType = Utility.getShort (buffer, offset);
|
||||
auxType = Utility.getLong (buffer, offset + 2);
|
||||
flags = Utility.getShort (buffer, offset + 6);
|
||||
this.offset = Utility.getShort (buffer, offset + 8);
|
||||
string = HexFormatter.getPascalString (buffer, this.offset);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ public class FinderData extends AbstractFile
|
|||
text.append (String.format ("%02X%n", buffer[ptr++]));
|
||||
}
|
||||
}
|
||||
else
|
||||
text.append (String.format ("Unknown finder data version: %d%n", version));
|
||||
|
||||
if (text.length () > 0)
|
||||
text.deleteCharAt (text.length () - 1);
|
||||
|
|
|
@ -32,9 +32,11 @@ public class FontFile extends CharacterList
|
|||
{
|
||||
if (buffer.length % 8 != 0)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (buffer[i] != 0 && buffer[i] != 0x7F)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
26
src/com/bytezone/diskbrowser/applefile/HeaderFormatter.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.BasicPreferences;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class HeaderFormatter extends BasicFormatter
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public HeaderFormatter (ApplesoftBasicProgram program,
|
||||
BasicPreferences basicPreferences)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (program, basicPreferences);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void append (StringBuilder fullText)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
fullText.append ("Name : " + program.name + "\n");
|
||||
fullText.append (String.format ("Length : $%04X (%<,d)%n", buffer.length));
|
||||
fullText.append (String.format ("Load at : $%04X (%<,d)%n%n", getLoadAddress ()));
|
||||
}
|
||||
}
|
|
@ -18,98 +18,101 @@ public abstract class HiResImage extends AbstractFile
|
|||
{
|
||||
static final String[] auxTypes =
|
||||
{ "Paintworks Packed SHR Image", "Packed Super Hi-Res Image",
|
||||
"Super Hi-Res Image (Apple Preferred Format)", "Packed QuickDraw II PICT File",
|
||||
"Packed Super Hi-Res 3200 color image" };
|
||||
"Super Hi-Res Image (Apple Preferred Format)", "Packed QuickDraw II PICT File",
|
||||
"Packed Super Hi-Res 3200 color image", "DreamGraphix" };
|
||||
|
||||
static final int COLOR_TABLE_SIZE = 32;
|
||||
static final int COLOR_TABLE_OFFSET_AUX_0 = 32_256;
|
||||
static final int COLOR_TABLE_OFFSET_AUX_2 = 32_000;
|
||||
public static final int FADDEN_AUX = 0x8066;
|
||||
|
||||
private byte[] fourBuf = new byte[4];
|
||||
private ColorTable defaultColorTable320 = new ColorTable (0, 0x00);
|
||||
private ColorTable defaultColorTable640 = new ColorTable (0, 0x80);
|
||||
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// File Type Aux Name Description
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// $06 BIN isGif() OriginalHiResImage
|
||||
// $06 BIN isPng() OriginalHiResImage
|
||||
// $06 BIN .BMP isBmp() OriginalHiResImage
|
||||
// $06 BIN .AUX DoubleHiResImage
|
||||
// $06 BIN .PAC DoubleHiResImage
|
||||
// $06 BIN .A2FC DoubleHiResImage
|
||||
// $06 BIN $2000 eof $4000 DoubleHiResImage
|
||||
// $06 BIN $1FFF eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
|
||||
// $06 BIN $2000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
|
||||
// $06 BIN $4000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage (?)
|
||||
// $06 BIN $4000 eof $4000 DoubleHiResImage (?)
|
||||
// $06 BIN .3200 SHRPictureFile2
|
||||
// $06 BIN .3201 SHRPictureFile2 packed
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// $08 FOT <$4000 Apple II Graphics File OriginalHiResImage
|
||||
// $08 FOT $4000 Packed Hi-Res file ???
|
||||
// $08 FOT $4001 Packed Double Hi-Res file ???
|
||||
// $08 FOT $8066 Fadden Hi-res FaddenHiResImage
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// * $C0 PNT $0000 Paintworks Packed Super Hi-Res SHRPictureFile2
|
||||
// * $C0 PNT $0001 Packed IIGS Super Hi-Res Image SHRPictureFile2
|
||||
// * $C0 PNT $0002 IIGS Super Hi-Res Picture File (APF) SHRPictureFile1
|
||||
// $C0 PNT $0003 Packed IIGS QuickDraw II PICT File SHRPictureFile2 *
|
||||
// * $C0 PNT $0004 Packed Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3201
|
||||
// $C0 PNT $1000
|
||||
// $C0 PNT $8000 Drawplus? Paintworks Gold?
|
||||
// $C0 PNT $8001 GTv background picture
|
||||
// $C0 PNT $8005 DreamGraphix document
|
||||
// $C0 PNT $8006 GIF
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// * $C1 PIC $0000 IIGS Super Hi-Res Image SHRPictureFile2
|
||||
// $C1 PIC $0001 IIGS QuickDraw II PICT File SHRPictureFile2 *
|
||||
// * $C1 PIC $0002 Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3200
|
||||
// $C1 PIC $2000 = $C1/0000
|
||||
// $C1 PIC $4100 = $C1/0000
|
||||
// $C1 PIC $4950 = $C1/0000
|
||||
// $C1 PIC $8001 Allison raw image
|
||||
// $C1 PIC $8002 Thunderscan
|
||||
// $C1 PIC $8003 DreamGraphix
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// $C2 ANI Paintworks animation
|
||||
// $C3 PAL Paintworks palette
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// File Type Aux Name Description
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// $06 BIN isGif() OriginalHiResImage
|
||||
// $06 BIN isPng() OriginalHiResImage
|
||||
// $06 BIN .BMP isBmp() OriginalHiResImage
|
||||
// $06 BIN .AUX DoubleHiResImage
|
||||
// $06 BIN .PAC DoubleHiResImage
|
||||
// $06 BIN .A2FC DoubleHiResImage
|
||||
// $06 BIN $2000 eof $4000 DoubleHiResImage
|
||||
// $06 BIN $1FFF eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
|
||||
// $06 BIN $2000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
|
||||
// $06 BIN $4000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage (?)
|
||||
// $06 BIN $4000 eof $4000 DoubleHiResImage (?)
|
||||
// $06 BIN .3200 SHRPictureFile2
|
||||
// $06 BIN .3201 SHRPictureFile2 packed
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// $08 FOT <$4000 Apple II Graphics File OriginalHiResImage
|
||||
// $08 FOT $4000 Packed Hi-Res file ???
|
||||
// $08 FOT $4001 Packed Double Hi-Res file ???
|
||||
// $08 FOT $8066 Fadden Hi-res FaddenHiResImage
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// * $C0 PNT $0000 Paintworks Packed Super Hi-Res SHRPictureFile2
|
||||
// * $C0 PNT $0001 Packed IIGS Super Hi-Res Image SHRPictureFile2
|
||||
// * $C0 PNT $0002 IIGS Super Hi-Res Picture File (APF) SHRPictureFile1
|
||||
// $C0 PNT $0003 Packed IIGS QuickDraw II PICT File SHRPictureFile2 *
|
||||
// * $C0 PNT $0004 Packed Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3201
|
||||
// $C0 PNT $1000
|
||||
// $C0 PNT $8000 Drawplus? Paintworks Gold?
|
||||
// $C0 PNT $8001 GTv background picture
|
||||
// $C0 PNT $8005 DreamGraphix document SHRPictureFile2
|
||||
// $C0 PNT $8006 GIF
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// * $C1 PIC $0000 IIGS Super Hi-Res Image SHRPictureFile2
|
||||
// $C1 PIC $0001 IIGS QuickDraw II PICT File SHRPictureFile2 *
|
||||
// * $C1 PIC $0002 Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3200
|
||||
// $C1 PIC $2000 = $C1/0000
|
||||
// $C1 PIC $4100 = $C1/0000
|
||||
// $C1 PIC $4950 = $C1/0000
|
||||
// $C1 PIC $8001 Allison raw image
|
||||
// $C1 PIC $8002 Thunderscan
|
||||
// $C1 PIC $8003 DreamGraphix
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
// $C2 ANI Paintworks animation
|
||||
// $C3 PAL Paintworks palette
|
||||
// ---- ---- ------ -------------------------------------- ------------------------
|
||||
|
||||
// packed unpacked
|
||||
// $06.3200 1
|
||||
// $06.3201 .
|
||||
// $08 0000 .
|
||||
// $08 4000 .
|
||||
// $08 4001 .
|
||||
// $08 8066 3
|
||||
// $C0 0000 1
|
||||
// $C0 0001 $C1 0000 2 1
|
||||
// $C0 0002 1,5
|
||||
// $C0 0003 $C1 0001 . .
|
||||
// $C0 0004 $C1 0002 . 1
|
||||
// $C0 1000 .
|
||||
// $C0 8000 .
|
||||
// $C0 8001 .
|
||||
// $C0 8005 .
|
||||
// $C0 8006 .
|
||||
// $C1 0042 4
|
||||
// $C1 0043 4
|
||||
// $C1 2000 .
|
||||
// $C1 4100 1
|
||||
// $C1 4950 .
|
||||
// $C1 8001 .
|
||||
// $C1 8002 .
|
||||
// $C1 8003 .
|
||||
// packed unpacked
|
||||
// $06.3200 1
|
||||
// $06.3201 .
|
||||
// $08 0000 .
|
||||
// $08 4000 .
|
||||
// $08 4001 .
|
||||
// $08 8066 3
|
||||
// $C0 0000 1
|
||||
// $C0 0001 $C1 0000 2 1
|
||||
// $C0 0002 1,5
|
||||
// $C0 0003 $C1 0001 . .
|
||||
// $C0 0004 $C1 0002 . 1
|
||||
// $C0 1000 .
|
||||
// $C0 8000 .
|
||||
// $C0 8001 .
|
||||
// $C0 8005 6
|
||||
// $C0 8006 .
|
||||
// $C1 0042 4
|
||||
// $C1 0043 4
|
||||
// $C1 2000 .
|
||||
// $C1 4100 1
|
||||
// $C1 4950 .
|
||||
// $C1 8001 .
|
||||
// $C1 8002 .
|
||||
// $C1 8003 .
|
||||
|
||||
// 1 Graphics & Animation.2mg
|
||||
// 2 0603 Katie's Farm - Disk 2.po
|
||||
// 3 CompressedSlides.do
|
||||
// 4 System Addons.hdv
|
||||
// 5 gfx.po
|
||||
//
|
||||
// 6 Dream Grafix v1.02.po
|
||||
|
||||
// see also - https://docs.google.com/spreadsheets/d
|
||||
// /1rKR6A_bVniSCtIP_rrv8QLWJdj4h6jEU1jJj0AebWwg/edit#gid=0
|
||||
// . /1rKR6A_bVniSCtIP_rrv8QLWJdj4h6jEU1jJj0AebWwg/edit#gid=0
|
||||
// also - http://lukazi.blogspot.com/2017/03/double-high-resolution-graphics-dhgr.html
|
||||
|
||||
static PaletteFactory paletteFactory = new PaletteFactory ();
|
||||
|
||||
|
@ -297,27 +300,20 @@ public abstract class HiResImage extends AbstractFile
|
|||
break;
|
||||
|
||||
case ProdosConstants.FILE_TYPE_PNT: // 0xC0
|
||||
auxText = auxType > 4 ? "Unknown aux: " + auxType : auxTypes[auxType];
|
||||
if (auxType == 0x8005)
|
||||
auxText = auxTypes[5];
|
||||
else
|
||||
auxText = auxType > 4 ? "Unknown aux: " + auxType : auxTypes[auxType];
|
||||
break;
|
||||
|
||||
case ProdosConstants.FILE_TYPE_PIC: // 0xC1
|
||||
switch (auxType)
|
||||
auxText = switch (auxType)
|
||||
{
|
||||
case 0:
|
||||
case 0x2000:
|
||||
case 0x0042:
|
||||
case 0x0043:
|
||||
auxText = "Super Hi-res Screen Image";
|
||||
break;
|
||||
case 1:
|
||||
auxText = "QuickDraw PICT File";
|
||||
break;
|
||||
case 2:
|
||||
auxText = "Super Hi-Res 3200 color image";
|
||||
break;
|
||||
default:
|
||||
auxText = "Unknown aux: " + auxType;
|
||||
}
|
||||
case 0, 0x2000, 0x0042, 0x0043 -> "Super Hi-res Screen Image";
|
||||
case 1 -> "QuickDraw PICT File";
|
||||
case 2 -> "Super Hi-Res 3200 color image";
|
||||
default -> "Unknown aux: " + auxType;
|
||||
};
|
||||
}
|
||||
|
||||
if (!auxText.isEmpty ())
|
||||
|
@ -505,7 +501,7 @@ public abstract class HiResImage extends AbstractFile
|
|||
int calculateBufferSize (byte[] buffer, int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// int ptr = 0;
|
||||
// int ptr = 0;
|
||||
int size = 0;
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
|
@ -630,15 +626,15 @@ public abstract class HiResImage extends AbstractFile
|
|||
return false;
|
||||
|
||||
String text = new String (buffer, 0, 2);
|
||||
int size = Utility.unsignedLong (buffer, 2);
|
||||
int size = Utility.getLong (buffer, 2);
|
||||
|
||||
if (false)
|
||||
{
|
||||
int empty = Utility.unsignedLong (buffer, 6);
|
||||
int offset = Utility.unsignedLong (buffer, 10);
|
||||
int header = Utility.unsignedLong (buffer, 14);
|
||||
int width = Utility.unsignedLong (buffer, 18);
|
||||
int height = Utility.unsignedLong (buffer, 22);
|
||||
int empty = Utility.getLong (buffer, 6);
|
||||
int offset = Utility.getLong (buffer, 10);
|
||||
int header = Utility.getLong (buffer, 14);
|
||||
int width = Utility.getLong (buffer, 18);
|
||||
int height = Utility.getLong (buffer, 22);
|
||||
|
||||
System.out.println (buffer.length);
|
||||
System.out.println (size);
|
||||
|
@ -810,7 +806,7 @@ public abstract class HiResImage extends AbstractFile
|
|||
public ColorEntry (byte[] data, int offset)
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
value = Utility.unsignedShort (data, offset);
|
||||
value = Utility.getShort (data, offset);
|
||||
|
||||
int red = ((value >> 8) & 0x0f) * 17;
|
||||
int green = ((value >> 4) & 0x0f) * 17;
|
||||
|
@ -839,8 +835,8 @@ public abstract class HiResImage extends AbstractFile
|
|||
public DirEntry (byte[] data, int offset)
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
numBytes = Utility.unsignedShort (data, offset);
|
||||
mode = Utility.unsignedShort (data, offset + 2);
|
||||
numBytes = Utility.getShort (data, offset);
|
||||
mode = Utility.getShort (data, offset + 2);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------//
|
||||
|
|
|
@ -29,16 +29,16 @@ public class IconFile extends AbstractFile implements ProdosConstants
|
|||
{
|
||||
super (name, buffer);
|
||||
|
||||
iBlkNext = Utility.unsignedLong (buffer, 0);
|
||||
iBlkID = Utility.unsignedShort (buffer, 4);
|
||||
iBlkPath = Utility.unsignedLong (buffer, 6);
|
||||
iBlkNext = Utility.getLong (buffer, 0);
|
||||
iBlkID = Utility.getShort (buffer, 4);
|
||||
iBlkPath = Utility.getLong (buffer, 6);
|
||||
iBlkName = HexFormatter.getHexString (buffer, 10, 16);
|
||||
|
||||
int ptr = 26;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int dataLen = Utility.unsignedShort (buffer, ptr);
|
||||
int dataLen = Utility.getShort (buffer, ptr);
|
||||
if (dataLen == 0 || (dataLen + ptr) > buffer.length)
|
||||
break;
|
||||
|
||||
|
@ -131,7 +131,7 @@ public class IconFile extends AbstractFile implements ProdosConstants
|
|||
public Icon (byte[] fullBuffer, int ptr)
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
iDataLen = Utility.unsignedShort (fullBuffer, ptr);
|
||||
iDataLen = Utility.getShort (fullBuffer, ptr);
|
||||
|
||||
buffer = new byte[iDataLen];
|
||||
System.arraycopy (fullBuffer, ptr, buffer, 0, buffer.length);
|
||||
|
@ -142,8 +142,8 @@ public class IconFile extends AbstractFile implements ProdosConstants
|
|||
len = buffer[66] & 0xFF;
|
||||
dataName = new String (buffer, 67, len);
|
||||
|
||||
iDataType = Utility.unsignedShort (buffer, 82);
|
||||
iDataAux = Utility.unsignedShort (buffer, 84);
|
||||
iDataType = Utility.getShort (buffer, 82);
|
||||
iDataAux = Utility.getShort (buffer, 84);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
|
@ -200,10 +200,10 @@ public class IconFile extends AbstractFile implements ProdosConstants
|
|||
public Image (byte[] buffer, int ptr) throws InvalidImageException
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
iconType = Utility.unsignedShort (buffer, ptr);
|
||||
iconSize = Utility.unsignedShort (buffer, ptr + 2);
|
||||
iconHeight = Utility.unsignedShort (buffer, ptr + 4);
|
||||
iconWidth = Utility.unsignedShort (buffer, ptr + 6);
|
||||
iconType = Utility.getShort (buffer, ptr);
|
||||
iconSize = Utility.getShort (buffer, ptr + 2);
|
||||
iconHeight = Utility.getShort (buffer, ptr + 4);
|
||||
iconWidth = Utility.getShort (buffer, ptr + 6);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
|
|
53
src/com/bytezone/diskbrowser/applefile/MagicWindowText.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class MagicWindowText extends AbstractFile
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public MagicWindowText (String name, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (name, buffer);
|
||||
}
|
||||
|
||||
// this was copied from SimpleText, should probably combine them
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String getText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append ("Name : " + name + "\n");
|
||||
text.append (String.format ("End of file : %,8d%n%n", buffer.length));
|
||||
|
||||
int ptr = 0x100;
|
||||
while (ptr < buffer.length && buffer[ptr] != 0x00)
|
||||
{
|
||||
String line = getLine (ptr);
|
||||
text.append (line + "\n");
|
||||
ptr += line.length () + 1;
|
||||
if (ptr < buffer.length && buffer[ptr] == 0x0A)
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getLine (int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder line = new StringBuilder ();
|
||||
|
||||
// added check for 0x00 eol 17/01/17
|
||||
while (ptr < buffer.length && buffer[ptr] != (byte) 0x8D && buffer[ptr] != 0x00)
|
||||
{
|
||||
line.append ((char) (buffer[ptr++] & 0x7F));
|
||||
}
|
||||
|
||||
return line.toString ();
|
||||
}
|
||||
}
|
|
@ -70,6 +70,8 @@ public class OriginalHiResImage extends HiResImage
|
|||
for (int ptr = base; ptr < max; ptr++)
|
||||
{
|
||||
int value = buffer[ptr] & 0x7F;
|
||||
if ((buffer[ptr] & 0x80) != 0)
|
||||
System.out.printf ("bit shift pixel found in %s%n", name);
|
||||
for (int px = 0; px < 7; px++)
|
||||
{
|
||||
int val = (value >> px) & 0x01;
|
||||
|
|
|
@ -22,13 +22,13 @@ public class PascalArea extends AbstractFile
|
|||
{
|
||||
super (name, buffer);
|
||||
|
||||
size = Utility.unsignedShort (buffer, 0);
|
||||
volumes = Utility.unsignedShort (buffer, 2);
|
||||
size = Utility.getShort (buffer, 0);
|
||||
volumes = Utility.getShort (buffer, 2);
|
||||
ppmName = HexFormatter.getPascalString (buffer, 4);
|
||||
start = Utility.unsignedShort (buffer, 8);
|
||||
length = Utility.unsignedShort (buffer, 11);
|
||||
start = Utility.getShort (buffer, 8);
|
||||
length = Utility.getShort (buffer, 11);
|
||||
defaultUnit = buffer[13] & 0xFF;
|
||||
oldDriver = Utility.unsignedShort (buffer, 14);
|
||||
oldDriver = Utility.getShort (buffer, 14);
|
||||
// writeProtected = buffer[12] != 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public class PascalCode extends AbstractFile
|
|||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
String codeName = HexFormatter.getString (buffer, 0x40 + i * 8, 8).trim ();
|
||||
int size = Utility.intValue (buffer[i * 4 + 2], buffer[i * 4 + 3]);
|
||||
int size = Utility.getShort (buffer, i * 4 + 2);
|
||||
if (codeName.length () == 0 && size > 0)
|
||||
codeName = "<NULL" + ++nonameCounter + ">";
|
||||
if (size > 0)
|
||||
|
|
|
@ -79,10 +79,8 @@ public class PascalCodeStatement implements PascalConstants
|
|||
int min = ptr + padding + 7;
|
||||
int max = min + (p2 - p1) * 2;
|
||||
for (int i = min; i <= max; i += 2)
|
||||
{
|
||||
jumps.add (
|
||||
new Jump (i, i - Utility.intValue (buffer[i], buffer[i + 1]), v++));
|
||||
}
|
||||
jumps.add (new Jump (i, i - Utility.getShort (buffer, i), v++));
|
||||
|
||||
break;
|
||||
|
||||
// UB, <block> - word aligned
|
||||
|
|
|
@ -4,6 +4,8 @@ package com.bytezone.diskbrowser.applefile;
|
|||
public class PascalInfo extends AbstractFile
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final byte CR = 0x0D;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public PascalInfo (String name, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -19,10 +21,7 @@ public class PascalInfo extends AbstractFile
|
|||
StringBuilder text = new StringBuilder (getHeader ());
|
||||
|
||||
for (int i = 0; i < buffer.length; i++)
|
||||
if (buffer[i] == 0x0D)
|
||||
text.append ("\n");
|
||||
else
|
||||
text.append ((char) buffer[i]);
|
||||
text.append (buffer[i] == CR ? "\n" : (char) buffer[i]);
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class PascalProcedure
|
|||
this.buffer = buffer;
|
||||
this.slot = slot;
|
||||
int p = buffer.length - 2 - slot * 2;
|
||||
offset = Utility.intValue (buffer[p], buffer[p + 1]);
|
||||
offset = Utility.getShort (buffer, p);
|
||||
procOffset = p - offset;
|
||||
valid = procOffset > 0;
|
||||
|
||||
|
@ -44,10 +44,10 @@ public class PascalProcedure
|
|||
{
|
||||
procedureNo = buffer[procOffset] & 0xFF;
|
||||
procLevel = buffer[procOffset + 1] & 0xFF;
|
||||
codeStart = Utility.intValue (buffer[procOffset - 2], buffer[procOffset - 1]);
|
||||
codeEnd = Utility.intValue (buffer[procOffset - 4], buffer[procOffset - 3]);
|
||||
parmSize = Utility.intValue (buffer[procOffset - 6], buffer[procOffset - 5]);
|
||||
dataSize = Utility.intValue (buffer[procOffset - 8], buffer[procOffset - 7]);
|
||||
codeStart = Utility.getShort (buffer, procOffset - 2);
|
||||
codeEnd = Utility.getShort (buffer, procOffset - 4);
|
||||
parmSize = Utility.getShort (buffer, procOffset - 6);
|
||||
dataSize = Utility.getShort (buffer, procOffset - 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,14 +43,12 @@ public class PascalSegment extends AbstractFile implements PascalConstants
|
|||
// this.blockOffset = blockOffset;
|
||||
// this.relocator = relocator;
|
||||
|
||||
this.blockNo = Utility.intValue (fullBuffer[seq * 4], fullBuffer[seq * 4 + 1]);
|
||||
this.size = Utility.intValue (fullBuffer[seq * 4 + 2], fullBuffer[seq * 4 + 3]);
|
||||
this.blockNo = Utility.getShort (fullBuffer, seq * 4);
|
||||
this.size = Utility.getShort (fullBuffer, seq * 4 + 2);
|
||||
|
||||
segKind =
|
||||
Utility.intValue (fullBuffer[0xC0 + seq * 2], fullBuffer[0xC0 + seq * 2 + 1]);
|
||||
segKind = Utility.getShort (fullBuffer, 0xC0 + seq * 2);
|
||||
|
||||
textAddress =
|
||||
Utility.intValue (fullBuffer[0xE0 + seq * 2], fullBuffer[0xE0 + seq * 2 + 1]);
|
||||
textAddress = Utility.getShort (fullBuffer, 0xE0 + seq * 2);
|
||||
|
||||
// segment 1 is the main segment, 2-6 are used by the system, and 7
|
||||
// onwards is for the program
|
||||
|
@ -65,10 +63,8 @@ public class PascalSegment extends AbstractFile implements PascalConstants
|
|||
|
||||
version = (flags & 0xD0) >> 5;
|
||||
|
||||
intrinsSegs1 =
|
||||
Utility.intValue (fullBuffer[0x120 + seq * 4], fullBuffer[0x120 + seq * 4 + 1]);
|
||||
intrinsSegs2 = Utility.intValue (fullBuffer[0x120 + seq * 4 + 2],
|
||||
fullBuffer[0x120 + seq * 4 + 3]);
|
||||
intrinsSegs1 = Utility.getShort (fullBuffer, 0x120 + seq * 4);
|
||||
intrinsSegs2 = Utility.getShort (fullBuffer, 0x120 + seq * 4 + 2);
|
||||
|
||||
int offset = blockNo * 512;
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ package com.bytezone.diskbrowser.applefile;
|
|||
public class PascalText extends TextFile
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private final static int PAGE_SIZE = 1024;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public PascalText (String name, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -16,26 +18,34 @@ public class PascalText extends TextFile
|
|||
public String getText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// Text files are broken up into 1024-byte pages.
|
||||
// [DLE] [indent] [text] [CR] ... [nulls]
|
||||
|
||||
StringBuilder text = new StringBuilder (getHeader ());
|
||||
|
||||
int ptr = 0x400;
|
||||
int ptr = PAGE_SIZE; // skip text editor header
|
||||
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
if (buffer[ptr] == 0x00)
|
||||
if (buffer[ptr] == 0x00) // padding to page boundary
|
||||
{
|
||||
++ptr;
|
||||
ptr = (ptr / PAGE_SIZE + 1) * PAGE_SIZE; // skip to next page
|
||||
continue;
|
||||
}
|
||||
if (buffer[ptr] == 0x10)
|
||||
|
||||
if (buffer[ptr] == 0x10) // Data Link Escape code
|
||||
{
|
||||
int tab = buffer[ptr + 1] - 0x20;
|
||||
int tab = (buffer[ptr + 1] & 0xFF) - 32; // indent amaount
|
||||
while (tab-- > 0)
|
||||
text.append (" ");
|
||||
ptr += 2;
|
||||
}
|
||||
String line = getLine (ptr);
|
||||
text.append (line + "\n");
|
||||
ptr += line.length () + 1;
|
||||
|
||||
while (buffer[ptr] != 0x0D)
|
||||
text.append ((char) buffer[ptr++]);
|
||||
|
||||
text.append ("\n");
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (text.length () > 0)
|
||||
|
@ -43,14 +53,4 @@ public class PascalText extends TextFile
|
|||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getLine (int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder line = new StringBuilder ();
|
||||
while (buffer[ptr] != 0x0D)
|
||||
line.append ((char) buffer[ptr++]);
|
||||
return line.toString ();
|
||||
}
|
||||
}
|
187
src/com/bytezone/diskbrowser/applefile/ProdosDirectory.java
Executable file
|
@ -0,0 +1,187 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.bytezone.diskbrowser.disk.FormattedDisk;
|
||||
import com.bytezone.diskbrowser.prodos.DirectoryHeader;
|
||||
import com.bytezone.diskbrowser.prodos.ProdosConstants;
|
||||
import com.bytezone.diskbrowser.prodos.ProdosDisk;
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class ProdosDirectory extends AbstractFile implements ProdosConstants
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static Locale US = Locale.US; // to force 3 character months
|
||||
static final DateTimeFormatter df = DateTimeFormatter.ofPattern ("d-LLL-yy", US);
|
||||
static final DateTimeFormatter tf = DateTimeFormatter.ofPattern ("H:mm");
|
||||
static final String UNDERLINE =
|
||||
"----------------------------------------------------\n";
|
||||
|
||||
private static final String NO_DATE = "<NO DATE>";
|
||||
|
||||
private final ProdosDisk parentFD;
|
||||
private final int totalBlocks;
|
||||
private final int freeBlocks;
|
||||
private final int usedBlocks;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public ProdosDirectory (FormattedDisk parent, String name, byte[] buffer,
|
||||
int totalBlocks, int freeBlocks, int usedBlocks)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (name, buffer);
|
||||
|
||||
this.parentFD = (ProdosDisk) parent;
|
||||
this.totalBlocks = totalBlocks;
|
||||
this.freeBlocks = freeBlocks;
|
||||
this.usedBlocks = usedBlocks;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String getText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (showDebugText)
|
||||
return getDebugText ();
|
||||
else
|
||||
return getDirectoryText ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getDebugText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<DirectoryHeader> directoryHeaders = parentFD.getDirectoryHeaders ();
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
for (DirectoryHeader directoryHeader : directoryHeaders)
|
||||
{
|
||||
text.append (UNDERLINE);
|
||||
text.append (directoryHeader.getText ());
|
||||
text.append ("\n");
|
||||
text.append (UNDERLINE);
|
||||
directoryHeader.listFileEntries (text);
|
||||
}
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getDirectoryText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append ("File : " + parentFD.getDisplayPath () + "\n\n");
|
||||
for (int i = 0; i < buffer.length; i += ENTRY_SIZE)
|
||||
{
|
||||
int storageType = (buffer[i] & 0xF0) >> 4;
|
||||
if (storageType == 0)
|
||||
continue; // break??
|
||||
|
||||
int nameLength = buffer[i] & 0x0F;
|
||||
String filename = HexFormatter.getString (buffer, i + 1, nameLength);
|
||||
String subType = "";
|
||||
|
||||
switch (storageType)
|
||||
{
|
||||
case VOLUME_HEADER:
|
||||
case SUBDIRECTORY_HEADER:
|
||||
String root = storageType == VOLUME_HEADER ? "/" : "";
|
||||
text.append (root + filename + "\n\n");
|
||||
text.append (" NAME TYPE BLOCKS "
|
||||
+ "MODIFIED CREATED ENDFILE SUBTYPE" + "\n\n");
|
||||
break;
|
||||
|
||||
case FREE:
|
||||
case SEEDLING:
|
||||
case SAPLING:
|
||||
case TREE:
|
||||
case PASCAL_ON_PROFILE:
|
||||
case GSOS_EXTENDED_FILE:
|
||||
case SUBDIRECTORY:
|
||||
int type = buffer[i + 16] & 0xFF;
|
||||
int blocks = Utility.getShort (buffer, i + 19);
|
||||
|
||||
LocalDateTime createdDate = Utility.getAppleDate (buffer, i + 24);
|
||||
LocalDateTime modifiedDate = Utility.getAppleDate (buffer, i + 33);
|
||||
|
||||
String dateC =
|
||||
createdDate == null ? NO_DATE : createdDate.format (df).toUpperCase ();
|
||||
String dateM =
|
||||
modifiedDate == null ? NO_DATE : modifiedDate.format (df).toUpperCase ();
|
||||
|
||||
String timeC = createdDate == null ? "" : createdDate.format (tf);
|
||||
String timeM = modifiedDate == null ? "" : modifiedDate.format (tf);
|
||||
|
||||
int eof = Utility.intValue (buffer[i + 21], buffer[i + 22], buffer[i + 23]);
|
||||
int fileType = buffer[i + 16] & 0xFF;
|
||||
String locked = (buffer[i + 30] & 0xE0) == 0xE0 ? " " : "*";
|
||||
int aux = Utility.getShort (buffer, i + 31);
|
||||
|
||||
switch (fileType)
|
||||
{
|
||||
case FILE_TYPE_TEXT:
|
||||
subType = String.format ("R=%5d", aux);
|
||||
break;
|
||||
|
||||
case FILE_TYPE_BINARY:
|
||||
case FILE_TYPE_PNT:
|
||||
case FILE_TYPE_PIC:
|
||||
case FILE_TYPE_FOT:
|
||||
subType = String.format ("A=$%4X", aux);
|
||||
break;
|
||||
|
||||
case FILE_TYPE_AWP:
|
||||
int flags = Utility.intValue (buffer[i + 32], buffer[i + 31]); // aux backwards!
|
||||
if (flags != 0)
|
||||
filename = convert (filename, flags);
|
||||
break;
|
||||
|
||||
default:
|
||||
subType = "";
|
||||
}
|
||||
|
||||
String forkFlag = storageType == 5 ? "+" : " ";
|
||||
text.append (
|
||||
String.format ("%s%-15s %3s%s %5d %9s %5s %9s %5s %8d %7s %04X%n",
|
||||
locked, filename, ProdosConstants.fileTypes[type], forkFlag, blocks,
|
||||
dateM, timeM, dateC, timeC, eof, subType, aux));
|
||||
break;
|
||||
|
||||
default:
|
||||
text.append (" <Unknown strage type : " + storageType + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
text.append (
|
||||
String.format ("%nBLOCKS FREE:%5d BLOCKS USED:%5d TOTAL BLOCKS:%5d%n",
|
||||
freeBlocks, usedBlocks, totalBlocks));
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String convert (String name, int flags)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
char[] newName = name.toCharArray ();
|
||||
for (int i = 0, weight = 0x8000; i < newName.length; i++, weight >>>= 1)
|
||||
{
|
||||
if ((flags & weight) != 0)
|
||||
{
|
||||
if (newName[i] == '.')
|
||||
newName[i] = ' ';
|
||||
else if (newName[i] >= 'A' && newName[i] <= 'Z')
|
||||
newName[i] += 32;
|
||||
}
|
||||
}
|
||||
return new String (newName);
|
||||
}
|
||||
}
|
|
@ -72,37 +72,37 @@ public class QuickDrawFont extends CharacterList
|
|||
|
||||
int ptr = nameLength + 1; // start of header record
|
||||
|
||||
headerSize = Utility.unsignedShort (buffer, ptr);
|
||||
headerSize = Utility.getShort (buffer, ptr);
|
||||
fontDefinitionOffset = nameLength + 1 + headerSize * 2;
|
||||
|
||||
fontFamily = Utility.unsignedShort (buffer, ptr + 2);
|
||||
fontStyle = Utility.unsignedShort (buffer, ptr + 4);
|
||||
fontSize = Utility.unsignedShort (buffer, ptr + 6);
|
||||
fontFamily = Utility.getShort (buffer, ptr + 2);
|
||||
fontStyle = Utility.getShort (buffer, ptr + 4);
|
||||
fontSize = Utility.getShort (buffer, ptr + 6);
|
||||
versionMajor = buffer[ptr + 8] & 0xFF;
|
||||
versionMinor = buffer[ptr + 9] & 0xFF;
|
||||
extent = Utility.unsignedShort (buffer, ptr + 10);
|
||||
extent = Utility.getShort (buffer, ptr + 10);
|
||||
|
||||
ptr = fontDefinitionOffset;
|
||||
|
||||
fontType = Utility.unsignedShort (buffer, ptr);
|
||||
firstChar = Utility.unsignedShort (buffer, ptr + 2);
|
||||
lastChar = Utility.unsignedShort (buffer, ptr + 4);
|
||||
widMax = Utility.unsignedShort (buffer, ptr + 6);
|
||||
kernMax = Utility.signedShort (buffer, ptr + 8);
|
||||
nDescent = Utility.signedShort (buffer, ptr + 10);
|
||||
fRectWidth = Utility.unsignedShort (buffer, ptr + 12);
|
||||
fRectHeight = Utility.unsignedShort (buffer, ptr + 14);
|
||||
fontType = Utility.getShort (buffer, ptr);
|
||||
firstChar = Utility.getShort (buffer, ptr + 2);
|
||||
lastChar = Utility.getShort (buffer, ptr + 4);
|
||||
widMax = Utility.getShort (buffer, ptr + 6);
|
||||
kernMax = Utility.getSignedShort (buffer, ptr + 8);
|
||||
nDescent = Utility.getSignedShort (buffer, ptr + 10);
|
||||
fRectWidth = Utility.getShort (buffer, ptr + 12);
|
||||
fRectHeight = Utility.getShort (buffer, ptr + 14);
|
||||
|
||||
owTLoc = Utility.unsignedShort (buffer, ptr + 16);
|
||||
owTLoc = Utility.getShort (buffer, ptr + 16);
|
||||
|
||||
offsetWidthTableOffset = (ptr + 16) + owTLoc * 2;
|
||||
locationTableOffset = offsetWidthTableOffset - (lastChar - firstChar + 3) * 2;
|
||||
bitImageOffset = ptr + 26;
|
||||
|
||||
ascent = Utility.unsignedShort (buffer, ptr + 18);
|
||||
descent = Utility.unsignedShort (buffer, ptr + 20);
|
||||
leading = Utility.unsignedShort (buffer, ptr + 22);
|
||||
rowWords = Utility.unsignedShort (buffer, ptr + 24);
|
||||
ascent = Utility.getShort (buffer, ptr + 18);
|
||||
descent = Utility.getShort (buffer, ptr + 20);
|
||||
leading = Utility.getShort (buffer, ptr + 22);
|
||||
rowWords = Utility.getShort (buffer, ptr + 24);
|
||||
|
||||
totalCharacters = lastChar - firstChar + 2; // includes 'missing' character
|
||||
|
||||
|
@ -151,12 +151,12 @@ public class QuickDrawFont extends CharacterList
|
|||
for (int i = 0, max = totalCharacters + 1; i < max; i++)
|
||||
{
|
||||
// index into the strike
|
||||
int location = Utility.unsignedShort (buffer, locationTableOffset + i * 2);
|
||||
int location = Utility.getShort (buffer, locationTableOffset + i * 2);
|
||||
|
||||
int j = i + 1; // next character
|
||||
if (j < max)
|
||||
{
|
||||
int nextLocation = Utility.unsignedShort (buffer, locationTableOffset + j * 2);
|
||||
int nextLocation = Utility.getShort (buffer, locationTableOffset + j * 2);
|
||||
int pixelWidth = nextLocation - location;
|
||||
|
||||
if (pixelWidth > 0)
|
||||
|
@ -259,9 +259,8 @@ public class QuickDrawFont extends CharacterList
|
|||
if (offset == 255 && width == 255)
|
||||
continue;
|
||||
|
||||
int location = Utility.unsignedShort (buffer, locationTableOffset + i * 2);
|
||||
int nextLocation =
|
||||
Utility.unsignedShort (buffer, locationTableOffset + (i + 1) * 2);
|
||||
int location = Utility.getShort (buffer, locationTableOffset + i * 2);
|
||||
int nextLocation = Utility.getShort (buffer, locationTableOffset + (i + 1) * 2);
|
||||
int pixelWidth = nextLocation - location;
|
||||
|
||||
text.append (String.format (
|
||||
|
|
|
@ -27,7 +27,7 @@ public class SHRPictureFile1 extends HiResImage
|
|||
int ptr = 0;
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
int len = Utility.unsignedLong (buffer, ptr);
|
||||
int len = Utility.getLong (buffer, ptr);
|
||||
if (len == 0 || len > buffer.length)
|
||||
{
|
||||
System.out.printf ("Block length: %d%n", len);
|
||||
|
@ -141,7 +141,7 @@ public class SHRPictureFile1 extends HiResImage
|
|||
int lo = dirEntry.mode & 0x00FF; // mode bit if hi == 0
|
||||
|
||||
boolean fillMode = (dirEntry.mode & 0x20) != 0;
|
||||
// assert fillMode == false;
|
||||
// assert fillMode == false;
|
||||
|
||||
if (hi != 0)
|
||||
System.out.println ("hi not zero");
|
||||
|
@ -232,7 +232,7 @@ public class SHRPictureFile1 extends HiResImage
|
|||
super (kind, data);
|
||||
|
||||
int ptr = 5 + kind.length ();
|
||||
numColorTables = Utility.unsignedShort (data, ptr);
|
||||
numColorTables = Utility.getShort (data, ptr);
|
||||
|
||||
ptr += 2;
|
||||
colorTables = new ColorTable[numColorTables];
|
||||
|
@ -242,7 +242,8 @@ public class SHRPictureFile1 extends HiResImage
|
|||
if (ptr < data.length - 32)
|
||||
colorTables[i] = new ColorTable (i, data, ptr);
|
||||
else
|
||||
colorTables[i] = new ColorTable (i, 0x00); // default empty table !! not finished
|
||||
colorTables[i] = new ColorTable (i, 0x00); // default empty table !! not
|
||||
// finished
|
||||
ptr += 32;
|
||||
}
|
||||
}
|
||||
|
@ -286,9 +287,9 @@ public class SHRPictureFile1 extends HiResImage
|
|||
super (kind, data);
|
||||
|
||||
int ptr = 5 + kind.length ();
|
||||
masterMode = Utility.unsignedShort (data, ptr);
|
||||
pixelsPerScanLine = Utility.unsignedShort (data, ptr + 2);
|
||||
numColorTables = Utility.unsignedShort (data, ptr + 4);
|
||||
masterMode = Utility.getShort (data, ptr);
|
||||
pixelsPerScanLine = Utility.getShort (data, ptr + 2);
|
||||
numColorTables = Utility.getShort (data, ptr + 4);
|
||||
mode640 = (masterMode & 0x80) != 0;
|
||||
|
||||
ptr += 6;
|
||||
|
@ -299,7 +300,7 @@ public class SHRPictureFile1 extends HiResImage
|
|||
ptr += 32;
|
||||
}
|
||||
|
||||
numScanLines = Utility.unsignedShort (data, ptr);
|
||||
numScanLines = Utility.getShort (data, ptr);
|
||||
ptr += 2;
|
||||
|
||||
scanLineDirectory = new DirEntry[numScanLines];
|
||||
|
@ -332,11 +333,11 @@ public class SHRPictureFile1 extends HiResImage
|
|||
ptr = 0;
|
||||
for (int line = 0; line < numScanLines; line++)
|
||||
{
|
||||
// if (isOddAndEmpty (packedScanLines[line]))
|
||||
// {
|
||||
// System.out.println ("Odd number of bytes in empty buffer in " + name);
|
||||
// break;
|
||||
// }
|
||||
// if (isOddAndEmpty (packedScanLines[line]))
|
||||
// {
|
||||
// System.out.println ("Odd number of bytes in empty buffer in " + name);
|
||||
// break;
|
||||
// }
|
||||
|
||||
int bytesUnpacked = unpack (packedScanLines[line], 0,
|
||||
packedScanLines[line].length, unpackedBuffer, ptr);
|
||||
|
@ -352,16 +353,17 @@ public class SHRPictureFile1 extends HiResImage
|
|||
}
|
||||
|
||||
// -------------------------------------------------------------------------------//
|
||||
// private boolean isOddAndEmpty (byte[] buffer)
|
||||
// // -------------------------------------------------------------------------------//
|
||||
// {
|
||||
// if (buffer.length % 2 == 0)
|
||||
// return false;
|
||||
// for (byte b : buffer)
|
||||
// if (b != 0)
|
||||
// return false;
|
||||
// return true;
|
||||
// }
|
||||
// private boolean isOddAndEmpty (byte[] buffer)
|
||||
// //
|
||||
// -------------------------------------------------------------------------------//
|
||||
// {
|
||||
// if (buffer.length % 2 == 0)
|
||||
// return false;
|
||||
// for (byte b : buffer)
|
||||
// if (b != 0)
|
||||
// return false;
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// -------------------------------------------------------------------------------//
|
||||
@Override
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.awt.image.DataBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.nufx.LZW3;
|
||||
import com.bytezone.diskbrowser.prodos.ProdosConstants;
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
@ -99,10 +100,10 @@ public class SHRPictureFile2 extends HiResImage
|
|||
unpack (buffer, 0, buffer.length, newBuffer, 0);
|
||||
buffer = newBuffer;
|
||||
|
||||
int mode = Utility.unsignedShort (this.buffer, 0);
|
||||
int rect1 = Utility.unsignedLong (this.buffer, 2);
|
||||
int rect2 = Utility.unsignedLong (this.buffer, 6);
|
||||
int version = Utility.unsignedShort (this.buffer, 10); // $8211
|
||||
int mode = Utility.getShort (this.buffer, 0);
|
||||
int rect1 = Utility.getLong (this.buffer, 2);
|
||||
int rect2 = Utility.getLong (this.buffer, 6);
|
||||
int version = Utility.getShort (this.buffer, 10); // $8211
|
||||
|
||||
break;
|
||||
|
||||
|
@ -144,6 +145,36 @@ public class SHRPictureFile2 extends HiResImage
|
|||
|
||||
break;
|
||||
|
||||
case 0x8005:
|
||||
int ptr = buffer.length - 17;
|
||||
|
||||
int imageType = Utility.getShort (buffer, ptr);
|
||||
int imageHeight = Utility.getShort (buffer, ptr + 2);
|
||||
int imageWidth = Utility.getShort (buffer, ptr + 4);
|
||||
|
||||
String id = HexFormatter.getPascalString (buffer, ptr + 6);
|
||||
assert "DreamWorld".equals (id);
|
||||
|
||||
int expectedLen = 32000 + 512;
|
||||
if (imageType == 0) // 256 colours
|
||||
expectedLen += (256 + 512);
|
||||
else // 3200 colours
|
||||
expectedLen += 6400;
|
||||
|
||||
byte[] dstBuffer = new byte[expectedLen + 1024];
|
||||
LZW3 lzw3 = new LZW3 ();
|
||||
int bytes = lzw3.unpack (buffer, dstBuffer, expectedLen);
|
||||
buffer = dstBuffer;
|
||||
|
||||
colorTables = new ColorTable[imageHeight];
|
||||
for (int i = 0; i < colorTables.length; i++)
|
||||
{
|
||||
colorTables[i] = new ColorTable (i, this.buffer, 32000 + i * COLOR_TABLE_SIZE);
|
||||
colorTables[i].reverse ();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
System.out.printf ("%s: PNT unknown aux: %04X%n", name, auxType);
|
||||
failureReason = "unknown PNT aux";
|
||||
|
@ -154,13 +185,13 @@ public class SHRPictureFile2 extends HiResImage
|
|||
private void doAnimation ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// int len = HexFormatter.unsignedLong (buffer, 0x8000);
|
||||
delay = Utility.unsignedLong (buffer, 0x8004);
|
||||
// int len = HexFormatter.getLong (buffer, 0x8000);
|
||||
delay = Utility.getLong (buffer, 0x8004);
|
||||
if (delay > 60)
|
||||
delay = 10;
|
||||
delay = delay * 1000 / 60;
|
||||
|
||||
// int offset = HexFormatter.unsignedLong (buffer, 0x8008);
|
||||
// int offset = HexFormatter.getLong (buffer, 0x8008);
|
||||
// int blockLen = eof - 0x8008;
|
||||
|
||||
// System.out.printf ("Delay: %,d%n", delay);
|
||||
|
@ -173,7 +204,7 @@ public class SHRPictureFile2 extends HiResImage
|
|||
int start = ptr;
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
int off = Utility.unsignedShort (buffer, ptr);
|
||||
int off = Utility.getShort (buffer, ptr);
|
||||
|
||||
ptr += 4;
|
||||
if (off == 0)
|
||||
|
@ -299,7 +330,7 @@ public class SHRPictureFile2 extends HiResImage
|
|||
|
||||
while (true)
|
||||
{
|
||||
int offset = Utility.unsignedShort (buffer, ptr);
|
||||
int offset = Utility.getShort (buffer, ptr);
|
||||
if (offset == 0)
|
||||
break;
|
||||
|
||||
|
|
|
@ -1,16 +1,40 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class SegmentDictionary
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private final boolean isValid;
|
||||
private int[] codeAddress = new int[16];
|
||||
private int[] codeLength = new int[16];
|
||||
private String[] segName = new String[16];
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public SegmentDictionary (String name, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
isValid = !name.equals ("SYSTEM.INTERP"); // temporary
|
||||
|
||||
int ptr = 0;
|
||||
for (int seg = 0; seg < 16; seg++)
|
||||
{
|
||||
codeAddress[seg] = Utility.getShort (buffer, ptr);
|
||||
ptr += 2;
|
||||
codeLength[seg] = Utility.getShort (buffer, ptr);
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
ptr = 64;
|
||||
for (int seg = 0; seg < 16; seg++)
|
||||
{
|
||||
segName[seg] = new String (buffer, ptr, 8);
|
||||
ptr += 8;
|
||||
}
|
||||
|
||||
// for (int seg = 0; seg < 16; seg++)
|
||||
// System.out.printf ("%04X %04X %s%n", codeAddress[seg], codeLength[seg], segName[seg]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
|
@ -37,6 +37,8 @@ public class SegmentHeader
|
|||
String loadname;
|
||||
String segname;
|
||||
|
||||
boolean debug = false;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public SegmentHeader (byte[] buffer, int offset)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -52,20 +54,20 @@ public class SegmentHeader
|
|||
version = buffer[offset + 15] & 0xFF;
|
||||
|
||||
banksize = Utility.getLong (buffer, offset + 16);
|
||||
kind2 = Utility.getWord (buffer, offset + 20);
|
||||
unused = Utility.getWord (buffer, offset + 22);
|
||||
kind2 = Utility.getShort (buffer, offset + 20);
|
||||
unused = Utility.getShort (buffer, offset + 22);
|
||||
org = Utility.getLong (buffer, offset + 24);
|
||||
align = Utility.getLong (buffer, offset + 28);
|
||||
|
||||
numsex = buffer[offset + 32] & 0xFF;
|
||||
lcbank = buffer[offset + 33] & 0xFF;
|
||||
|
||||
segnum = Utility.getWord (buffer, offset + 34);
|
||||
segnum = Utility.getShort (buffer, offset + 34);
|
||||
|
||||
entry = Utility.getLong (buffer, offset + 36);
|
||||
|
||||
dispname = Utility.getWord (buffer, offset + 40);
|
||||
dispdata = Utility.getWord (buffer, offset + 42);
|
||||
dispname = Utility.getShort (buffer, offset + 40);
|
||||
dispdata = Utility.getShort (buffer, offset + 42);
|
||||
|
||||
decodeKind ();
|
||||
|
||||
|
@ -79,7 +81,8 @@ public class SegmentHeader
|
|||
else
|
||||
segname = "not finished";
|
||||
|
||||
System.out.println (this);
|
||||
if (debug)
|
||||
System.out.println (this);
|
||||
|
||||
int ptr = offset + dispdata;
|
||||
while (true)
|
||||
|
@ -89,25 +92,33 @@ public class SegmentHeader
|
|||
|
||||
if (recType > 0 && recType <= 0xDF)
|
||||
{
|
||||
System.out.printf ("Const: %02X%n", recType);
|
||||
System.out.println (HexFormatter.format (buffer, ptr, recType + 1, ptr));
|
||||
if (debug)
|
||||
{
|
||||
System.out.printf ("Const: %02X%n", recType);
|
||||
System.out.println (HexFormatter.format (buffer, ptr, recType + 1, ptr));
|
||||
}
|
||||
ptr += recType + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.printf ("%02X ", recType);
|
||||
if (debug)
|
||||
System.out.printf ("%02X ", recType);
|
||||
|
||||
switch (recType)
|
||||
{
|
||||
case 0x00: // END
|
||||
System.out.println ("END");
|
||||
if (debug)
|
||||
System.out.println ("END");
|
||||
break;
|
||||
|
||||
case 0xE0: // ALIGN
|
||||
System.out.printf ("ALIGN:%n");
|
||||
if (debug)
|
||||
System.out.printf ("ALIGN:%n");
|
||||
break;
|
||||
|
||||
case 0xE1: // ORG
|
||||
System.out.printf ("ORG:%n");
|
||||
if (debug)
|
||||
System.out.printf ("ORG:%n");
|
||||
break;
|
||||
|
||||
case 0xE2: // RELOC
|
||||
|
@ -115,8 +126,9 @@ public class SegmentHeader
|
|||
int bitShift = buffer[ptr + 2] & 0xFF;
|
||||
int segmentOffset = Utility.getLong (buffer, ptr + 3);
|
||||
int value = Utility.getLong (buffer, ptr + 7);
|
||||
System.out.printf ("RELOC: %02X %02X %08X %08X%n", bytesRelocated, bitShift,
|
||||
segmentOffset, value);
|
||||
if (debug)
|
||||
System.out.printf ("RELOC: %02X %02X %08X %08X%n", bytesRelocated, bitShift,
|
||||
segmentOffset, value);
|
||||
ptr += 11;
|
||||
continue;
|
||||
|
||||
|
@ -124,102 +136,121 @@ public class SegmentHeader
|
|||
int count1 = buffer[ptr + 1] & 0xFF;
|
||||
int count2 = buffer[ptr + 2] & 0xFF;
|
||||
int operandOffset = Utility.getLong (buffer, ptr + 3);
|
||||
int fileNo = Utility.getWord (buffer, ptr + 7);
|
||||
int segNo = Utility.getWord (buffer, ptr + 9);
|
||||
int fileNo = Utility.getShort (buffer, ptr + 7);
|
||||
int segNo = Utility.getShort (buffer, ptr + 9);
|
||||
int subroutineOffset = Utility.getLong (buffer, ptr + 11);
|
||||
System.out.printf ("INTERSEG: %02X %02X %08X %04X %04X %08X%n", count1, count2,
|
||||
operandOffset, fileNo, segNo, subroutineOffset);
|
||||
if (debug)
|
||||
System.out.printf ("INTERSEG: %02X %02X %08X %04X %04X %08X%n", count1,
|
||||
count2, operandOffset, fileNo, segNo, subroutineOffset);
|
||||
ptr += 15;
|
||||
break;
|
||||
|
||||
case 0xE4: // USING
|
||||
System.out.printf ("USING:%n");
|
||||
if (debug)
|
||||
System.out.printf ("USING:%n");
|
||||
break;
|
||||
|
||||
case 0xE5: // STRONG
|
||||
System.out.printf ("STRONG:%n");
|
||||
if (debug)
|
||||
System.out.printf ("STRONG:%n");
|
||||
break;
|
||||
|
||||
case 0xE6: // GLOBAL
|
||||
System.out.printf ("GLOBAL:%n");
|
||||
if (debug)
|
||||
System.out.printf ("GLOBAL:%n");
|
||||
break;
|
||||
|
||||
case 0xE7: // GEQU
|
||||
System.out.printf ("GEQU:%n");
|
||||
if (debug)
|
||||
System.out.printf ("GEQU:%n");
|
||||
break;
|
||||
|
||||
case 0xE8: // MEM
|
||||
System.out.printf ("MEM:%n");
|
||||
if (debug)
|
||||
System.out.printf ("MEM:%n");
|
||||
break;
|
||||
|
||||
case 0xEB: // EXPR
|
||||
System.out.printf ("EXPR:%n");
|
||||
if (debug)
|
||||
System.out.printf ("EXPR:%n");
|
||||
break;
|
||||
|
||||
case 0xEC: // ZEXPR
|
||||
System.out.printf ("ZEXPR:%n");
|
||||
if (debug)
|
||||
System.out.printf ("ZEXPR:%n");
|
||||
break;
|
||||
|
||||
case 0xED: // BEXPR
|
||||
System.out.printf ("BEXPR:%n");
|
||||
if (debug)
|
||||
System.out.printf ("BEXPR:%n");
|
||||
break;
|
||||
|
||||
case 0xEE: // RELEXPR
|
||||
System.out.printf ("RELEXPR:%n");
|
||||
if (debug)
|
||||
System.out.printf ("RELEXPR:%n");
|
||||
break;
|
||||
|
||||
case 0xEF: // LOCAL
|
||||
System.out.printf ("LOCAL:%n");
|
||||
if (debug)
|
||||
System.out.printf ("LOCAL:%n");
|
||||
break;
|
||||
|
||||
case 0xF0: // EQU
|
||||
String label = HexFormatter.getPascalString (buffer, ptr + 1);
|
||||
System.out.printf ("EQU: %s%n", label);
|
||||
if (debug)
|
||||
System.out.printf ("EQU: %s%n", label);
|
||||
break;
|
||||
|
||||
case 0xF1: // DS
|
||||
System.out.printf ("DS:%n");
|
||||
if (debug)
|
||||
System.out.printf ("DS:%n");
|
||||
break;
|
||||
|
||||
case 0xF2: // LCONST
|
||||
int constLength = Utility.getLong (buffer, ptr + 1);
|
||||
System.out.printf ("Const: %04X%n", constLength);
|
||||
if (debug)
|
||||
System.out.printf ("Const: %04X%n", constLength);
|
||||
ptr += constLength + 5;
|
||||
continue;
|
||||
|
||||
case 0xF3: // LEXPR
|
||||
System.out.printf ("LEXPR:%n");
|
||||
if (debug)
|
||||
System.out.printf ("LEXPR:%n");
|
||||
break;
|
||||
|
||||
case 0xF4: // ENTRY
|
||||
System.out.printf ("ENTRY:%n");
|
||||
if (debug)
|
||||
System.out.printf ("ENTRY:%n");
|
||||
break;
|
||||
|
||||
case 0xF5: // cRELOC
|
||||
int cBytesRelocated = buffer[ptr + 1] & 0xFF;
|
||||
int cBitShift = buffer[ptr + 2] & 0xFF;
|
||||
int cSegmentOffset = Utility.getWord (buffer, ptr + 3);
|
||||
int cValue = Utility.getWord (buffer, ptr + 5);
|
||||
System.out.printf ("cRELOC: %02X %02X %08X %08X%n", cBytesRelocated, cBitShift,
|
||||
cSegmentOffset, cValue);
|
||||
int cSegmentOffset = Utility.getShort (buffer, ptr + 3);
|
||||
int cValue = Utility.getShort (buffer, ptr + 5);
|
||||
if (debug)
|
||||
System.out.printf ("cRELOC: %02X %02X %08X %08X%n", cBytesRelocated,
|
||||
cBitShift, cSegmentOffset, cValue);
|
||||
ptr += 7;
|
||||
continue;
|
||||
|
||||
case 0xF6: // cINTERSEG
|
||||
int cCount1 = buffer[ptr + 1] & 0xFF;
|
||||
int cCount2 = buffer[ptr + 2] & 0xFF;
|
||||
int cOperandOffset = Utility.getWord (buffer, ptr + 3);
|
||||
int cOperandOffset = Utility.getShort (buffer, ptr + 3);
|
||||
int cSegNo = buffer[ptr + 5] & 0xFF;
|
||||
int cSubroutineOffset = Utility.getWord (buffer, ptr + 6);
|
||||
System.out.printf ("cINTERSEG: %02X %02X %04X %02X %04X%n", cCount1, cCount2,
|
||||
cOperandOffset, cSegNo, cSubroutineOffset);
|
||||
int cSubroutineOffset = Utility.getShort (buffer, ptr + 6);
|
||||
if (debug)
|
||||
System.out.printf ("cINTERSEG: %02X %02X %04X %02X %04X%n", cCount1, cCount2,
|
||||
cOperandOffset, cSegNo, cSubroutineOffset);
|
||||
ptr += 8;
|
||||
continue;
|
||||
|
||||
case 0xF7: // SUPER
|
||||
int superLength = Utility.getLong (buffer, ptr + 1);
|
||||
int recordType = buffer[ptr + 5] & 0xFF;
|
||||
System.out.printf ("Super type %02X%n", recordType);
|
||||
if (debug)
|
||||
System.out.printf ("Super type %02X%n", recordType);
|
||||
ptr += superLength + 5;
|
||||
continue;
|
||||
|
||||
|
@ -228,7 +259,8 @@ public class SegmentHeader
|
|||
break;
|
||||
}
|
||||
|
||||
System.out.println ();
|
||||
if (debug)
|
||||
System.out.println ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -258,33 +290,18 @@ public class SegmentHeader
|
|||
kindPrivate = (segAttr & 0x40) != 0;
|
||||
kindStatic = (segAttr & 0x80) == 0;
|
||||
|
||||
switch (segType)
|
||||
kindWhereText = switch (segType)
|
||||
{
|
||||
case 0:
|
||||
kindWhereText = "Code Segment";
|
||||
break;
|
||||
case 1:
|
||||
kindWhereText = "Data Segment";
|
||||
break;
|
||||
case 2:
|
||||
kindWhereText = "Jump Table Segment";
|
||||
break;
|
||||
case 4:
|
||||
kindWhereText = "Pathname Segment";
|
||||
break;
|
||||
case 8:
|
||||
kindWhereText = "Library Dictionary Segment";
|
||||
break;
|
||||
case 0x10:
|
||||
kindWhereText = "Initialization Segment";
|
||||
break;
|
||||
case 0x11:
|
||||
kindWhereText = "Absolute Bank Segment";
|
||||
break;
|
||||
case 0x12:
|
||||
kindWhereText = "Direct Page / Stack Segment";
|
||||
break;
|
||||
}
|
||||
case 0x00 -> "Code Segment";
|
||||
case 0x01 -> "Data Segment";
|
||||
case 0x02 -> "Jump Table Segment";
|
||||
case 0x04 -> "Pathname Segment";
|
||||
case 0x08 -> "Library Dictionary Segment";
|
||||
case 0x10 -> "Initialization Segment";
|
||||
case 0x11 -> "Absolute Bank Segment";
|
||||
case 0x12 -> "Direct Page / Stack Segment";
|
||||
default -> "Unknown";
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
|
@ -77,21 +77,14 @@ public class Selector extends AbstractFile
|
|||
labelLength = buffer[ptr] & 0xFF;
|
||||
label = new String (buffer, ptr + 1, labelLength);
|
||||
copyFlags = buffer[ptr + 15];
|
||||
switch (copyFlags & 0xFF)
|
||||
|
||||
copyText = switch (copyFlags & 0xFF)
|
||||
{
|
||||
case 0:
|
||||
copyText = "First boot";
|
||||
break;
|
||||
case 0x80:
|
||||
copyText = "First use";
|
||||
break;
|
||||
case 0xC0:
|
||||
copyText = "Never";
|
||||
break;
|
||||
default:
|
||||
copyText = "Unknown";
|
||||
break;
|
||||
}
|
||||
case 0x00 -> "First boot";
|
||||
case 0x80 -> "First use";
|
||||
case 0xC0 -> "Never";
|
||||
default -> "Unknown";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
235
src/com/bytezone/diskbrowser/applefile/Shape.java
Normal file
|
@ -0,0 +1,235 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
class Shape
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final int SIZE = 400;
|
||||
|
||||
private final byte[] buffer;
|
||||
private final int index;
|
||||
|
||||
int offset;
|
||||
int actualLength;
|
||||
int minRow, maxRow;
|
||||
int minCol, maxCol;
|
||||
int startRow = SIZE / 2;
|
||||
int startCol = SIZE / 2;
|
||||
int[][] grid = new int[SIZE][SIZE];
|
||||
int[][] displayGrid;
|
||||
boolean valid;
|
||||
|
||||
BufferedImage image;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public Shape (byte[] buffer, int index)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.index = index;
|
||||
this.buffer = buffer;
|
||||
|
||||
int row = startRow;
|
||||
int col = startCol;
|
||||
|
||||
offset = Utility.getShort (buffer, index * 2 + 2);
|
||||
|
||||
int ptr = offset;
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
int value = buffer[ptr++] & 0xFF;
|
||||
|
||||
if (value == 0)
|
||||
break;
|
||||
|
||||
// P = plot
|
||||
// DD = direction to move
|
||||
int v1 = value >>> 6; // DD......
|
||||
int v2 = (value & 0x38) >>> 3; // ..PDD...
|
||||
int v3 = value & 0x07; // .....PDD
|
||||
|
||||
// rightmost 3 bits
|
||||
if (v3 >= 4)
|
||||
if (!plot (grid, row, col))
|
||||
return;
|
||||
|
||||
if (v3 == 0 || v3 == 4)
|
||||
row--;
|
||||
else if (v3 == 1 || v3 == 5)
|
||||
col++;
|
||||
else if (v3 == 2 || v3 == 6)
|
||||
row++;
|
||||
else
|
||||
col--;
|
||||
|
||||
// middle 3 bits
|
||||
if (v2 >= 4)
|
||||
if (!plot (grid, row, col))
|
||||
return;
|
||||
|
||||
// cannot move up without plotting if v1 is zero
|
||||
if ((v2 == 0 && v1 != 0) || v2 == 4)
|
||||
row--;
|
||||
else if (v2 == 1 || v2 == 5)
|
||||
col++;
|
||||
else if (v2 == 2 || v2 == 6)
|
||||
row++;
|
||||
else if (v2 == 3 || v2 == 7)
|
||||
col--;
|
||||
|
||||
// leftmost 2 bits (cannot plot or move up)
|
||||
if (v1 == 1)
|
||||
col++;
|
||||
else if (v1 == 2)
|
||||
row++;
|
||||
else if (v1 == 3)
|
||||
col--;
|
||||
}
|
||||
|
||||
actualLength = ptr - offset;
|
||||
|
||||
// endRow = row;
|
||||
// endCol = col;
|
||||
|
||||
// find min and max rows with pixels
|
||||
minRow = startRow;
|
||||
maxRow = startRow;
|
||||
// minRow = Math.min (minRow, endRow);
|
||||
// maxRow = Math.max (maxRow, endRow);
|
||||
for (row = 1; row < grid.length; row++)
|
||||
{
|
||||
if (grid[row][0] > 0)
|
||||
{
|
||||
minRow = Math.min (minRow, row);
|
||||
maxRow = Math.max (maxRow, row);
|
||||
}
|
||||
}
|
||||
|
||||
// find min and max columns with pixels
|
||||
minCol = startCol;
|
||||
maxCol = startCol;
|
||||
// minCol = Math.min (minCol, endCol);
|
||||
// maxCol = Math.max (maxCol, endCol);
|
||||
for (col = 1; col < grid[0].length; col++)
|
||||
{
|
||||
if (grid[0][col] > 0)
|
||||
{
|
||||
minCol = Math.min (minCol, col);
|
||||
maxCol = Math.max (maxCol, col);
|
||||
}
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void convertGrid (int offsetRows, int offsetColumns, int rows, int columns)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// System.out.printf ("Converting shape # %d%n", index);
|
||||
// System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows,
|
||||
// offsetColumns);
|
||||
// System.out.printf ("rows %d cols %d%n", rows, columns);
|
||||
|
||||
displayGrid = new int[rows][columns];
|
||||
for (int row = 0; row < rows; row++)
|
||||
for (int col = 0; col < columns; col++)
|
||||
displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col];
|
||||
grid = null;
|
||||
|
||||
// draw the image
|
||||
image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY);
|
||||
DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
|
||||
int element = 0;
|
||||
for (int row = 0; row < rows; row++)
|
||||
for (int col = 0; col < columns; col++)
|
||||
dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255);
|
||||
|
||||
startRow -= offsetRows;
|
||||
startCol -= offsetColumns;
|
||||
// endRow -= offsetRows;
|
||||
// endCol -= offsetColumns;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean plot (int[][] grid, int row, int col)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (row < 0 || row >= SIZE || col < 0 || col >= SIZE)
|
||||
{
|
||||
System.out.printf ("Shape table out of range: %d, %d%n", row, col);
|
||||
return false;
|
||||
}
|
||||
grid[row][col] = 1; // plot
|
||||
grid[0][col]++; // increment total column dots
|
||||
grid[row][0]++; // increment total row dots
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void drawText (StringBuilder text)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
text.append (String.format ("Shape : %d%n", index));
|
||||
text.append (String.format ("Size : %d%n", actualLength));
|
||||
// text.append (String.format ("Width : %d%n", width));
|
||||
// text.append (String.format ("Height : %d%n", height));
|
||||
|
||||
// append the shape's data
|
||||
String bytes = HexFormatter.getHexString (buffer, offset, actualLength);
|
||||
int ptr = offset;
|
||||
for (String s : split (bytes))
|
||||
{
|
||||
text.append (String.format (" %04X : %s%n", ptr, s));
|
||||
ptr += 16;
|
||||
}
|
||||
text.append ("\n");
|
||||
|
||||
for (int row = 0; row < displayGrid.length; row++)
|
||||
{
|
||||
for (int col = 0; col < displayGrid[0].length; col++)
|
||||
if (col == startCol && row == startRow)
|
||||
text.append (displayGrid[row][col] > 0 ? " @" : " .");
|
||||
// else if (col == endCol && row == endRow)
|
||||
// text.append (displayGrid[row][col] > 0 ? " #" : " .");
|
||||
else if (displayGrid[row][col] == 0)
|
||||
text.append (" ");
|
||||
else
|
||||
text.append (" X");
|
||||
|
||||
text.append ("\n");
|
||||
}
|
||||
|
||||
text.append ("\n");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private List<String> split (String line)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<String> list = new ArrayList<> ();
|
||||
while (line.length () > 48)
|
||||
{
|
||||
list.add (line.substring (0, 47));
|
||||
line = line.substring (48);
|
||||
}
|
||||
list.add (line);
|
||||
return list;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String toString ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return String.format ("%3d %3d %3d %3d %3d", index, minRow, maxRow, minCol,
|
||||
maxCol);
|
||||
}
|
||||
}
|
|
@ -3,11 +3,9 @@ package com.bytezone.diskbrowser.applefile;
|
|||
import java.awt.AlphaComposite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
/*-
|
||||
|
@ -27,7 +25,6 @@ public class ShapeTable extends AbstractFile
|
|||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private final List<Shape> shapes = new ArrayList<> ();
|
||||
private static final int SIZE = 400;
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
|
||||
|
@ -131,7 +128,7 @@ public class ShapeTable extends AbstractFile
|
|||
return false;
|
||||
|
||||
// check index points inside the file
|
||||
int offset = Utility.unsignedShort (buffer, ptr);
|
||||
int offset = Utility.getShort (buffer, ptr);
|
||||
if (offset == 0 || offset >= buffer.length)
|
||||
return false;
|
||||
|
||||
|
@ -142,216 +139,4 @@ public class ShapeTable extends AbstractFile
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
class Shape
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
private final byte[] buffer;
|
||||
private final int index;
|
||||
|
||||
int offset;
|
||||
int actualLength;
|
||||
int minRow, maxRow;
|
||||
int minCol, maxCol;
|
||||
int startRow = SIZE / 2;
|
||||
int startCol = SIZE / 2;
|
||||
int[][] grid = new int[SIZE][SIZE];
|
||||
int[][] displayGrid;
|
||||
boolean valid;
|
||||
|
||||
private BufferedImage image;
|
||||
|
||||
public Shape (byte[] buffer, int index)
|
||||
{
|
||||
this.index = index;
|
||||
this.buffer = buffer;
|
||||
|
||||
int row = startRow;
|
||||
int col = startCol;
|
||||
|
||||
offset = Utility.unsignedShort (buffer, index * 2 + 2);
|
||||
|
||||
int ptr = offset;
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
int value = buffer[ptr++] & 0xFF;
|
||||
|
||||
if (value == 0)
|
||||
break;
|
||||
|
||||
// P = plot
|
||||
// DD = direction to move
|
||||
int v1 = value >>> 6; // DD......
|
||||
int v2 = (value & 0x38) >>> 3; // ..PDD...
|
||||
int v3 = value & 0x07; // .....PDD
|
||||
|
||||
// rightmost 3 bits
|
||||
if (v3 >= 4)
|
||||
if (!plot (grid, row, col))
|
||||
return;
|
||||
|
||||
if (v3 == 0 || v3 == 4)
|
||||
row--;
|
||||
else if (v3 == 1 || v3 == 5)
|
||||
col++;
|
||||
else if (v3 == 2 || v3 == 6)
|
||||
row++;
|
||||
else
|
||||
col--;
|
||||
|
||||
// middle 3 bits
|
||||
if (v2 >= 4)
|
||||
if (!plot (grid, row, col))
|
||||
return;
|
||||
|
||||
// cannot move up without plotting if v1 is zero
|
||||
if ((v2 == 0 && v1 != 0) || v2 == 4)
|
||||
row--;
|
||||
else if (v2 == 1 || v2 == 5)
|
||||
col++;
|
||||
else if (v2 == 2 || v2 == 6)
|
||||
row++;
|
||||
else if (v2 == 3 || v2 == 7)
|
||||
col--;
|
||||
|
||||
// leftmost 2 bits (cannot plot or move up)
|
||||
if (v1 == 1)
|
||||
col++;
|
||||
else if (v1 == 2)
|
||||
row++;
|
||||
else if (v1 == 3)
|
||||
col--;
|
||||
}
|
||||
|
||||
actualLength = ptr - offset;
|
||||
|
||||
// endRow = row;
|
||||
// endCol = col;
|
||||
|
||||
// find min and max rows with pixels
|
||||
minRow = startRow;
|
||||
maxRow = startRow;
|
||||
// minRow = Math.min (minRow, endRow);
|
||||
// maxRow = Math.max (maxRow, endRow);
|
||||
for (row = 1; row < grid.length; row++)
|
||||
{
|
||||
if (grid[row][0] > 0)
|
||||
{
|
||||
minRow = Math.min (minRow, row);
|
||||
maxRow = Math.max (maxRow, row);
|
||||
}
|
||||
}
|
||||
|
||||
// find min and max columns with pixels
|
||||
minCol = startCol;
|
||||
maxCol = startCol;
|
||||
// minCol = Math.min (minCol, endCol);
|
||||
// maxCol = Math.max (maxCol, endCol);
|
||||
for (col = 1; col < grid[0].length; col++)
|
||||
{
|
||||
if (grid[0][col] > 0)
|
||||
{
|
||||
minCol = Math.min (minCol, col);
|
||||
maxCol = Math.max (maxCol, col);
|
||||
}
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
|
||||
void convertGrid (int offsetRows, int offsetColumns, int rows, int columns)
|
||||
{
|
||||
// System.out.printf ("Converting shape # %d%n", index);
|
||||
// System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows,
|
||||
// offsetColumns);
|
||||
// System.out.printf ("rows %d cols %d%n", rows, columns);
|
||||
|
||||
displayGrid = new int[rows][columns];
|
||||
for (int row = 0; row < rows; row++)
|
||||
for (int col = 0; col < columns; col++)
|
||||
displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col];
|
||||
grid = null;
|
||||
|
||||
// draw the image
|
||||
image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY);
|
||||
DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
|
||||
int element = 0;
|
||||
for (int row = 0; row < rows; row++)
|
||||
for (int col = 0; col < columns; col++)
|
||||
dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255);
|
||||
|
||||
startRow -= offsetRows;
|
||||
startCol -= offsetColumns;
|
||||
// endRow -= offsetRows;
|
||||
// endCol -= offsetColumns;
|
||||
}
|
||||
|
||||
private boolean plot (int[][] grid, int row, int col)
|
||||
{
|
||||
if (row < 0 || row >= SIZE || col < 0 || col >= SIZE)
|
||||
{
|
||||
System.out.printf ("Shape table out of range: %d, %d%n", row, col);
|
||||
return false;
|
||||
}
|
||||
grid[row][col] = 1; // plot
|
||||
grid[0][col]++; // increment total column dots
|
||||
grid[row][0]++; // increment total row dots
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void drawText (StringBuilder text)
|
||||
{
|
||||
text.append (String.format ("Shape : %d%n", index));
|
||||
text.append (String.format ("Size : %d%n", actualLength));
|
||||
// text.append (String.format ("Width : %d%n", width));
|
||||
// text.append (String.format ("Height : %d%n", height));
|
||||
|
||||
// append the shape's data
|
||||
String bytes = HexFormatter.getHexString (buffer, offset, actualLength);
|
||||
int ptr = offset;
|
||||
for (String s : split (bytes))
|
||||
{
|
||||
text.append (String.format (" %04X : %s%n", ptr, s));
|
||||
ptr += 16;
|
||||
}
|
||||
text.append ("\n");
|
||||
|
||||
for (int row = 0; row < displayGrid.length; row++)
|
||||
{
|
||||
for (int col = 0; col < displayGrid[0].length; col++)
|
||||
if (col == startCol && row == startRow)
|
||||
text.append (displayGrid[row][col] > 0 ? " @" : " .");
|
||||
// else if (col == endCol && row == endRow)
|
||||
// text.append (displayGrid[row][col] > 0 ? " #" : " .");
|
||||
else if (displayGrid[row][col] == 0)
|
||||
text.append (" ");
|
||||
else
|
||||
text.append (" X");
|
||||
|
||||
text.append ("\n");
|
||||
}
|
||||
|
||||
text.append ("\n");
|
||||
}
|
||||
|
||||
private List<String> split (String line)
|
||||
{
|
||||
List<String> list = new ArrayList<> ();
|
||||
while (line.length () > 48)
|
||||
{
|
||||
list.add (line.substring (0, 47));
|
||||
line = line.substring (48);
|
||||
}
|
||||
list.add (line);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return String.format ("%3d %3d %3d %3d %3d", index, minRow, maxRow, minCol,
|
||||
maxCol);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,13 @@ public class SimpleText extends AbstractFile
|
|||
if (ptr < buffer.length && buffer[ptr] == 0x0A)
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (resourceFork != null)
|
||||
{
|
||||
text.append ("\n\nResource Fork\n=============\n");
|
||||
text.append (resourceFork);
|
||||
}
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
|
|
99
src/com/bytezone/diskbrowser/applefile/SourceLine.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COLON;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_QUOTE;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class SourceLine implements ApplesoftConstants
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
ApplesoftBasicProgram program;
|
||||
int linkField;
|
||||
int lineNumber;
|
||||
int linePtr;
|
||||
int length;
|
||||
byte[] buffer;
|
||||
|
||||
List<SubLine> sublines = new ArrayList<> ();
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
SourceLine (ApplesoftBasicProgram program, byte[] buffer, int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.program = program;
|
||||
this.buffer = buffer;
|
||||
|
||||
linePtr = ptr;
|
||||
linkField = getShort (buffer, ptr);
|
||||
lineNumber = getShort (buffer, ptr + 2);
|
||||
|
||||
int startPtr = ptr += 4; // skip link field and lineNumber
|
||||
boolean inString = false; // can toggle
|
||||
boolean inRemark = false; // can only go false -> true
|
||||
byte b;
|
||||
|
||||
while (ptr < buffer.length && (b = buffer[ptr++]) != 0)
|
||||
{
|
||||
if (inRemark) // cannot terminate a REM
|
||||
continue;
|
||||
|
||||
if (inString)
|
||||
{
|
||||
if (b == ASCII_QUOTE) // terminate string
|
||||
inString = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (b)
|
||||
{
|
||||
// break IF statements into two sublines (allows for easier line indenting)
|
||||
case TOKEN_IF:
|
||||
while (buffer[ptr] != TOKEN_THEN && buffer[ptr] != TOKEN_GOTO
|
||||
&& buffer[ptr] != 0)
|
||||
ptr++;
|
||||
|
||||
if (buffer[ptr] == TOKEN_THEN) // keep THEN with the IF
|
||||
++ptr;
|
||||
|
||||
startPtr = addSubLine (startPtr, ptr); // create subline from the condition
|
||||
break;
|
||||
|
||||
case ASCII_COLON: // end of subline
|
||||
startPtr = addSubLine (startPtr, ptr);
|
||||
break;
|
||||
|
||||
case TOKEN_REM:
|
||||
if (ptr == startPtr + 1) // at start of line
|
||||
inRemark = true;
|
||||
else // mid-line - should be illegal
|
||||
{
|
||||
System.out.printf ("%s : %5d mid-line REM token%n", program.name, lineNumber);
|
||||
startPtr = addSubLine (startPtr, --ptr); // point back to the REM
|
||||
}
|
||||
break;
|
||||
|
||||
case Utility.ASCII_QUOTE:
|
||||
inString = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
length = ptr - linePtr;
|
||||
|
||||
addSubLine (startPtr, ptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int addSubLine (int startPtr, int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
|
||||
return ptr;
|
||||
}
|
||||
}
|
|
@ -26,10 +26,10 @@ public class StoredVariables extends AbstractFile
|
|||
int strPtr = buffer.length;
|
||||
|
||||
text.append ("File length : " + HexFormatter.format4 (buffer.length));
|
||||
int totalLength = Utility.intValue (buffer[0], buffer[1]);
|
||||
int totalLength = Utility.getShort (buffer, 0);
|
||||
text.append ("\nTotal length : " + HexFormatter.format4 (totalLength));
|
||||
|
||||
int varLength = Utility.intValue (buffer[2], buffer[3]);
|
||||
int varLength = Utility.getShort (buffer, 2);
|
||||
text.append ("\nVar length : " + HexFormatter.format4 (varLength));
|
||||
text.append ("\n\n");
|
||||
|
||||
|
@ -51,8 +51,8 @@ public class StoredVariables extends AbstractFile
|
|||
}
|
||||
else if (suffix == '%')
|
||||
{
|
||||
intValue = Utility.intValue (buffer[ptr + 3], buffer[ptr + 2]);
|
||||
if ((buffer[ptr + 2] & 0x80) > 0)
|
||||
intValue = Utility.intValue (buffer[ptr + 3], buffer[ptr + 2]); // backwards!
|
||||
if ((buffer[ptr + 2] & 0x80) != 0)
|
||||
intValue -= 65536;
|
||||
text.append (" = " + intValue);
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class StoredVariables extends AbstractFile
|
|||
{
|
||||
if (hasValue (ptr + 2))
|
||||
{
|
||||
String value = HexFormatter.floatValue (buffer, ptr + 2) + "";
|
||||
String value = Utility.floatValue (buffer, ptr + 2) + "";
|
||||
if (value.endsWith (".0"))
|
||||
text.append (" = " + value.substring (0, value.length () - 2));
|
||||
else
|
||||
|
@ -101,7 +101,7 @@ public class StoredVariables extends AbstractFile
|
|||
suffix = ' ';
|
||||
}
|
||||
|
||||
StringBuffer variableName = new StringBuffer ();
|
||||
StringBuilder variableName = new StringBuilder ();
|
||||
variableName.append (c1);
|
||||
if (c2 > 32)
|
||||
variableName.append (c2);
|
||||
|
@ -116,12 +116,14 @@ public class StoredVariables extends AbstractFile
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ("(");
|
||||
|
||||
for (int i = 0; i < values.length; i++)
|
||||
{
|
||||
text.append (values[i]);
|
||||
if (i < values.length - 1)
|
||||
text.append (',');
|
||||
}
|
||||
|
||||
return text.append (')').toString ();
|
||||
}
|
||||
|
||||
|
@ -133,14 +135,14 @@ public class StoredVariables extends AbstractFile
|
|||
{
|
||||
String variableName = getVariableName (buffer[ptr], buffer[ptr + 1]);
|
||||
text.append ("\n");
|
||||
int offset = Utility.intValue (buffer[ptr + 2], buffer[ptr + 3]);
|
||||
int offset = Utility.getShort (buffer, ptr + 2);
|
||||
int dimensions = buffer[ptr + 4] & 0xFF;
|
||||
int[] dimensionSizes = new int[dimensions];
|
||||
int totalElements = 0;
|
||||
for (int i = 0; i < dimensions; i++)
|
||||
{
|
||||
int p = i * 2 + 5 + ptr;
|
||||
int elements = Utility.intValue (buffer[p + 1], buffer[p]);
|
||||
int elements = Utility.intValue (buffer[p + 1], buffer[p]); // backwards!
|
||||
dimensionSizes[dimensions - i - 1] = elements - 1;
|
||||
if (totalElements == 0)
|
||||
totalElements = elements;
|
||||
|
@ -160,7 +162,7 @@ public class StoredVariables extends AbstractFile
|
|||
text.append (variableName + " " + getDimensionText (values) + " = ");
|
||||
if (elementSize == 2)
|
||||
{
|
||||
int intValue = Utility.intValue (buffer[p + 1], buffer[p]);
|
||||
int intValue = Utility.intValue (buffer[p + 1], buffer[p]); // backwards
|
||||
if ((buffer[p] & 0x80) > 0)
|
||||
intValue -= 65536;
|
||||
text.append (intValue + "\n");
|
||||
|
@ -178,7 +180,7 @@ public class StoredVariables extends AbstractFile
|
|||
else if (elementSize == 5)
|
||||
{
|
||||
if (hasValue (p))
|
||||
text.append (HexFormatter.floatValue (buffer, p));
|
||||
text.append (Utility.floatValue (buffer, p));
|
||||
text.append ("\n");
|
||||
}
|
||||
p += elementSize;
|
||||
|
@ -201,6 +203,7 @@ public class StoredVariables extends AbstractFile
|
|||
for (int i = 0; i < 5; i++)
|
||||
if (buffer[p + i] != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -212,10 +215,10 @@ public class StoredVariables extends AbstractFile
|
|||
StringBuffer text = new StringBuffer ();
|
||||
|
||||
text.append ("File length : " + HexFormatter.format4 (buffer.length));
|
||||
int totalLength = Utility.intValue (buffer[0], buffer[1]);
|
||||
int totalLength = Utility.getShort (buffer, 0);
|
||||
text.append ("\nTotal length : " + HexFormatter.format4 (totalLength));
|
||||
|
||||
int varLength = Utility.intValue (buffer[2], buffer[3]);
|
||||
int varLength = Utility.getShort (buffer, 2);
|
||||
text.append ("\nVar length : " + HexFormatter.format4 (varLength));
|
||||
|
||||
int unknown = buffer[4] & 0xFF;
|
||||
|
@ -232,14 +235,14 @@ public class StoredVariables extends AbstractFile
|
|||
text.append ("\nArrays : \n\n");
|
||||
while (ptr < totalLength + 5)
|
||||
{
|
||||
int offset = Utility.intValue (buffer[ptr + 2], buffer[ptr + 3]);
|
||||
int offset = Utility.getShort (buffer, ptr + 2);
|
||||
int dimensions = buffer[ptr + 4] & 0xFF;
|
||||
int[] dimensionSizes = new int[dimensions];
|
||||
int totalElements = 0;
|
||||
for (int i = 0; i < dimensions; i++)
|
||||
{
|
||||
int p = i * 2 + 5 + ptr;
|
||||
int elements = Utility.intValue (buffer[p + 1], buffer[p]);
|
||||
int elements = Utility.intValue (buffer[p + 1], buffer[p]); // backwards!
|
||||
dimensionSizes[dimensions - i - 1] = elements;
|
||||
if (totalElements == 0)
|
||||
totalElements = elements;
|
||||
|
|
734
src/com/bytezone/diskbrowser/applefile/SubLine.java
Normal file
|
@ -0,0 +1,734 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_BACKSPACE;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COLON;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COMMA;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_CR;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_DOLLAR;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LEFT_BRACKET;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LF;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_MINUS;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_PERCENT;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_QUOTE;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_RIGHT_BRACKET;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.getIndent;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isControlCharacter;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isDigit;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isLetter;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleNumber;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleVariable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class SubLine implements ApplesoftConstants
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
SourceLine sourceLine;
|
||||
|
||||
byte[] buffer;
|
||||
int startPtr;
|
||||
int length;
|
||||
|
||||
String[] nextVariables;
|
||||
String forVariable = "";
|
||||
|
||||
int equalsPosition; // used for aligning the equals sign
|
||||
int endPosition; // not sure yet - possibly for aligning REMs
|
||||
|
||||
String functionArgument;
|
||||
String functionName;
|
||||
|
||||
String callTarget;
|
||||
|
||||
private final List<Integer> gotoLines = new ArrayList<> ();
|
||||
private final List<Integer> gosubLines = new ArrayList<> ();
|
||||
|
||||
private final List<String> variables = new ArrayList<> ();
|
||||
private final List<String> functions = new ArrayList<> ();
|
||||
private final List<String> arrays = new ArrayList<> ();
|
||||
|
||||
private final List<Integer> constantsInt = new ArrayList<> ();
|
||||
private final List<Float> constantsFloat = new ArrayList<> ();
|
||||
|
||||
private final List<String> stringsText = new ArrayList<> ();
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
SubLine (SourceLine sourceLine, int offset, int length)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.sourceLine = sourceLine;
|
||||
this.startPtr = offset;
|
||||
this.length = length;
|
||||
this.buffer = sourceLine.buffer;
|
||||
|
||||
int ptr = startPtr;
|
||||
byte firstByte = buffer[startPtr];
|
||||
|
||||
if (isToken (firstByte))
|
||||
{
|
||||
doToken (firstByte);
|
||||
if (is (TOKEN_REM) || is (TOKEN_DATA)) // no further processing
|
||||
return;
|
||||
|
||||
if (is (TOKEN_CALL))
|
||||
ptr = startPtr + callTarget.length ();
|
||||
else
|
||||
ptr = startPtr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDigit (firstByte)) // split IF xx THEN nnn
|
||||
{
|
||||
addXref (getLineNumber (buffer, startPtr), gotoLines);
|
||||
return;
|
||||
}
|
||||
else if (isLetter (firstByte)) // variable name
|
||||
setEqualsPosition ();
|
||||
else if (isEndOfLine (firstByte)) // empty subline
|
||||
return;
|
||||
else // probably Beagle Bros 0D or 0A
|
||||
System.out.printf ("%s unexpected bytes at line %5d:%n%s%n",
|
||||
sourceLine.program.name, sourceLine.lineNumber,
|
||||
HexFormatter.formatNoHeader (buffer, startPtr, length));
|
||||
}
|
||||
|
||||
String var = "";
|
||||
|
||||
boolean inQuote = false;
|
||||
boolean inFunction = false;
|
||||
boolean inDefine = false;
|
||||
int stringPtr = 0;
|
||||
|
||||
int endOfLine = startPtr + length - 1;
|
||||
while (isEndOfLine (buffer[endOfLine])) // zero or colon
|
||||
--endOfLine;
|
||||
|
||||
while (ptr <= endOfLine)
|
||||
{
|
||||
byte b = buffer[ptr++];
|
||||
|
||||
if (inDefine) // ignore the name and argument
|
||||
{
|
||||
if (b == TOKEN_EQUALS)
|
||||
inDefine = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b == TOKEN_DEF)
|
||||
{
|
||||
inDefine = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b == TOKEN_FN)
|
||||
{
|
||||
assert !inDefine;
|
||||
inFunction = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inQuote)
|
||||
{
|
||||
if (b == ASCII_QUOTE) // ignore strings
|
||||
{
|
||||
inQuote = false;
|
||||
addString (stringPtr, ptr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b == ASCII_QUOTE)
|
||||
{
|
||||
inQuote = true;
|
||||
stringPtr = ptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPossibleVariable (b) || isPossibleNumber (b))
|
||||
{
|
||||
if (var.isEmpty () && isPossibleNumber (b) && buffer[ptr - 2] == TOKEN_MINUS)
|
||||
var = "-";
|
||||
|
||||
var += (char) b;
|
||||
|
||||
// allow for PRINT A$B$
|
||||
if ((b == ASCII_DOLLAR || b == ASCII_PERCENT) // var name end
|
||||
&& buffer[ptr] != ASCII_LEFT_BRACKET) // not an array
|
||||
{
|
||||
checkVar (var, b);
|
||||
var = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inFunction)
|
||||
{
|
||||
checkFunction (var, b);
|
||||
inFunction = false;
|
||||
}
|
||||
else
|
||||
checkVar (var, b);
|
||||
|
||||
var = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (inQuote)
|
||||
addString (stringPtr, ptr); // unterminated string
|
||||
else
|
||||
checkVar (var, (byte) 0); // unprocessed variable or number
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean isEndOfLine (byte b)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return b == 0 || b == ASCII_COLON;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addString (int stringPtr, int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
stringsText.add (new String (buffer, stringPtr - 1, ptr - stringPtr + 1));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void checkFunction (String var, byte terminator)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// assert terminator == ASCII_LEFT_BRACKET;
|
||||
|
||||
if (!functions.contains (var))
|
||||
functions.add (var);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void checkVar (String var, byte terminator)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (var.length () == 0)
|
||||
return;
|
||||
|
||||
if (!isLetter ((byte) var.charAt (0)))
|
||||
{
|
||||
if (is (TOKEN_GOTO) || is (TOKEN_GOSUB) || is (TOKEN_ON) || is (TOKEN_ONERR))
|
||||
return; // ignore line numbers
|
||||
addNumber (var);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is (TOKEN_DEF) && (var.equals (functionName) || var.equals (functionArgument)))
|
||||
return;
|
||||
|
||||
if (terminator == ASCII_LEFT_BRACKET)
|
||||
{
|
||||
if (!arrays.contains (var))
|
||||
arrays.add (var);
|
||||
}
|
||||
else if (!variables.contains (var))
|
||||
variables.add (var);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void doToken (byte b)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
switch (b)
|
||||
{
|
||||
case TOKEN_FOR:
|
||||
int p = startPtr + 1;
|
||||
while (buffer[p] != TOKEN_EQUALS)
|
||||
forVariable += (char) buffer[p++];
|
||||
break;
|
||||
|
||||
case TOKEN_NEXT:
|
||||
if (length == 2) // no variables
|
||||
nextVariables = new String[0];
|
||||
else
|
||||
{
|
||||
String varList = new String (buffer, startPtr + 1, length - 2);
|
||||
nextVariables = varList.split (",");
|
||||
}
|
||||
break;
|
||||
|
||||
case TOKEN_LET:
|
||||
setEqualsPosition ();
|
||||
break;
|
||||
|
||||
case TOKEN_GOTO:
|
||||
int targetLine = getLineNumber (buffer, startPtr + 1);
|
||||
addXref (targetLine, gotoLines);
|
||||
break;
|
||||
|
||||
case TOKEN_GOSUB:
|
||||
targetLine = getLineNumber (buffer, startPtr + 1);
|
||||
addXref (targetLine, gosubLines);
|
||||
break;
|
||||
|
||||
case TOKEN_ON:
|
||||
p = startPtr + 1;
|
||||
int max = startPtr + length - 1;
|
||||
while (p < max && buffer[p] != TOKEN_GOTO && buffer[p] != TOKEN_GOSUB)
|
||||
p++;
|
||||
|
||||
switch (buffer[p++])
|
||||
{
|
||||
case TOKEN_GOSUB:
|
||||
for (int destLine : getLineNumbers (buffer, p))
|
||||
addXref (destLine, gosubLines);
|
||||
break;
|
||||
|
||||
case TOKEN_GOTO:
|
||||
for (int destLine : getLineNumbers (buffer, p))
|
||||
addXref (destLine, gotoLines);
|
||||
break;
|
||||
|
||||
default:
|
||||
System.out.println ("GOTO / GOSUB not found");
|
||||
}
|
||||
break;
|
||||
|
||||
case TOKEN_ONERR:
|
||||
if (buffer[startPtr + 1] == TOKEN_GOTO)
|
||||
{
|
||||
targetLine = getLineNumber (buffer, startPtr + 2);
|
||||
addXref (targetLine, gotoLines);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOKEN_CALL:
|
||||
callTarget = getCallTarget ();
|
||||
break;
|
||||
|
||||
case TOKEN_DEF:
|
||||
byte[] lineBuffer = getBuffer ();
|
||||
assert lineBuffer[0] == TOKEN_FN;
|
||||
|
||||
int leftBracket = getPosition (lineBuffer, 1, ASCII_LEFT_BRACKET);
|
||||
int rightBracket = getPosition (lineBuffer, leftBracket + 1, ASCII_RIGHT_BRACKET);
|
||||
|
||||
functionName = new String (lineBuffer, 1, leftBracket - 1);
|
||||
functionArgument =
|
||||
new String (lineBuffer, leftBracket + 1, rightBracket - leftBracket - 1);
|
||||
functions.add (functionName);
|
||||
|
||||
break;
|
||||
|
||||
case TOKEN_DATA:
|
||||
for (String chunk : new String (getBuffer ()).split (","))
|
||||
{
|
||||
chunk = chunk.trim ();
|
||||
if (chunk.isEmpty ())
|
||||
continue;
|
||||
|
||||
b = (byte) chunk.charAt (0);
|
||||
if (isPossibleNumber (b) || b == ASCII_MINUS)
|
||||
{
|
||||
if (!addNumber (chunk))
|
||||
stringsText.add (chunk);
|
||||
}
|
||||
else
|
||||
stringsText.add (chunk);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean addNumber (String var)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
try
|
||||
{
|
||||
if (var.indexOf ('.') < 0) // no decimal point
|
||||
{
|
||||
int varInt = Integer.parseInt (var);
|
||||
if (!constantsInt.contains (varInt))
|
||||
constantsInt.add (varInt);
|
||||
}
|
||||
else
|
||||
{
|
||||
float varFloat = Float.parseFloat (var);
|
||||
if (!constantsFloat.contains (varFloat))
|
||||
constantsFloat.add (varFloat);
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException nfe)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getCallTarget ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
int ptr = startPtr + 1;
|
||||
int max = startPtr + length - 1;
|
||||
|
||||
while (ptr < max)
|
||||
{
|
||||
byte b = buffer[ptr++];
|
||||
if (isToken (b))
|
||||
text.append (tokens[b & 0x7F]);
|
||||
else if (b == ASCII_COMMA) // end of call target
|
||||
break;
|
||||
else
|
||||
text.append ((char) b);
|
||||
}
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getPosition (byte[] buffer, int start, byte value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
for (int i = start; i < buffer.length; i++)
|
||||
if (buffer[i] == value)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addXref (int targetLine, List<Integer> list)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (!list.contains (targetLine))
|
||||
list.add (targetLine);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private List<Integer> getLineNumbers (byte[] buffer, int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<Integer> lineNumbers = new ArrayList<> ();
|
||||
int start = ptr;
|
||||
|
||||
while (ptr < buffer.length && buffer[ptr] != 0 && buffer[ptr] != ASCII_COLON)
|
||||
ptr++;
|
||||
|
||||
String s = new String (buffer, start, ptr - start);
|
||||
String[] chunks = s.split (",");
|
||||
|
||||
try
|
||||
{
|
||||
for (String chunk : chunks)
|
||||
lineNumbers.add (Integer.parseInt (chunk));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
System.out.printf ("NFE2: %s%n", s);
|
||||
}
|
||||
|
||||
return lineNumbers;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getLineNumber (byte[] buffer, int ptr)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int lineNumber = 0;
|
||||
|
||||
while (ptr < buffer.length && isDigit (buffer[ptr]))
|
||||
lineNumber = lineNumber * 10 + (buffer[ptr++] & 0xFF) - 0x30;
|
||||
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isImpliedGoto ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return (isDigit (buffer[startPtr]));
|
||||
}
|
||||
|
||||
// Record the position of the equals sign so it can be aligned with adjacent lines.
|
||||
// Illegal lines could have a variable name with no equals sign.
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void setEqualsPosition ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int p = startPtr;
|
||||
int max = startPtr + length;
|
||||
|
||||
while (++p < max)
|
||||
if (buffer[p] == TOKEN_EQUALS)
|
||||
{
|
||||
String expandedLine = toString ();
|
||||
equalsPosition = expandedLine.indexOf ('=');
|
||||
endPosition = expandedLine.length ();
|
||||
if (expandedLine.endsWith (":"))
|
||||
endPosition--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isJoinableRem ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return is (TOKEN_REM) && isNotFirst ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isFirst ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return (sourceLine.linePtr + 4) == startPtr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isNotFirst ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return !isFirst ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean is (byte token)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return buffer[startPtr] == token;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean has (byte token)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int ptr = startPtr;
|
||||
int max = startPtr + length;
|
||||
|
||||
while (++ptr < max)
|
||||
if (buffer[ptr] == token)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isEmpty ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return length == 1 && buffer[startPtr] == 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean containsToken ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// ignore first byte, check the rest for tokens
|
||||
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
|
||||
if (isToken (buffer[p]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean containsControlChars ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
|
||||
{
|
||||
int c = buffer[p] & 0xFF;
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
if (c < 32)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void addFormattedRem (StringBuilder text)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int ptr = startPtr + 1;
|
||||
int max = startPtr + length - 1;
|
||||
|
||||
if (isFirst ())
|
||||
{
|
||||
if (containsBackspaces (ptr, max)) // probably going to erase the line number
|
||||
{
|
||||
// apple format uses left-justified line numbers so the length varies
|
||||
text.setLength (0);
|
||||
text.append (String.format (" %d REM ", sourceLine.lineNumber)); // mimic apple
|
||||
}
|
||||
else
|
||||
text.append (" REM ");
|
||||
}
|
||||
else
|
||||
text.append ("REM ");
|
||||
|
||||
while (ptr < max)
|
||||
{
|
||||
switch (buffer[ptr])
|
||||
{
|
||||
case ASCII_BACKSPACE:
|
||||
if (text.length () > 0)
|
||||
text.deleteCharAt (text.length () - 1);
|
||||
break;
|
||||
|
||||
case ASCII_CR:
|
||||
text.append ("\n");
|
||||
break;
|
||||
|
||||
case ASCII_LF:
|
||||
int indent = getIndent (text);
|
||||
text.append ("\n");
|
||||
for (int i = 0; i < indent; i++)
|
||||
text.append (" ");
|
||||
break;
|
||||
|
||||
default:
|
||||
text.append ((char) buffer[ptr]); // do not mask with 0xFF
|
||||
}
|
||||
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean containsBackspaces (int ptr, int max)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
while (ptr < max)
|
||||
if (buffer[ptr++] == ASCII_BACKSPACE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public byte[] getBuffer ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int len = length - 1;
|
||||
if (buffer[startPtr + len] == ASCII_COLON || buffer[startPtr + len] == 0)
|
||||
len--;
|
||||
byte[] buffer2 = new byte[len];
|
||||
System.arraycopy (buffer, startPtr + 1, buffer2, 0, buffer2.length);
|
||||
|
||||
return buffer2;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
boolean isToken (byte b)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return isHighBitSet (b);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<String> getVariables ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<String> getFunctions ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return functions;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<String> getArrays ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return arrays;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<Integer> getGotoLines ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return gotoLines;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<Integer> getGosubLines ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return gosubLines;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<Integer> getConstantsInt ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return constantsInt;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<Float> getConstantsFloat ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return constantsFloat;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
List<String> getStringsText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return stringsText;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
StringBuilder toStringBuilder ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder line = new StringBuilder ();
|
||||
|
||||
// All sublines end with 0 or : except IF lines that are split into two
|
||||
int max = startPtr + length - 1;
|
||||
if (buffer[max] == 0)
|
||||
--max;
|
||||
|
||||
if (isImpliedGoto () && !ApplesoftBasicProgram.basicPreferences.showThen)
|
||||
line.append ("GOTO ");
|
||||
|
||||
for (int p = startPtr; p <= max; p++)
|
||||
{
|
||||
byte b = buffer[p];
|
||||
if (isToken (b))
|
||||
{
|
||||
if (line.length () > 0 && line.charAt (line.length () - 1) != ' ')
|
||||
line.append (' ');
|
||||
int val = b & 0x7F;
|
||||
if (b != TOKEN_THEN || ApplesoftBasicProgram.basicPreferences.showThen)
|
||||
line.append (ApplesoftConstants.tokens[val] + " ");
|
||||
}
|
||||
// else if (Utility.isControlCharacter (b))
|
||||
// line.append (ApplesoftBasicProgram.basicPreferences.showCaret
|
||||
// ? "^" + (char) (b + 64) : "?");
|
||||
else if (!isControlCharacter (b))
|
||||
line.append ((char) b);
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String toString ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return toStringBuilder ().toString ();
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ public class TextBuffer
|
|||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append ("Record length : " + reclen + "\n");
|
||||
text.append ("Buffer length : " + buffer.length + "\n");
|
||||
text.append ("First record : " + firstRecNo + "\n\n");
|
||||
text.append (HexFormatter.format (buffer, 0, buffer.length) + "\n");
|
||||
|
||||
|
|
351
src/com/bytezone/diskbrowser/applefile/UserBasicFormatter.java
Normal file
|
@ -0,0 +1,351 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_DOLLAR;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_PERCENT;
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.getIndent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.BasicPreferences;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class UserBasicFormatter extends BasicFormatter
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final Pattern dimPattern =
|
||||
Pattern.compile ("[A-Z][A-Z0-9]*[$%]?\\([0-9]+(,[0-9]+)*\\)[,:]?");
|
||||
private static final int INDENT_SIZE = 2;
|
||||
private static final String EIGHT_SPACES = " ";
|
||||
private static final String FOUR_SPACES = " ";
|
||||
private static boolean FORCE = true;
|
||||
private static boolean NO_FORCE = false;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public UserBasicFormatter (ApplesoftBasicProgram program,
|
||||
BasicPreferences basicPreferences)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (program, basicPreferences);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void append (StringBuilder fullText)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
boolean insertBlankLine = false;
|
||||
int baseOffset = 7; // 5 digit line number + 2 spaces
|
||||
|
||||
Stack<String> loopVariables = new Stack<> ();
|
||||
Alignment alignment = new Alignment ();
|
||||
|
||||
for (SourceLine line : sourceLines)
|
||||
{
|
||||
StringBuilder text = new StringBuilder (String.format ("%5d", (line.lineNumber)));
|
||||
|
||||
int indentLevel = loopVariables.size (); // each full line starts at the loop indent
|
||||
int ifIndent = 0; // IF statement(s) limit back indentation by NEXT
|
||||
|
||||
for (SubLine subline : line.sublines)
|
||||
{
|
||||
// Allow empty statements (caused by a single colon)
|
||||
if (subline.isEmpty ())
|
||||
continue;
|
||||
|
||||
// A REM statement might conceal an assembler routine
|
||||
// - see P.CREATE on Diags2E.DSK
|
||||
if (subline.is (TOKEN_REM) && subline.containsToken ())
|
||||
{
|
||||
int address = getLoadAddress () + subline.startPtr + 1; // skip the REM token
|
||||
fullText.append (text + String.format ("REM - Inline assembler @ $%02X (%d)%n",
|
||||
address, address));
|
||||
String padding = " ".substring (0, text.length () + 2);
|
||||
for (String asm : getRemAssembler (subline))
|
||||
fullText.append (padding + asm + NEWLINE);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Beagle Bros often have multiline REM statements
|
||||
if (subline.is (TOKEN_REM) && basicPreferences.formatRem
|
||||
&& subline.containsControlChars ())
|
||||
{
|
||||
subline.addFormattedRem (text);
|
||||
fullText.append (text + NEWLINE);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reduce the indent by each NEXT, but only as far as the IF indent allows
|
||||
if (subline.is (TOKEN_NEXT))
|
||||
{
|
||||
popLoopVariables (loopVariables, subline);
|
||||
indentLevel = Math.max (ifIndent, loopVariables.size ());
|
||||
}
|
||||
|
||||
// Are we joining REM lines with the previous subline?
|
||||
if (joinableRem (subline))
|
||||
{
|
||||
// Join this REM statement to the previous line, so no indenting
|
||||
fullText.deleteCharAt (fullText.length () - 1); // remove newline
|
||||
fullText.append (" ");
|
||||
}
|
||||
else // ... otherwise do all the indenting
|
||||
{
|
||||
// Align assign statements if required
|
||||
if (basicPreferences.alignAssign)
|
||||
alignEqualsPosition (subline, alignment);
|
||||
|
||||
int column = indentLevel * INDENT_SIZE + baseOffset;
|
||||
while (text.length () < column)
|
||||
text.append (" ");
|
||||
}
|
||||
|
||||
// Add the current text, then reset it
|
||||
String lineText = alignment.getAlignedText (subline);
|
||||
|
||||
if (subline.is (TOKEN_DATA) && basicPreferences.deleteExtraDataSpace)
|
||||
lineText = lineText.replaceFirst ("DATA +", "DATA "); // regex
|
||||
|
||||
// Check for a wrappable REM/DATA/DIM statement
|
||||
// (see SEA BATTLE on DISK283.DSK)
|
||||
int inset = Math.max (text.length (), getIndent (fullText)) + 1;
|
||||
if (subline.is (TOKEN_REM) && lineText.length () > basicPreferences.wrapRemAt)
|
||||
{
|
||||
List<String> lines =
|
||||
splitLine (lineText, basicPreferences.wrapRemAt, ' ', FORCE);
|
||||
addSplitLines (lines, text, inset);
|
||||
}
|
||||
else if (subline.is (TOKEN_DATA)
|
||||
&& lineText.length () > basicPreferences.wrapDataAt)
|
||||
{
|
||||
List<String> lines =
|
||||
splitLine (lineText, basicPreferences.wrapDataAt, ',', FORCE);
|
||||
addSplitLines (lines, text, inset);
|
||||
}
|
||||
else if (subline.is (TOKEN_PRINT)
|
||||
&& lineText.length () > basicPreferences.wrapPrintAt)
|
||||
{
|
||||
List<String> lines =
|
||||
splitLine (lineText, basicPreferences.wrapDataAt, ';', NO_FORCE);
|
||||
addSplitLines (lines, text, inset);
|
||||
}
|
||||
else if (subline.is (TOKEN_DIM) && basicPreferences.splitDim)
|
||||
{
|
||||
List<String> lines = splitDim (lineText);
|
||||
addSplitLines (lines, text, inset);
|
||||
}
|
||||
else
|
||||
text.append (lineText);
|
||||
|
||||
if (subline == alignment.lastSubLine)
|
||||
alignment.reset ();
|
||||
|
||||
fullText.append (text);
|
||||
fullText.append (NEWLINE);
|
||||
text.setLength (0);
|
||||
|
||||
// Calculate indent changes that take effect after the current subline
|
||||
if (subline.is (TOKEN_IF))
|
||||
ifIndent = ++indentLevel;
|
||||
else if (subline.is (TOKEN_FOR))
|
||||
{
|
||||
String latestLoopVar = loopVariables.size () > 0 ? loopVariables.peek () : "";
|
||||
if (!subline.forVariable.equals (latestLoopVar)) // don't add repeated loop
|
||||
{
|
||||
loopVariables.push (subline.forVariable);
|
||||
++indentLevel;
|
||||
}
|
||||
}
|
||||
else if (basicPreferences.blankAfterReturn && subline.is (TOKEN_RETURN)
|
||||
&& subline.isFirst ())
|
||||
insertBlankLine = true;
|
||||
}
|
||||
|
||||
if (insertBlankLine)
|
||||
{
|
||||
fullText.append (NEWLINE);
|
||||
insertBlankLine = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private List<String> splitLine (String line, int wrapLength, char breakChar,
|
||||
boolean force)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int spaceAt = 0;
|
||||
while (spaceAt < line.length () && line.charAt (spaceAt) != ' ')
|
||||
++spaceAt;
|
||||
String indent = spaceAt < 8 ? EIGHT_SPACES.substring (0, spaceAt + 1) : EIGHT_SPACES;
|
||||
|
||||
List<String> lines = new ArrayList<> ();
|
||||
|
||||
while (line.length () > wrapLength)
|
||||
{
|
||||
int breakAt = wrapLength - 1;
|
||||
while (breakAt > spaceAt && line.charAt (breakAt) != breakChar)
|
||||
--breakAt;
|
||||
|
||||
if (breakAt <= spaceAt)
|
||||
break;
|
||||
|
||||
lines.add (line.substring (0, breakAt + 1)); // keep breakChar at end
|
||||
line = indent + line.substring (breakAt + 1).trim ();
|
||||
}
|
||||
|
||||
if (force)
|
||||
while (line.length () > wrapLength) // no breakChars found
|
||||
{
|
||||
lines.add (line.substring (0, wrapLength));
|
||||
line = indent + line.substring (wrapLength);
|
||||
}
|
||||
|
||||
lines.add (line);
|
||||
return lines;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private List<String> splitDim (String line)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<String> lines = new ArrayList<> ();
|
||||
|
||||
Matcher m = dimPattern.matcher (line);
|
||||
|
||||
while (m.find ())
|
||||
lines.add (FOUR_SPACES + m.group ());
|
||||
|
||||
if (lines.size () > 0)
|
||||
lines.set (0, "DIM " + lines.get (0).trim ());
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addSplitLines (List<String> lines, StringBuilder text, int indent)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
boolean first = true;
|
||||
|
||||
for (String line : lines)
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
text.append (line);
|
||||
}
|
||||
else
|
||||
text.append (
|
||||
"\n ".substring (0, indent) + line);
|
||||
}
|
||||
|
||||
// Decide whether the current subline needs to be aligned on its equals sign. If so,
|
||||
// and the column hasn't been calculated, read ahead to find the highest position.
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void alignEqualsPosition (SubLine subline, Alignment alignment)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (subline.equalsPosition == 0)
|
||||
{
|
||||
alignment.reset ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (alignment.equalsPosition == 0)
|
||||
findHighest (subline, alignment);
|
||||
}
|
||||
|
||||
// The IF processing is so that any assignment that is being aligned doesn't continue
|
||||
// to the next full line (because the indentation has changed).
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void findHighest (SubLine startSubline, Alignment alignment)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
boolean started = false;
|
||||
alignment.setFirst (startSubline);
|
||||
|
||||
outerLoop: for (int i = sourceLines.indexOf (startSubline.sourceLine); i < sourceLines
|
||||
.size (); i++)
|
||||
{
|
||||
boolean precededByIf = false;
|
||||
|
||||
for (SubLine subline : sourceLines.get (i).sublines)
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
// Stop when we come to a subline without an equals sign (joinable REMs
|
||||
// can be ignored)
|
||||
if (subline.equalsPosition == 0 && !joinableRem (subline))
|
||||
break outerLoop;
|
||||
|
||||
if (subline.equalsPosition > 0)
|
||||
alignment.check (subline);
|
||||
}
|
||||
else if (subline == startSubline)
|
||||
started = true;
|
||||
else if (subline.is (TOKEN_IF))
|
||||
precededByIf = true;
|
||||
}
|
||||
|
||||
if (started && precededByIf) // sublines of IF have now finished
|
||||
break; // don't continue with following SourceLine
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean joinableRem (SubLine subline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return subline.isJoinableRem () && !basicPreferences.splitRem;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void popLoopVariables (Stack<String> loopVariables, SubLine subline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (subline.nextVariables.length == 0) // naked NEXT
|
||||
{
|
||||
if (loopVariables.size () > 0)
|
||||
loopVariables.pop ();
|
||||
}
|
||||
else
|
||||
for (String variable : subline.nextVariables) // e.g. NEXT X,Y,Z
|
||||
while (loopVariables.size () > 0)
|
||||
if (sameVariable (variable, loopVariables.pop ()))
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean sameVariable (String v1, String v2)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return getUniqueName (v1).equals (getUniqueName (v2));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getUniqueName (String symbol)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int ptr = symbol.length () - 1;
|
||||
|
||||
if (symbol.charAt (ptr) == ASCII_DOLLAR // string
|
||||
|| symbol.charAt (ptr) == ASCII_PERCENT) // integer
|
||||
ptr--;
|
||||
|
||||
return (ptr <= 1) ? symbol : symbol.substring (0, 2) + symbol.substring (ptr + 1);
|
||||
}
|
||||
|
||||
// A REM statement might conceal an assembler routine
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String[] getRemAssembler (SubLine subline)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
AssemblerProgram program = new AssemblerProgram ("REM assembler",
|
||||
subline.getBuffer (), getLoadAddress () + subline.startPtr + 1);
|
||||
|
||||
return program.getAssembler ().split (NEWLINE);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import com.bytezone.diskbrowser.visicalc.Sheet;
|
|||
public class VisicalcFile extends AbstractFile
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static boolean debug;
|
||||
private Sheet sheet;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -27,25 +26,11 @@ public class VisicalcFile extends AbstractFile
|
|||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append ("Visicalc : " + name + "\n\n");
|
||||
text.append (sheet.getTextDisplay (debug));
|
||||
text.append (sheet.getTextDisplay (showDebugText));
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static void setDefaultDebug (boolean value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
debug = value;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static void setDebug (boolean value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
debug = value;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static boolean isVisicalcFile (byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
460
src/com/bytezone/diskbrowser/applefile/XrefFormatter.java
Normal file
|
@ -0,0 +1,460 @@
|
|||
package com.bytezone.diskbrowser.applefile;
|
||||
|
||||
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleNumber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.BasicPreferences;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class XrefFormatter extends BasicFormatter
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final String underline =
|
||||
"----------------------------------------------------"
|
||||
+ "----------------------------------------------";
|
||||
|
||||
private final Map<Integer, List<Integer>> gotoLines = new TreeMap<> ();
|
||||
private final Map<Integer, List<Integer>> gosubLines = new TreeMap<> ();
|
||||
private final Map<Integer, List<Integer>> constantsInt = new TreeMap<> ();
|
||||
private final Map<Float, List<Integer>> constantsFloat = new TreeMap<> ();
|
||||
|
||||
private final Map<String, List<Integer>> callLines = new TreeMap<> ();
|
||||
private final Map<String, List<Integer>> symbolLines = new TreeMap<> ();
|
||||
private final Map<String, List<Integer>> functionLines = new TreeMap<> ();
|
||||
private final Map<String, List<Integer>> arrayLines = new TreeMap<> ();
|
||||
|
||||
private final Map<String, List<String>> uniqueSymbols = new TreeMap<> ();
|
||||
private final Map<String, List<String>> uniqueArrays = new TreeMap<> ();
|
||||
|
||||
private final List<Integer> stringsLine = new ArrayList<> ();
|
||||
private final List<String> stringsText = new ArrayList<> ();
|
||||
|
||||
private final String formatLeft;
|
||||
private final String formatLineNumber;
|
||||
private final String formatRight;
|
||||
|
||||
private final int longestVarName;
|
||||
|
||||
private final int maxDigits;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public XrefFormatter (ApplesoftBasicProgram program, BasicPreferences basicPreferences)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (program, basicPreferences);
|
||||
|
||||
for (SourceLine sourceLine : program.getSourceLines ())
|
||||
checkXref (sourceLine);
|
||||
|
||||
longestVarName = getLongestName ();
|
||||
maxDigits = getMaxDigits ();
|
||||
|
||||
// build format strings based on existing line numbers and variable names
|
||||
formatLeft = longestVarName > 7 ? "%-" + longestVarName + "." + longestVarName + "s "
|
||||
: "%-7.7s ";
|
||||
formatRight = formatLeft.replace ("-", "");
|
||||
formatLineNumber = "%" + maxDigits + "d ";
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void checkXref (SourceLine line)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
for (SubLine subline : line.sublines)
|
||||
{
|
||||
for (String symbol : subline.getVariables ())
|
||||
checkVar (symbol, line.lineNumber, symbolLines, uniqueSymbols);
|
||||
for (String symbol : subline.getArrays ())
|
||||
checkVar (symbol, line.lineNumber, arrayLines, uniqueArrays);
|
||||
for (String symbol : subline.getFunctions ())
|
||||
checkFunction (line.lineNumber, symbol);
|
||||
for (int targetLine : subline.getGosubLines ())
|
||||
addNumberInt (line.lineNumber, targetLine, gosubLines);
|
||||
for (int targetLine : subline.getGotoLines ())
|
||||
addNumberInt (line.lineNumber, targetLine, gotoLines);
|
||||
for (int num : subline.getConstantsInt ())
|
||||
addNumberInt (line.lineNumber, num, constantsInt);
|
||||
for (float num : subline.getConstantsFloat ())
|
||||
addNumberFloat (line.lineNumber, num, constantsFloat);
|
||||
if (subline.callTarget != null)
|
||||
addString (line.lineNumber, subline.callTarget, callLines);
|
||||
for (String s : subline.getStringsText ())
|
||||
{
|
||||
stringsText.add (s);
|
||||
stringsLine.add (line.lineNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void append (StringBuilder fullText)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (basicPreferences.showSymbols)
|
||||
{
|
||||
if (!symbolLines.isEmpty ())
|
||||
showSymbolsLeft (fullText, symbolLines, "Var");
|
||||
|
||||
if (!arrayLines.isEmpty ())
|
||||
showSymbolsLeft (fullText, arrayLines, "Array");
|
||||
}
|
||||
|
||||
if (basicPreferences.showDuplicateSymbols)
|
||||
{
|
||||
if (!uniqueSymbols.isEmpty ())
|
||||
showDuplicates (fullText, uniqueSymbols, "Var");
|
||||
|
||||
if (!uniqueArrays.isEmpty ())
|
||||
showDuplicates (fullText, uniqueArrays, "Array");
|
||||
}
|
||||
|
||||
if (basicPreferences.showFunctions && !functionLines.isEmpty ())
|
||||
showSymbolsLeft (fullText, functionLines, "Fnction");
|
||||
|
||||
if (basicPreferences.showConstants)
|
||||
{
|
||||
if (!constantsInt.isEmpty ())
|
||||
showSymbolsRightInt (fullText, constantsInt, "Integer");
|
||||
|
||||
if (!constantsFloat.isEmpty ())
|
||||
showSymbolsRightFloat (fullText, constantsFloat, "Float");
|
||||
|
||||
if (stringsLine.size () > 0)
|
||||
{
|
||||
heading (fullText, formatRight, "Line", "String");
|
||||
for (int i = 0; i < stringsLine.size (); i++)
|
||||
fullText.append (String.format (formatRight + "%s%n", stringsLine.get (i),
|
||||
stringsText.get (i)));
|
||||
}
|
||||
}
|
||||
|
||||
if (basicPreferences.showGosubGoto)
|
||||
{
|
||||
if (!gosubLines.isEmpty ())
|
||||
showSymbolsRight (fullText, gosubLines, "GOSUB");
|
||||
|
||||
if (!gotoLines.isEmpty ())
|
||||
showSymbolsRight (fullText, gotoLines, "GOTO");
|
||||
}
|
||||
|
||||
if (basicPreferences.showCalls && !callLines.isEmpty ())
|
||||
showSymbolsLeftRight (fullText, callLines, " CALL");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getMaxDigits ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (sourceLines.size () == 0)
|
||||
return 4; // anything non-zero
|
||||
|
||||
SourceLine lastLine = sourceLines.get (sourceLines.size () - 1);
|
||||
return (lastLine.lineNumber + "").length ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getLongestName ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int longestName = getLongestName (symbolLines, 0);
|
||||
longestName = getLongestName (arrayLines, longestName);
|
||||
longestName = getLongestName (functionLines, longestName);
|
||||
|
||||
return longestName;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getLongestName (Map<String, List<Integer>> map, int longestName)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
for (String symbol : map.keySet ())
|
||||
if (symbol.length () > longestName)
|
||||
longestName = symbol.length ();
|
||||
|
||||
return longestName;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void heading (StringBuilder fullText, String format, String... heading)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (fullText.charAt (fullText.length () - 2) != '\n')
|
||||
fullText.append (NEWLINE);
|
||||
|
||||
fullText.append (String.format (format, underline));
|
||||
fullText.append (underline);
|
||||
fullText.append (NEWLINE);
|
||||
|
||||
fullText.append (String.format (format, heading[0]));
|
||||
if (heading.length == 1)
|
||||
fullText.append ("Line numbers");
|
||||
else
|
||||
fullText.append (heading[1]);
|
||||
|
||||
fullText.append (NEWLINE);
|
||||
fullText.append (String.format (format, underline));
|
||||
fullText.append (underline);
|
||||
fullText.append (NEWLINE);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void showDuplicates (StringBuilder fullText, Map<String, List<String>> map,
|
||||
String heading)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
boolean headingShown = false;
|
||||
for (String key : map.keySet ())
|
||||
{
|
||||
List<String> usage = map.get (key);
|
||||
if (usage.size () > 1)
|
||||
{
|
||||
if (!headingShown)
|
||||
{
|
||||
headingShown = true;
|
||||
heading (fullText, formatLeft, heading, "Duplicate Names");
|
||||
}
|
||||
|
||||
String line = usage.toString ();
|
||||
line = line.substring (1, line.length () - 1);
|
||||
fullText.append (String.format ("%-6s %s%n", key, line));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void showSymbolsLeft (StringBuilder fullText, Map<String, List<Integer>> map,
|
||||
String heading)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
heading (fullText, formatLeft, heading);
|
||||
|
||||
for (String symbol : map.keySet ()) // left-justify strings
|
||||
{
|
||||
if (symbol.length () <= 7)
|
||||
appendLineNumbers (fullText, String.format (formatLeft, symbol),
|
||||
map.get (symbol));
|
||||
else
|
||||
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void showSymbolsLeftRight (StringBuilder fullText,
|
||||
Map<String, List<Integer>> map, String heading)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
heading (fullText, formatLeft, heading);
|
||||
|
||||
for (String symbol : map.keySet ()) // left-justify strings
|
||||
{
|
||||
if (isNumeric (symbol))
|
||||
appendLineNumbers (fullText, String.format (formatRight, symbol),
|
||||
map.get (symbol));
|
||||
else if (symbol.length () <= 7)
|
||||
appendLineNumbers (fullText, String.format (formatLeft, symbol),
|
||||
map.get (symbol));
|
||||
else
|
||||
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void showSymbolsRight (StringBuilder fullText, Map<Integer, List<Integer>> map,
|
||||
String heading)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
heading (fullText, formatRight, heading);
|
||||
|
||||
for (Integer symbol : map.keySet ()) // right-justify integers
|
||||
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void showSymbolsRightInt (StringBuilder fullText,
|
||||
Map<Integer, List<Integer>> map, String heading)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
heading (fullText, formatRight, heading);
|
||||
|
||||
for (int symbol : map.keySet ()) // right-justify integers
|
||||
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void showSymbolsRightFloat (StringBuilder fullText,
|
||||
Map<Float, List<Integer>> map, String heading)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
heading (fullText, formatRight, heading);
|
||||
|
||||
for (float symbol : map.keySet ()) // right-justify integers
|
||||
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private boolean isNumeric (String value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
byte[] bytes = value.getBytes ();
|
||||
int start = value.charAt (0) == Utility.ASCII_MINUS ? 1 : 0;
|
||||
for (int i = start; i < bytes.length; i++)
|
||||
if (!isPossibleNumber (bytes[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void appendLineNumbers (StringBuilder fullText, String symbol,
|
||||
List<Integer> lineNumbers)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
text.append (symbol);
|
||||
|
||||
for (int lineNo : lineNumbers)
|
||||
{
|
||||
if (text.length () > underline.length () - maxDigits + longestVarName)
|
||||
{
|
||||
fullText.append (text);
|
||||
fullText.append (NEWLINE);
|
||||
text.setLength (0);
|
||||
text.append (String.format (formatRight, ""));
|
||||
}
|
||||
text.append (String.format (formatLineNumber, lineNo));
|
||||
}
|
||||
|
||||
if (text.length () > longestVarName + 3)
|
||||
fullText.append (text + "\n");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void checkVar (String var, int lineNumber, Map<String, List<Integer>> map,
|
||||
Map<String, List<String>> unique)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<Integer> lines = map.get (var);
|
||||
if (lines == null)
|
||||
{
|
||||
lines = new ArrayList<> ();
|
||||
map.put (var, lines);
|
||||
}
|
||||
|
||||
if (lines.size () == 0)
|
||||
lines.add (lineNumber);
|
||||
else
|
||||
{
|
||||
int lastLine = lines.get (lines.size () - 1);
|
||||
if (lastLine != lineNumber)
|
||||
lines.add (lineNumber);
|
||||
}
|
||||
|
||||
checkUniqueName (var, unique);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void checkFunction (int sourceLine, String var)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<Integer> lines = functionLines.get (var);
|
||||
if (lines == null)
|
||||
{
|
||||
lines = new ArrayList<> ();
|
||||
functionLines.put (var, lines);
|
||||
}
|
||||
|
||||
addLine (lines, sourceLine);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addNumberInt (int sourceLine, Integer key, Map<Integer, List<Integer>> map)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<Integer> lines = map.get (key);
|
||||
if (lines == null)
|
||||
{
|
||||
lines = new ArrayList<> ();
|
||||
map.put (key, lines);
|
||||
}
|
||||
|
||||
addLine (lines, sourceLine);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addNumberFloat (int sourceLine, Float key, Map<Float, List<Integer>> map)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<Integer> lines = map.get (key);
|
||||
if (lines == null)
|
||||
{
|
||||
lines = new ArrayList<> ();
|
||||
map.put (key, lines);
|
||||
}
|
||||
|
||||
addLine (lines, sourceLine);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addString (int sourceLine, String key, Map<String, List<Integer>> map)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<Integer> lines = map.get (key);
|
||||
if (lines == null)
|
||||
{
|
||||
lines = new ArrayList<> ();
|
||||
map.put (key, lines);
|
||||
}
|
||||
|
||||
addLine (lines, sourceLine);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void addLine (List<Integer> lines, int lineNumber)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (lines.size () == 0)
|
||||
lines.add (lineNumber);
|
||||
else
|
||||
{
|
||||
int lastLine = lines.get (lines.size () - 1);
|
||||
if (lastLine != lineNumber)
|
||||
lines.add (lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void checkUniqueName (String symbol, Map<String, List<String>> map)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
String uniqueName = getUniqueName (symbol);
|
||||
|
||||
List<String> usage = map.get (uniqueName);
|
||||
if (usage == null)
|
||||
{
|
||||
usage = new ArrayList<> ();
|
||||
map.put (uniqueName, usage);
|
||||
}
|
||||
|
||||
if (!usage.contains (symbol))
|
||||
usage.add (symbol);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getUniqueName (String symbolName)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int ptr = symbolName.length () - 1;
|
||||
|
||||
if (symbolName.charAt (ptr) == Utility.ASCII_DOLLAR // string
|
||||
|| symbolName.charAt (ptr) == Utility.ASCII_PERCENT) // integer
|
||||
ptr--;
|
||||
|
||||
return (ptr <= 1) ? symbolName
|
||||
: symbolName.substring (0, 2) + symbolName.substring (ptr + 1);
|
||||
}
|
||||
}
|
|
@ -22,10 +22,19 @@
|
|||
0035 YSAV1
|
||||
0036 CSWL
|
||||
0037 CSHW
|
||||
0044 A5L - volume number?
|
||||
003D A1H
|
||||
003C A1L
|
||||
003F A2H
|
||||
003E A2L
|
||||
0041 A3H
|
||||
0040 A3L
|
||||
0043 A4H
|
||||
0042 A4L
|
||||
0045 A5H
|
||||
0044 A5L
|
||||
004E RND-LO
|
||||
004F RND-HI
|
||||
0050 LINNUM
|
||||
0050 LINNUM line number, unsigned word
|
||||
0067 Basic program address LO
|
||||
0068 Basic program address HI
|
||||
0069 Basic variables address LO
|
||||
|
@ -52,8 +61,48 @@
|
|||
0200 Input buffer
|
||||
|
||||
03D0 Applesoft warm start
|
||||
03EA VECT
|
||||
A56E catalog routine
|
||||
|
||||
03F2 RST: Control-reset vector
|
||||
03F3 RST: Control-reset vector
|
||||
03F4 RST: Control-reset checksum (EOR #$A5)
|
||||
03FB NMI: Non-Maskable Interrupt vector
|
||||
03FC NMI: Non-Maskable Interrupt vector
|
||||
03F8 USR: user vector (Control-Y)
|
||||
03F9 USR: user vector (Control-Y)
|
||||
03FE IRQ: Interrupt Request/BRK vector
|
||||
03FF IRQ: Interrupt Request/BRK vector
|
||||
|
||||
A54F DOS 3.3 INIT
|
||||
A413 DOS 3.3 LOAD
|
||||
A397 DOS 3.3 SAVE
|
||||
A4D1 DOS 3.3 RUN
|
||||
A4F0 DOS 3.3 CHAIN
|
||||
A263 DOS 3.3 DELETE
|
||||
A271 DOS 3.3 LOCK
|
||||
A275 DOS 3.3 UNLOCK
|
||||
A2EA DOS 3.3 CLOSE
|
||||
A51B DOS 3.3 READ
|
||||
A5C6 DOS 3.3 EXEC
|
||||
A510 DOS 3.3 WRITE
|
||||
A5DD DOS 3.3 POSITION
|
||||
A2A3 DOS 3.3 OPEN
|
||||
A298 DOS 3.3 APPEND
|
||||
A281 DOS 3.3 RENAME
|
||||
A56E DOS 3.3 CATALOG
|
||||
A233 DOS 3.3 MON
|
||||
A23D DOS 3.3 NOMON
|
||||
A229 DOS 3.3 PR#
|
||||
A22E DOS 3.3 IN#
|
||||
A251 DOS 3.3 MAXFILES
|
||||
A57A DOS 3.3 FP
|
||||
A59E DOS 3.3 INT
|
||||
A331 DOS 3.3 BSAVE
|
||||
A35D DOS 3.3 BLOAD
|
||||
A38E DOS 3.3 BRUN
|
||||
A27D DOS 3.3 VERIFY
|
||||
|
||||
BF00 ProDOS MLI entry point
|
||||
BF98 ProDOS Machine ID Byte
|
||||
|
||||
* C000 80STOREOFF Allow page2 to switch video page1 page2
|
||||
C001 80STOREON Allow page2 to switch main & aux video memory
|
||||
|
@ -68,8 +117,12 @@ C009 ALTZPON Enable aux memory from $0000-$01FF & avl BSR
|
|||
C00A SLOTC3ROMOFF Enable main ROM from $C300-$C3FF
|
||||
C00B SLOTC3ROMON Enable slot ROM from $C300-$C3FF
|
||||
|
||||
C000 KYBD - last key pressed
|
||||
C010 STROBE - Clear KYBD
|
||||
C000 KBD - Last key pressed
|
||||
C010 KBDSTRB - Clear KYBD
|
||||
C019 VBL - Vertical Blank
|
||||
C020 TAPEOUT - Toggle cassette output
|
||||
C030 SPKR - Toggle speaker
|
||||
C040 STROBE - Output strobe pulse to game I/O connector
|
||||
C050 TXTCLR - Display Graphics
|
||||
C051 TXTSET - Display Text
|
||||
C052 MIXCLR - Display Full Screen
|
||||
|
@ -78,6 +131,15 @@ C054 TXTPAGE1 - Display Page 1
|
|||
C055 TXTPAGE2 - If 80STORE Off: Display Page 2, If 80STORE On: Read/Write Aux Display Mem
|
||||
C056 LORES - Display LoRes Graphics
|
||||
C057 HIRES - Display HiRes Graphics
|
||||
C060 TAPEIN - Read audio from cassette input
|
||||
C061 PB0 - Read joystick button 0/Open-Apple key
|
||||
C062 PB1 - Read joystick button 1/Closed-Apple key
|
||||
C063 PB2 - Read joystick button 2
|
||||
C064 PADDL0 - Read paddle/joystick 0
|
||||
C065 PADDL1 - Read paddle/joystick 1
|
||||
C066 PADDL2 - Read paddle/joystick 2
|
||||
C067 PADDL3 - Read paddle/joystick 3
|
||||
C070 PTRIG - Clear paddle/joystick timer
|
||||
|
||||
C080 Read RAM bank 2; no write
|
||||
C081 ROMIN - Read ROM; write RAM bank 2
|
||||
|
@ -96,6 +158,9 @@ C08D Read ROM; write RAM bank 1
|
|||
C08E Read ROM; no write
|
||||
C08F Read/write RAM bank 1
|
||||
|
||||
C600 BOOT0 - Disk II controller ROM
|
||||
0801 BOOT1 - Disk II bootstrap RAM
|
||||
|
||||
D52C INLIN numeric input
|
||||
DB3A STROUT - output a string
|
||||
DB5C output a character
|
||||
|
@ -106,6 +171,7 @@ DEC0 SYNCHR
|
|||
DEC9 syntax error
|
||||
DFE3 PTRGET
|
||||
|
||||
E000 Applesoft BASIC entry
|
||||
E053 find a variable
|
||||
E10C convert FP to INT
|
||||
E2F2 GIVAYF - convert (A,Y) to FP
|
||||
|
@ -177,15 +243,16 @@ F941 PRINTAX - print AX registers in hex
|
|||
F948 PRBLNK - print 3 spaces
|
||||
F94A PRBL2 - print X blank spaces
|
||||
|
||||
FAA6 reboot DOS
|
||||
FAA6 PWRUP - reboot
|
||||
FAFF 0 = Autostart ROM, 1 = Old Monitor
|
||||
|
||||
FB1E PREAD - read game paddle
|
||||
FB2F initialise text screen
|
||||
FB2F INIT - initialise text screen
|
||||
FB39 text mode - SETTXT
|
||||
FB40 SETGR
|
||||
FB5B TABV - monitor tab routine
|
||||
FB6F set powerup checksum
|
||||
FB6F SETPWRC - set powerup checksum
|
||||
FBB3 VERSION - monitor ROM ID byte
|
||||
FBC1 BASCALC - calculate video address
|
||||
FBDD BELL1 - beep speaker
|
||||
FBF4 CURSRIT - move cursor right
|
||||
|
@ -193,6 +260,7 @@ FBF4 CURSRIT - move cursor right
|
|||
FC10 CURSLFT - move cursor left
|
||||
FC1A CURSUP - move cursor up
|
||||
FC22 VTAB
|
||||
FC24 VTABZ
|
||||
FC42 CLREOP - clear to end of page
|
||||
FC58 HOME - clear screen
|
||||
FC62 CR
|
||||
|
@ -215,12 +283,13 @@ FDE3 PRHEX - print a hex digit
|
|||
FDED COUT - print a character (in Acc)
|
||||
FDF0 COUT1 - print character to screen
|
||||
|
||||
FE2C move a block of memory
|
||||
FE1F IDROUTINE - detect //gs
|
||||
FE2C MOVE - move a block of memory
|
||||
FE80 SETINV - set inverse mode
|
||||
FE84 SETNORM - set normal mode
|
||||
FE89 disconnect DOS from I/O links
|
||||
FE89 SETKBD - disconnect DOS from I/O links
|
||||
FE8B INPORT
|
||||
FE93 disconnect DOS from I/O links
|
||||
FE93 SETVID - disconnect DOS from I/O links
|
||||
FE95 OUTPORT
|
||||
FECD WRITE
|
||||
FEFD READ
|
||||
|
@ -231,6 +300,14 @@ FF3A BELL
|
|||
FF3F IOREST - restore all registers
|
||||
FF4A IOSAVE - save all registers
|
||||
FF58 RTS - jump to <address on stack> + 1
|
||||
FF59 Monitor cold entry point
|
||||
FF59 MON - Monitor cold entry point (w/BELL)
|
||||
FF69 MONZ - Monitor entry point from BASIC (CALL -151)
|
||||
FFA7 GETNUM - move num to A2L.A2H
|
||||
FFC7 ZMODE - monitor get ASCII return
|
||||
|
||||
FFFA NMI_VECTOR
|
||||
FFFB NMI_VECTOR
|
||||
FFFC RESET_VECTOR
|
||||
FFFD RESET_VECTOR
|
||||
FFFE IRQ_VECTOR
|
||||
FFFF IRQ_VECTOR
|
||||
|
|
|
@ -52,7 +52,7 @@ public class AppleworksADBFile extends AbstractFile
|
|||
|
||||
dbMinVersion = buffer[218] & 0xFF;
|
||||
|
||||
headerSize = Utility.unsignedShort (buffer, 0);
|
||||
headerSize = Utility.getShort (buffer, 0);
|
||||
cursorDirectionSRL = buffer[30];
|
||||
cursorDirectionMRL = (char) buffer[31];
|
||||
currentDisplay = (char) buffer[34];
|
||||
|
@ -60,7 +60,7 @@ public class AppleworksADBFile extends AbstractFile
|
|||
categoryNames = new String[categories];
|
||||
|
||||
totalReports = buffer[38] & 0xFF;
|
||||
int recs = Utility.unsignedShort (buffer, 36);
|
||||
int recs = Utility.getShort (buffer, 36);
|
||||
totalRecords = dbMinVersion == 0 ? recs : recs & 0x7FFF;
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
|
@ -79,9 +79,9 @@ public class AppleworksADBFile extends AbstractFile
|
|||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
selectionRules[i] = Utility.unsignedShort (buffer, 223 + i * 2);
|
||||
testTypes[i] = Utility.unsignedShort (buffer, 229 + i * 2);
|
||||
continuation[i] = Utility.unsignedShort (buffer, 235 + i * 2);
|
||||
selectionRules[i] = Utility.getShort (buffer, 223 + i * 2);
|
||||
testTypes[i] = Utility.getShort (buffer, 229 + i * 2);
|
||||
continuation[i] = Utility.getShort (buffer, 235 + i * 2);
|
||||
comparison[i] = new String (buffer, 241 + i * 20, 20);
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ public class AppleworksADBFile extends AbstractFile
|
|||
ptr += 600;
|
||||
}
|
||||
|
||||
int length = Utility.unsignedShort (buffer, ptr);
|
||||
int length = Utility.getShort (buffer, ptr);
|
||||
ptr += 2;
|
||||
|
||||
if (length == 0)
|
||||
|
@ -118,7 +118,7 @@ public class AppleworksADBFile extends AbstractFile
|
|||
|
||||
for (int recordNo = 0; recordNo < totalRecords; recordNo++)
|
||||
{
|
||||
length = Utility.unsignedShort (buffer, ptr);
|
||||
length = Utility.getShort (buffer, ptr);
|
||||
ptr += 2;
|
||||
if (length == 0)
|
||||
break;
|
||||
|
|
|
@ -25,7 +25,7 @@ public class AppleworksSSFile extends AbstractFile
|
|||
int ptr = header.ssMinVers == 0 ? 300 : 302;
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
int length = Utility.unsignedShort (buffer, ptr);
|
||||
int length = Utility.getShort (buffer, ptr);
|
||||
|
||||
if (length == 0xFFFF)
|
||||
break;
|
||||
|
@ -103,7 +103,7 @@ public class AppleworksSSFile extends AbstractFile
|
|||
|
||||
calcOrder = (char) buffer[131];
|
||||
calcFrequency = (char) buffer[132];
|
||||
lastRow = Utility.unsignedShort (buffer, 133);
|
||||
lastRow = Utility.getShort (buffer, 133);
|
||||
lastColumn = buffer[135] & 0xFF;
|
||||
windowLayout = (char) buffer[136];
|
||||
windowSynch = buffer[137] != 0;
|
||||
|
@ -204,15 +204,15 @@ public class AppleworksSSFile extends AbstractFile
|
|||
|
||||
r1 = buffer[offset + 3] & 0xFF;
|
||||
c1 = buffer[offset + 4] & 0xFF;
|
||||
r2 = Utility.unsignedShort (buffer, offset + 5);
|
||||
r2 = Utility.getShort (buffer, offset + 5);
|
||||
c2 = buffer[offset + 7] & 0xFF;
|
||||
r3 = Utility.unsignedShort (buffer, offset + 8);
|
||||
r3 = Utility.getShort (buffer, offset + 8);
|
||||
c3 = buffer[offset + 10] & 0xFF;
|
||||
r4 = Utility.unsignedShort (buffer, offset + 11);
|
||||
r4 = Utility.getShort (buffer, offset + 11);
|
||||
c4 = buffer[offset + 13] & 0xFF;
|
||||
r5 = buffer[offset + 14] & 0xFF;
|
||||
c5 = buffer[offset + 15] & 0xFF;
|
||||
r6 = Utility.unsignedShort (buffer, offset + 16);
|
||||
r6 = Utility.getShort (buffer, offset + 16);
|
||||
c6 = buffer[offset + 18] & 0xFF;
|
||||
r7 = buffer[offset + 19] & 0xFF;
|
||||
c7 = buffer[offset + 20] & 0xFF;
|
||||
|
@ -265,7 +265,7 @@ public class AppleworksSSFile extends AbstractFile
|
|||
|
||||
public Row (int ptr)
|
||||
{
|
||||
rowNumber = Utility.unsignedShort (buffer, ptr);
|
||||
rowNumber = Utility.getShort (buffer, ptr);
|
||||
ptr += 2; // first control byte
|
||||
|
||||
int column = 0;
|
||||
|
|
|
@ -139,7 +139,7 @@ public class AppleworksWPFile extends AbstractFile
|
|||
break;
|
||||
|
||||
default:
|
||||
System.out.printf ("Unknown value in %s: %02X %02X%n", name, b1, b2);
|
||||
System.out.printf ("Unknown value in %s: %02X %02X%n", getName (), b1, b2);
|
||||
}
|
||||
ptr += 2;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class CellAddress
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
colRef = buffer[offset];
|
||||
rowRef = Utility.intValue (buffer[offset + 1], buffer[offset + 2]);
|
||||
rowRef = Utility.getShort (buffer, offset + 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
|
@ -131,9 +131,9 @@ abstract class Report
|
|||
if (buffer[offset + 480 + fudge] == 0) // test high byte
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
selectionRules[i] = Utility.unsignedShort (buffer, offset + 479 + i * 2 + fudge);
|
||||
testTypes[i] = Utility.unsignedShort (buffer, offset + 485 + i * 2 + fudge);
|
||||
continuation[i] = Utility.unsignedShort (buffer, offset + 491 + i * 2 + fudge);
|
||||
selectionRules[i] = Utility.getShort (buffer, offset + 479 + i * 2 + fudge);
|
||||
testTypes[i] = Utility.getShort (buffer, offset + 485 + i * 2 + fudge);
|
||||
continuation[i] = Utility.getShort (buffer, offset + 491 + i * 2 + fudge);
|
||||
comparison[i] = pascalString (buffer, offset + 497 + i * 32 + fudge);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -26,23 +26,28 @@ class CPMCatalogSector extends AbstractSector
|
|||
|
||||
for (int i = 0; i <= 255; i += CATALOG_ENTRY_SIZE)
|
||||
{
|
||||
if (buffer[i] == (byte) 0xE5)
|
||||
if (buffer[i] == (byte) 0xE5 && buffer[i + 1] == (byte) 0xE5)
|
||||
break;
|
||||
|
||||
int userNumber = buffer[i] & 0xFF;
|
||||
if (userNumber > 31 && userNumber != (byte) 0xE5)
|
||||
break;
|
||||
|
||||
boolean readOnly = (buffer[i + 9] & 0x80) != 0;
|
||||
boolean systemFile = (buffer[i + 10] & 0x80) != 0;
|
||||
boolean unknown = (buffer[i + 11] & 0x80) != 0;
|
||||
String type;
|
||||
String extra;
|
||||
|
||||
if (readOnly || systemFile)
|
||||
if (readOnly || systemFile || unknown)
|
||||
{
|
||||
byte[] typeBuffer = new byte[3];
|
||||
typeBuffer[0] = (byte) (buffer[i + 9] & 0x7F);
|
||||
typeBuffer[1] = (byte) (buffer[i + 10] & 0x7F);
|
||||
typeBuffer[2] = buffer[i + 11];
|
||||
typeBuffer[2] = (byte) (buffer[i + 11] & 0x7F);
|
||||
type = new String (typeBuffer).trim ();
|
||||
extra = String.format (" (%s%s)", readOnly ? "read only" : "",
|
||||
systemFile ? "system file" : "");
|
||||
extra = String.format (" (%s%s%s)", readOnly ? "read only" : "",
|
||||
systemFile ? "system file" : "", unknown ? "unknown" : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -50,8 +55,14 @@ class CPMCatalogSector extends AbstractSector
|
|||
extra = "";
|
||||
}
|
||||
|
||||
addText (text, buffer, i, 1, "User number");
|
||||
addText (text, buffer, i + 1, 4, "File name : " + new String (buffer, i + 1, 8));
|
||||
if (buffer[i] == (byte) 0xE5)
|
||||
addText (text, buffer, i, 1, "Deleted file?");
|
||||
else
|
||||
addText (text, buffer, i, 1, "User number");
|
||||
if (buffer[i + 1] == 0)
|
||||
addText (text, buffer, i + 1, 4, "File name : ");
|
||||
else
|
||||
addText (text, buffer, i + 1, 4, "File name : " + new String (buffer, i + 1, 8));
|
||||
addText (text, buffer, i + 5, 4, "");
|
||||
addText (text, buffer, i + 9, 3, "File type : " + type + extra);
|
||||
addText (text, buffer, i + 12, 1, "Extent counter LO");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.bytezone.diskbrowser.cpm;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
|
@ -13,11 +14,15 @@ import com.bytezone.diskbrowser.disk.Disk;
|
|||
import com.bytezone.diskbrowser.disk.DiskAddress;
|
||||
import com.bytezone.diskbrowser.disk.SectorType;
|
||||
import com.bytezone.diskbrowser.gui.DataSource;
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
|
||||
// https://www.retrotechnology.com/dri/howto_cpm.html
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class CPMDisk extends AbstractFormattedDisk
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final int EMPTY_BYTE_VALUE = 0xE5;
|
||||
|
||||
private final Color green = new Color (0, 200, 0);
|
||||
|
||||
public final SectorType catalogSector = new SectorType ("Catalog", green);
|
||||
|
@ -50,7 +55,7 @@ public class CPMDisk extends AbstractFormattedDisk
|
|||
sectorTypesList.add (macSector);
|
||||
sectorTypesList.add (otherSector);
|
||||
|
||||
setEmptyByte ((byte) 0xE5);
|
||||
setEmptyByte ((byte) EMPTY_BYTE_VALUE);
|
||||
|
||||
// search for the version string
|
||||
for (int i = 8; i >= 4; i -= 2)
|
||||
|
@ -74,22 +79,28 @@ public class CPMDisk extends AbstractFormattedDisk
|
|||
|
||||
sectorTypes[da.getBlockNo ()] = catalogSector;
|
||||
byte[] buffer = disk.readBlock (da);
|
||||
|
||||
int b1 = buffer[0] & 0xFF;
|
||||
int b2 = buffer[1] & 0xFF;
|
||||
if (b1 == 0xE5)
|
||||
|
||||
if (b1 == EMPTY_BYTE_VALUE && (b2 == EMPTY_BYTE_VALUE || b2 == 0))
|
||||
continue;
|
||||
if (b1 > 31)
|
||||
|
||||
if (b1 > 31 && b1 != EMPTY_BYTE_VALUE)
|
||||
break;
|
||||
if (b2 < 32 || (b2 > 126 && b2 != 0xE5))
|
||||
|
||||
if (b2 <= 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
|
||||
break;
|
||||
|
||||
for (int i = 0; i < buffer.length; i += 32)
|
||||
{
|
||||
b1 = buffer[i] & 0xFF;
|
||||
b2 = buffer[i + 1] & 0xFF;
|
||||
if (b1 == 0xE5)
|
||||
break;
|
||||
if (b2 < 32 || (b2 > 126 && b2 != 0xE5))
|
||||
|
||||
if (b1 == EMPTY_BYTE_VALUE) // deleted file??
|
||||
continue;
|
||||
|
||||
if (b2 <= 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
|
||||
break;
|
||||
|
||||
DirectoryEntry entry = new DirectoryEntry (this, buffer, i);
|
||||
|
@ -111,9 +122,6 @@ public class CPMDisk extends AbstractFormattedDisk
|
|||
}
|
||||
}
|
||||
|
||||
// root.setUserObject (getCatalog ()); // override the disk's default display
|
||||
// makeNodeVisible (rootNode.getFirstLeaf ());
|
||||
|
||||
volumeNode.setUserObject (getCatalog ());
|
||||
makeNodeVisible (volumeNode.getFirstLeaf ());
|
||||
}
|
||||
|
@ -147,6 +155,7 @@ public class CPMDisk extends AbstractFormattedDisk
|
|||
{
|
||||
if (fileEntries.size () > 0 && fileEntries.size () > fileNo)
|
||||
return fileEntries.get (fileNo).getSectors ();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -180,19 +189,17 @@ public class CPMDisk extends AbstractFormattedDisk
|
|||
public AppleFileSource getCatalog ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
String newLine = String.format ("%n");
|
||||
String line =
|
||||
"---- --------- --- - - -- -- -- -- ----------------------------"
|
||||
+ "-------------------" + newLine;
|
||||
String line = "---- --------- --- - - -- -- -- -- ----------------------------"
|
||||
+ "-------------------\n";
|
||||
StringBuilder text = new StringBuilder ();
|
||||
text.append (String.format ("Disk : %s%n%n", getDisplayPath ()));
|
||||
text.append ("User Name Typ R S Ex S2 S1 RC Blocks" + newLine);
|
||||
text.append (String.format ("File : %s%n%n", getDisplayPath ()));
|
||||
text.append ("User Name Typ R S Ex S2 S1 RC Blocks\n");
|
||||
text.append (line);
|
||||
|
||||
for (AppleFileSource entry : fileEntries)
|
||||
{
|
||||
text.append (((DirectoryEntry) entry).line ());
|
||||
text.append (newLine);
|
||||
text.append ("\n");
|
||||
}
|
||||
text.append (line);
|
||||
if (version != 0)
|
||||
|
@ -204,6 +211,64 @@ public class CPMDisk extends AbstractFormattedDisk
|
|||
// ---------------------------------------------------------------------------------//
|
||||
public static boolean isCorrectFormat (AppleDisk disk)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
boolean debug = false;
|
||||
|
||||
disk.setInterleave (3);
|
||||
|
||||
// collect catalog sectors
|
||||
List<DiskAddress> catalog = new ArrayList<> ();
|
||||
for (int i = 0; i < 8; i++)
|
||||
catalog.add (disk.getDiskAddress (3, i));
|
||||
byte[] buffer = disk.readBlocks (catalog);
|
||||
|
||||
if (debug)
|
||||
System.out.println (HexFormatter.format (buffer));
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int start = i * 1024;
|
||||
int end = start + 1024;
|
||||
|
||||
for (int ptr = start; ptr < end; ptr += 32)
|
||||
{
|
||||
if (buffer[ptr] == (byte) EMPTY_BYTE_VALUE)
|
||||
{
|
||||
if (buffer[ptr + 1] == (byte) EMPTY_BYTE_VALUE //
|
||||
|| buffer[ptr + 1] == 0) // finished this block
|
||||
break;
|
||||
continue; // deleted file?
|
||||
}
|
||||
|
||||
int userNo = buffer[ptr] & 0xFF;
|
||||
if (userNo > 31)
|
||||
return false;
|
||||
|
||||
for (int j = 1; j < 12; j++)
|
||||
{
|
||||
int ch = buffer[ptr + j] & 0x7F; // remove flag
|
||||
if (ch < 32 || ch > 126) // invalid ascii
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
{
|
||||
String fileName = new String (buffer, ptr + 1, 8);
|
||||
String fileType = new String (buffer, ptr + 9, 3);
|
||||
System.out.printf ("%2d %s %s%n", userNo, fileName, fileType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug)
|
||||
System.out.println ("CP/M disk");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private static boolean isCorrectFormat2 (AppleDisk disk)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disk.setInterleave (3);
|
||||
|
||||
|
@ -222,26 +287,60 @@ public class CPMDisk extends AbstractFormattedDisk
|
|||
for (int sector = 0; sector < 8; sector++)
|
||||
{
|
||||
byte[] buffer = disk.readBlock (3, sector);
|
||||
System.out.println (HexFormatter.format (buffer));
|
||||
|
||||
// check if entire sector is empty (everything == 0xE5)
|
||||
if (bufferContainsAll (buffer, (byte) 0xE5))
|
||||
if (bufferContainsAll (buffer, (byte) EMPTY_BYTE_VALUE))
|
||||
break;
|
||||
|
||||
int b1 = buffer[0] & 0xFF;
|
||||
int b2 = buffer[1] & 0xFF;
|
||||
|
||||
if (b1 == EMPTY_BYTE_VALUE && (b2 == EMPTY_BYTE_VALUE || b2 == 0))
|
||||
continue;
|
||||
|
||||
if (b1 > 31 && b1 != EMPTY_BYTE_VALUE)
|
||||
break;
|
||||
|
||||
if (b2 < 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
|
||||
break;
|
||||
|
||||
for (int i = 0; i < buffer.length; i += 32)
|
||||
{
|
||||
int val = buffer[i] & 0xFF;
|
||||
if (val == 0xE5)
|
||||
break;
|
||||
b1 = buffer[i] & 0xFF;
|
||||
b2 = buffer[i + 1] & 0xFF;
|
||||
|
||||
if (val > 31)
|
||||
if (b1 == EMPTY_BYTE_VALUE) // deleted file??
|
||||
continue;
|
||||
|
||||
if (b2 < 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
|
||||
return false;
|
||||
|
||||
for (int j = 1; j <= 8; j++)
|
||||
{
|
||||
val = buffer[i + j] & 0xFF;
|
||||
if (val < 32 || (val > 126 && val != 0xE5))
|
||||
return false;
|
||||
}
|
||||
// int val = buffer[i] & 0xFF;
|
||||
// if (val == EMPTY_BYTE_VALUE)
|
||||
// {
|
||||
// if (debug)
|
||||
// System.out.println ("empty value found - deleted file?");
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (val > 31)
|
||||
// {
|
||||
// if (debug)
|
||||
// System.out.println ("val > 31");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// for (int j = 1; j <= 8; j++)
|
||||
// {
|
||||
// val = buffer[i + j] & 0xFF;
|
||||
// if (val < 32 || (val > 126 && val != EMPTY_BYTE_VALUE))
|
||||
// {
|
||||
// if (debug)
|
||||
// System.out.println ("val < 32 || val > 126");
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
||||
import com.bytezone.diskbrowser.applefile.CPMBasicFile;
|
||||
import com.bytezone.diskbrowser.applefile.CPMTextFile;
|
||||
import com.bytezone.diskbrowser.applefile.DefaultAppleFile;
|
||||
import com.bytezone.diskbrowser.disk.AppleDiskAddress;
|
||||
|
@ -54,7 +55,11 @@ class DirectoryEntry implements AppleFileSource
|
|||
type = new String (typeBuffer).trim ();
|
||||
|
||||
userNumber = buffer[offset] & 0xFF;
|
||||
name = new String (buffer, offset + 1, 8).trim ();
|
||||
if (userNumber == 0xE5 && buffer[offset + 1] == 0)
|
||||
name = "";
|
||||
else
|
||||
name = new String (buffer, offset + 1, 8).trim ();
|
||||
|
||||
extent = buffer[offset + 12] & 0xFF;
|
||||
s2 = buffer[offset + 13] & 0xFF;
|
||||
s1 = buffer[offset + 14] & 0xFF;
|
||||
|
@ -120,6 +125,7 @@ class DirectoryEntry implements AppleFileSource
|
|||
for (DiskAddress sector : blocks)
|
||||
if (sector.matches (da))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -133,9 +139,8 @@ class DirectoryEntry implements AppleFileSource
|
|||
char ro = readOnly ? '*' : ' ';
|
||||
char sf = systemFile ? '*' : ' ';
|
||||
|
||||
String text =
|
||||
String.format ("%3d %-8s %-3s %s %s %02X %02X %02X %02X %s",
|
||||
userNumber, name, type, ro, sf, extent, s2, s1, recordsUsed, bytes);
|
||||
String text = String.format ("%3d %-8s %-3s %s %s %02X %02X %02X %02X %s",
|
||||
userNumber, name, type, ro, sf, extent, s2, s1, recordsUsed, bytes);
|
||||
for (DirectoryEntry entry : entries)
|
||||
text = text + "\n" + entry.line ();
|
||||
|
||||
|
@ -207,23 +212,18 @@ class DirectoryEntry implements AppleFileSource
|
|||
byte[] exactBuffer = new byte[len];
|
||||
System.arraycopy (buffer, 0, exactBuffer, 0, len);
|
||||
|
||||
int max = Math.min (256, exactBuffer.length);
|
||||
int count = 0;
|
||||
for (int i = 1; i < max; i++)
|
||||
{
|
||||
if (exactBuffer[i - 1] == 0x0D && exactBuffer[i] == 0x0A)
|
||||
++count;
|
||||
}
|
||||
|
||||
if ("COM".equals (type))
|
||||
appleFile = new DefaultAppleFile (name, exactBuffer, "COM File");
|
||||
else if ("DVR".equals (type))
|
||||
appleFile = new DefaultAppleFile (name, exactBuffer, "DVR File");
|
||||
else if ("ASM".equals (type) || "DOC".equals (type) || "TXT".equals (type)
|
||||
|| count > 2)
|
||||
else if ("ASM".equals (type) || "DOC".equals (type) || "COB".equals (type)
|
||||
|| "HLP".equals (type) || "TXT".equals (type) || "LET".equals (type) || "ALX".equals (type)
|
||||
|| "SRC".equals (type) || "H".equals (type) || exactBuffer[len - 1] == 0x1A)
|
||||
appleFile = new CPMTextFile (name, exactBuffer);
|
||||
else if ("BAS".equals (type))
|
||||
appleFile = new CPMBasicFile (name, exactBuffer);
|
||||
else
|
||||
appleFile = new DefaultAppleFile (name, exactBuffer, "CPM File : " + type);
|
||||
appleFile = new DefaultAppleFile (name, exactBuffer, "CPM File : " + name + "." + type);
|
||||
|
||||
return appleFile;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
|
|||
*/
|
||||
sectorTypesList.add (emptySector);
|
||||
sectorTypesList.add (usedSector);
|
||||
|
||||
/*
|
||||
* Hopefully every used sector will be changed by the subclass to something
|
||||
* sensible, but deleted files will always leave the sector as used/unknown
|
||||
|
@ -74,16 +75,23 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
|
|||
*/
|
||||
setSectorTypes ();
|
||||
setGridLayout ();
|
||||
|
||||
/*
|
||||
* Create the disk name as the root for the catalog tree. Subclasses will
|
||||
* have to append their catalog entries to this node.
|
||||
*/
|
||||
String name = getName ();
|
||||
if (name.endsWith (".tmp"))
|
||||
name = "tmp.dsk";
|
||||
|
||||
DefaultAppleFileSource afs =
|
||||
new DefaultAppleFileSource (getName (), disk.toString (), this);
|
||||
// new DefaultAppleFileSource (name, disk.toString (), this);
|
||||
new DefaultAppleFileSource (name, new DefaultDataSource (disk), this);
|
||||
DefaultMutableTreeNode root = new DefaultMutableTreeNode (afs);
|
||||
DefaultTreeModel treeModel = new DefaultTreeModel (root);
|
||||
catalogTree = new JTree (treeModel);
|
||||
treeModel.setAsksAllowsChildren (true); // allows empty nodes to appear as folders
|
||||
|
||||
/*
|
||||
* Add an ActionListener to the disk in case the interleave or blocksize
|
||||
* changes
|
||||
|
@ -124,50 +132,37 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
|
|||
{
|
||||
int totalBlocks = disk.getTotalBlocks ();
|
||||
|
||||
switch (totalBlocks)
|
||||
Dimension newGridLayout = switch (totalBlocks)
|
||||
{
|
||||
case 280:
|
||||
gridLayout = new Dimension (8, 35);
|
||||
break;
|
||||
|
||||
case 455:
|
||||
gridLayout = new Dimension (13, 35);
|
||||
break;
|
||||
|
||||
case 560:
|
||||
gridLayout = new Dimension (16, 35);
|
||||
break;
|
||||
|
||||
case 704:
|
||||
gridLayout = new Dimension (16, 44);
|
||||
break;
|
||||
|
||||
case 768:
|
||||
gridLayout = new Dimension (16, 48);
|
||||
break;
|
||||
|
||||
case 1600:
|
||||
case 280 -> new Dimension (8, 35);
|
||||
case 455 -> new Dimension (13, 35);
|
||||
case 560 -> new Dimension (16, 35);
|
||||
case 704 -> new Dimension (16, 44);
|
||||
case 768 -> new Dimension (16, 48);
|
||||
case 800 -> new Dimension (8, 100);
|
||||
case 1600 ->
|
||||
{
|
||||
if (disk.getBlocksPerTrack () == 32)
|
||||
gridLayout = new Dimension (disk.getBlocksPerTrack (), disk.getTotalTracks ());
|
||||
yield new Dimension (32, 50);
|
||||
else
|
||||
gridLayout = new Dimension (16, 100);
|
||||
break;
|
||||
|
||||
case 2048:
|
||||
gridLayout = new Dimension (8, 256);
|
||||
break;
|
||||
|
||||
default:
|
||||
yield new Dimension (16, 100);
|
||||
}
|
||||
case 2048 -> new Dimension (8, 256);
|
||||
case 3200 -> new Dimension (16, 200);
|
||||
default ->
|
||||
{
|
||||
int[] sizes = { 32, 20, 16, 8 };
|
||||
for (int size : sizes)
|
||||
if ((totalBlocks % size) == 0)
|
||||
{
|
||||
gridLayout = new Dimension (size, totalBlocks / size);
|
||||
break;
|
||||
}
|
||||
if (gridLayout == null)
|
||||
System.out.println ("Unusable total blocks : " + totalBlocks);
|
||||
}
|
||||
yield new Dimension (size, totalBlocks / size);
|
||||
yield null;
|
||||
}
|
||||
};
|
||||
|
||||
if (newGridLayout == null)
|
||||
System.out.println ("Unusable total blocks : " + totalBlocks);
|
||||
else
|
||||
gridLayout = newGridLayout;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -211,8 +206,7 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
|
|||
|
||||
DefaultMutableTreeNode root = getCatalogTreeRoot ();
|
||||
if (root.getUserObject () == null)
|
||||
root.setUserObject (
|
||||
new DefaultAppleFileSource (getName (), disk.toString (), this));
|
||||
root.setUserObject (new DefaultAppleFileSource (getName (), disk.toString (), this));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -239,13 +233,15 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
|
|||
public String getDisplayPath ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (originalPath != null)
|
||||
return originalPath.toString ();
|
||||
|
||||
String home = System.getProperty ("user.home");
|
||||
String path = disk.getFile ().getAbsolutePath ();
|
||||
if (path.startsWith (home))
|
||||
return "~" + path.substring (home.length ());
|
||||
|
||||
String path =
|
||||
originalPath != null ? originalPath.toString () : disk.getFile ().getAbsolutePath ();
|
||||
|
||||
int pos = path.indexOf (home);
|
||||
|
||||
if (pos == 0 || (path.startsWith ("/Volumes/") && pos > 0))
|
||||
return "~" + path.substring (home.length () + pos);
|
||||
|
||||
return disk.getFile ().getAbsolutePath ();
|
||||
}
|
||||
|
@ -328,24 +324,21 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
|
|||
{
|
||||
Enumeration<TreeNode> children = node.breadthFirstEnumeration ();
|
||||
if (children != null)
|
||||
{
|
||||
while (children.hasMoreElements ())
|
||||
{
|
||||
DefaultMutableTreeNode childNode =
|
||||
(DefaultMutableTreeNode) children.nextElement ();
|
||||
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) children.nextElement ();
|
||||
if (childNode.getUserObject ().toString ().indexOf (name) > 0)
|
||||
return childNode;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
||||
/*
|
||||
* These routines just hand back the information that was created above, and
|
||||
* added to by the subclass.
|
||||
*/
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public SectorType getSectorType (int block)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
|
@ -8,19 +8,16 @@ import javax.swing.JPanel;
|
|||
import com.bytezone.diskbrowser.applefile.AssemblerProgram;
|
||||
import com.bytezone.diskbrowser.gui.DataSource;
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public abstract class AbstractSector implements DataSource
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static String newLine = String.format ("%n");
|
||||
private static String newLine2 = newLine + newLine;
|
||||
|
||||
final public byte[] buffer;
|
||||
protected Disk disk;
|
||||
protected DiskAddress diskAddress;
|
||||
AssemblerProgram assembler;
|
||||
String description;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AbstractSector (Disk disk, byte[] buffer, DiskAddress diskAddress)
|
||||
|
@ -57,6 +54,14 @@ public abstract class AbstractSector implements DataSource
|
|||
return HexFormatter.format (buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public byte[] getBuffer ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public BufferedImage getImage ()
|
||||
|
@ -70,9 +75,7 @@ public abstract class AbstractSector implements DataSource
|
|||
public String getText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (description == null)
|
||||
description = createText ();
|
||||
return description;
|
||||
return createText ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -85,10 +88,11 @@ public abstract class AbstractSector implements DataSource
|
|||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append (title + newLine2);
|
||||
text.append ("Offset Value Description" + newLine);
|
||||
text.append (title + "\n\n");
|
||||
text.append ("Offset Value Description\n");
|
||||
text.append ("======= =========== "
|
||||
+ "===============================================================" + newLine);
|
||||
+ "===============================================================\n");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
@ -138,14 +142,13 @@ public abstract class AbstractSector implements DataSource
|
|||
String desc)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (size == 1)
|
||||
desc += " (" + (b[offset] & 0xFF) + ")";
|
||||
else if (size == 2)
|
||||
desc +=
|
||||
String.format (" (%,d)", ((b[offset + 1] & 0xFF) * 256 + (b[offset] & 0xFF)));
|
||||
else if (size == 3)
|
||||
desc += String.format (" (%,d)", ((b[offset + 2] & 0xFF) * 65536)
|
||||
+ ((b[offset + 1] & 0xFF) * 256) + (b[offset] & 0xFF));
|
||||
desc += switch (size)
|
||||
{
|
||||
case 1 -> " (" + (b[offset] & 0xFF) + ")";
|
||||
case 2 -> String.format (" (%,d)", Utility.getShort (b, offset));
|
||||
case 3 -> String.format (" (%,d)", Utility.readTriple (b, offset));
|
||||
default -> "";
|
||||
};
|
||||
|
||||
addText (text, b, offset, size, desc);
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ import java.util.List;
|
|||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
||||
import com.bytezone.diskbrowser.nib.NibFile;
|
||||
import com.bytezone.diskbrowser.nib.V2dFile;
|
||||
import com.bytezone.diskbrowser.nib.WozFile;
|
||||
import com.bytezone.diskbrowser.nufx.Binary2;
|
||||
import com.bytezone.diskbrowser.nufx.NuFX;
|
||||
import com.bytezone.diskbrowser.utilities.FileFormatException;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class AppleDisk implements Disk
|
||||
|
@ -33,17 +35,24 @@ public class AppleDisk implements Disk
|
|||
private final int tracks; // usually 35 for floppy disks
|
||||
private int sectors; // 8 or 16 (or 32 for unidos)
|
||||
private int blocks; // 280 or 560 for floppy disks, higher for HD
|
||||
private int dosVersion;
|
||||
|
||||
private final int trackSize; // 4096
|
||||
public int sectorSize; // 256 or 512
|
||||
|
||||
private NuFX nuFX;
|
||||
private Binary2 bin2;
|
||||
private WozFile wozFile;
|
||||
private PrefixDiskCopy prefixDiskCopy;
|
||||
private Prefix2mg prefix2mg;
|
||||
|
||||
private int interleave = 0;
|
||||
private static int[][] interleaveSector = //
|
||||
{ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||
22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, // None
|
||||
{ 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15 }, // Prodos/Pascal
|
||||
{ 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 }, // Infocom
|
||||
{ 0, 6, 12, 3, 9, 15, 14, 5, 11, 2, 8, 7, 13, 4, 10, 1 } }; // CPM
|
||||
{ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, //
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, // None
|
||||
{ 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15 }, // Prodos/Pascal
|
||||
{ 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 }, // Infocom
|
||||
{ 0, 6, 12, 3, 9, 15, 14, 5, 11, 2, 8, 7, 13, 4, 10, 1 } }; // CPM
|
||||
|
||||
// Physical disk interleave:
|
||||
// Info from http://www.applelogic.org/TheAppleIIEGettingStarted.html
|
||||
|
@ -82,8 +91,6 @@ public class AppleDisk implements Disk
|
|||
private ActionListener actionListenerList;
|
||||
private List<DiskAddress> blockList;
|
||||
|
||||
private WozFile wozFile;
|
||||
|
||||
private final boolean debug = false;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -94,8 +101,7 @@ public class AppleDisk implements Disk
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AppleDisk (File file, int tracks, int sectors, int skip)
|
||||
throws FileFormatException
|
||||
public AppleDisk (File file, int tracks, int sectors, int skip) throws FileFormatException
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
assert (file.exists ()) : "No such path :" + file.getAbsolutePath ();
|
||||
|
@ -113,15 +119,14 @@ public class AppleDisk implements Disk
|
|||
|
||||
if ("2mg".equalsIgnoreCase (suffix) || "2IMG".equals (prefix))
|
||||
{
|
||||
// System.out.println ("checking 2mg");
|
||||
if ("2IMG".equals (prefix))
|
||||
{
|
||||
Prefix2mg prefix2mg = new Prefix2mg (buffer);
|
||||
prefix2mg = new Prefix2mg (buffer);
|
||||
if (debug)
|
||||
System.out.println (prefix2mg);
|
||||
|
||||
if (prefix2mg.diskData > 0)
|
||||
this.blocks = prefix2mg.diskData / 4096 * 8; // reduce blocks to a multiple of 8
|
||||
if (prefix2mg.length > 0)
|
||||
this.blocks = prefix2mg.length / 4096 * 8; // reduce blocks to a multiple of 8
|
||||
|
||||
this.sectorSize = 512;
|
||||
this.trackSize = 8 * sectorSize;
|
||||
|
@ -142,7 +147,7 @@ public class AppleDisk implements Disk
|
|||
}
|
||||
else if ("img".equals (suffix) || "dimg".equals (suffix))
|
||||
{
|
||||
PrefixDiskCopy prefixDiskCopy = new PrefixDiskCopy (buffer);
|
||||
prefixDiskCopy = new PrefixDiskCopy (buffer);
|
||||
|
||||
blocks = prefixDiskCopy.getBlocks ();
|
||||
this.sectorSize = 512;
|
||||
|
@ -152,7 +157,7 @@ public class AppleDisk implements Disk
|
|||
tracks = blocks / 8; // change parameter!
|
||||
sectors = 8; // change parameter!
|
||||
}
|
||||
else if (suffix.equalsIgnoreCase ("HDV")
|
||||
else if (suffix.equalsIgnoreCase ("HDV") //
|
||||
|| (suffix.equalsIgnoreCase ("po") && tracks > 50)) // ULTIMATE APPLE1 CFFA 3.5.po
|
||||
{
|
||||
//this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8
|
||||
|
@ -160,7 +165,7 @@ public class AppleDisk implements Disk
|
|||
this.sectorSize = 512;
|
||||
this.trackSize = sectors * sectorSize;
|
||||
}
|
||||
else if (file.length () == 143360 && tracks == 256 && sectors == 8) // wiz4
|
||||
else if (file.length () == 143360 && tracks == 256 && sectors == 8) // wiz4 or wiz5
|
||||
{
|
||||
this.blocks = tracks * sectors;
|
||||
this.sectorSize = 512;
|
||||
|
@ -232,12 +237,27 @@ public class AppleDisk implements Disk
|
|||
checkSectorsForData ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void setNuFX (NuFX nufx)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.nuFX = nufx;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void setBinary2 (Binary2 bin2)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.bin2 = bin2;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AppleDisk (V2dFile disk, int tracks, int sectors)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.tracks = tracks;
|
||||
this.sectors = sectors;
|
||||
|
||||
file = disk.file;
|
||||
diskBuffer = disk.getDiskBuffer ();
|
||||
|
||||
|
@ -337,6 +357,13 @@ public class AppleDisk implements Disk
|
|||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void setDosVersion (int version)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.dosVersion = version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines that implement the Disk interface
|
||||
*/
|
||||
|
@ -440,6 +467,7 @@ public class AppleDisk implements Disk
|
|||
readBuffer (da, buffer, ptr);
|
||||
ptr += sectorSize;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
@ -528,8 +556,7 @@ public class AppleDisk implements Disk
|
|||
{
|
||||
if (!isValidAddress (block))
|
||||
{
|
||||
System.out.printf ("getDiskAddress: Invalid block : %d of %d%n", block,
|
||||
this.blocks);
|
||||
System.out.printf ("getDiskAddress: Invalid block : %d of %d%n", block, this.blocks);
|
||||
return null;
|
||||
// return new AppleDiskAddress (this, 0); // this was looping 26/07/2016
|
||||
}
|
||||
|
@ -564,8 +591,13 @@ public class AppleDisk implements Disk
|
|||
public boolean isValidAddress (int track, int sector)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
track &= 0x3F;
|
||||
sector &= 0x1F;
|
||||
if (dosVersion >= 0x41)
|
||||
{
|
||||
track &= 0x3F;
|
||||
sector &= 0x1F;
|
||||
}
|
||||
// else
|
||||
// System.out.println ("normal dos");
|
||||
|
||||
if (track < 0 || track >= this.tracks)
|
||||
return false;
|
||||
|
@ -590,10 +622,9 @@ public class AppleDisk implements Disk
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
assert da.getDisk () == this : "Disk address not applicable to this disk";
|
||||
assert sectorSize == SECTOR_SIZE
|
||||
|| sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize;
|
||||
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : "
|
||||
+ interleave;
|
||||
assert sectorSize == SECTOR_SIZE || sectorSize == BLOCK_SIZE : "Invalid sector size : "
|
||||
+ sectorSize;
|
||||
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " + interleave;
|
||||
|
||||
if (sectorSize == SECTOR_SIZE)
|
||||
{
|
||||
|
@ -606,8 +637,7 @@ public class AppleDisk implements Disk
|
|||
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE);
|
||||
|
||||
diskOffset = getBufferOffset (da, 1);
|
||||
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset + SECTOR_SIZE,
|
||||
SECTOR_SIZE);
|
||||
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset + SECTOR_SIZE, SECTOR_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,10 +646,9 @@ public class AppleDisk implements Disk
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
assert da.getDisk () == this : "Disk address not applicable to this disk";
|
||||
assert sectorSize == SECTOR_SIZE
|
||||
|| sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize;
|
||||
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : "
|
||||
+ interleave;
|
||||
assert sectorSize == SECTOR_SIZE || sectorSize == BLOCK_SIZE : "Invalid sector size : "
|
||||
+ sectorSize;
|
||||
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " + interleave;
|
||||
|
||||
if (sectorSize == SECTOR_SIZE)
|
||||
{
|
||||
|
@ -636,6 +665,13 @@ public class AppleDisk implements Disk
|
|||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public byte[] getBuffer ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return diskBuffer;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int getBufferOffset (DiskAddress da)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -684,11 +720,11 @@ public class AppleDisk implements Disk
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AppleFileSource getDetails ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return new DefaultAppleFileSource (toString (), file.getName (), null);
|
||||
}
|
||||
// private AppleFileSource getDetails ()
|
||||
// // ---------------------------------------------------------------------------------//
|
||||
// {
|
||||
// return new DefaultAppleFileSource (toString (), file.getName (), null);
|
||||
// }
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
|
@ -697,28 +733,29 @@ public class AppleDisk implements Disk
|
|||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
String path = file.getAbsolutePath ();
|
||||
String home = System.getProperty ("user.home");
|
||||
if (path.startsWith (home))
|
||||
path = "~" + path.substring (home.length ());
|
||||
|
||||
text.append (String.format ("Path................. %s%n", path));
|
||||
text.append (String.format ("File name............ %s%n", file.getName ()));
|
||||
text.append (String.format ("File size............ %,d%n", file.length ()));
|
||||
text.append (String.format ("Tracks............... %d%n", tracks));
|
||||
text.append (String.format ("Sectors.............. %d%n", sectors));
|
||||
text.append (String.format ("Blocks............... %,d%n", blocks));
|
||||
text.append (String.format ("Track size........... %,d%n", trackSize));
|
||||
text.append (String.format ("Sector size.......... %d%n", sectorSize));
|
||||
text.append (String.format ("Interleave........... %d", interleave));
|
||||
text.append (
|
||||
String.format ("Path ......... %s%n", Utility.getShortPath (file.getAbsolutePath ())));
|
||||
text.append (String.format ("File name .... %s%n", file.getName ()));
|
||||
text.append (String.format ("File size .... %,d%n", file.length ()));
|
||||
text.append (String.format ("Tracks ....... %d%n", tracks));
|
||||
text.append (String.format ("Sectors ...... %d%n", sectors));
|
||||
text.append (String.format ("Blocks ....... %,d%n", blocks));
|
||||
text.append (String.format ("Track size ... %,d%n", trackSize));
|
||||
text.append (String.format ("Sector size .. %d%n", sectorSize));
|
||||
text.append (String.format ("Interleave ... %d%n%n", interleave));
|
||||
|
||||
if (wozFile != null)
|
||||
{
|
||||
text.append ("\n\n");
|
||||
text.append (wozFile);
|
||||
}
|
||||
else if (nuFX != null)
|
||||
text.append (nuFX);
|
||||
else if (bin2 != null)
|
||||
text.append (bin2);
|
||||
else if (prefixDiskCopy != null)
|
||||
text.append (prefixDiskCopy);
|
||||
else if (prefix2mg != null)
|
||||
text.append (prefix2mg);
|
||||
|
||||
return text.toString ();
|
||||
return Utility.rtrim (text).toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
|
@ -35,7 +35,7 @@ public class AppleDiskAddress implements DiskAddress
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.owner = owner;
|
||||
zeroFlag = (track & 0x40) != 0;
|
||||
zeroFlag = (track & 0x40) == 0x40;
|
||||
this.track = track & 0x3F;
|
||||
this.sector = sector & 0x1F;
|
||||
this.block = this.track * owner.getBlocksPerTrack () + this.sector;
|
||||
|
|
|
@ -49,8 +49,8 @@ public class DefaultAppleFileSource implements AppleFileSource
|
|||
{
|
||||
this (title, file, owner);
|
||||
this.blocks = blocks;
|
||||
if (file instanceof DefaultDataSource)
|
||||
((DefaultDataSource) file).buffer = owner.getDisk ().readBlocks (blocks);
|
||||
if (file instanceof DefaultDataSource dds)
|
||||
dds.buffer = owner.getDisk ().readBlocks (blocks);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -58,8 +58,8 @@ public class DefaultAppleFileSource implements AppleFileSource
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.blocks = blocks;
|
||||
if (file instanceof DefaultDataSource)
|
||||
((DefaultDataSource) file).buffer = owner.getDisk ().readBlocks (blocks);
|
||||
if (file instanceof DefaultDataSource dds)
|
||||
dds.buffer = owner.getDisk ().readBlocks (blocks);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
|
|
@ -14,6 +14,7 @@ public class DefaultDataSource implements DataSource
|
|||
{
|
||||
public String text;
|
||||
byte[] buffer;
|
||||
Object textSource;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public DefaultDataSource (String text)
|
||||
|
@ -22,6 +23,13 @@ public class DefaultDataSource implements DataSource
|
|||
this.text = text;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public DefaultDataSource (Object textSource)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
this.textSource = textSource;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String getAssembler ()
|
||||
|
@ -40,6 +48,14 @@ public class DefaultDataSource implements DataSource
|
|||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public byte[] getBuffer ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public BufferedImage getImage ()
|
||||
|
@ -53,7 +69,7 @@ public class DefaultDataSource implements DataSource
|
|||
public String getText ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return text;
|
||||
return textSource == null ? text : textSource.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -61,7 +77,6 @@ public class DefaultDataSource implements DataSource
|
|||
public JComponent getComponent ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
System.out.println ("In DefaultDataSource.getComponent()");
|
||||
JPanel panel = new JPanel ();
|
||||
return panel;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.bytezone.diskbrowser.disk;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -18,10 +19,12 @@ import com.bytezone.diskbrowser.infocom.InfocomDisk;
|
|||
import com.bytezone.diskbrowser.nib.NibFile;
|
||||
import com.bytezone.diskbrowser.nib.V2dFile;
|
||||
import com.bytezone.diskbrowser.nib.WozFile;
|
||||
import com.bytezone.diskbrowser.nufx.Binary2;
|
||||
import com.bytezone.diskbrowser.nufx.NuFX;
|
||||
import com.bytezone.diskbrowser.pascal.PascalDisk;
|
||||
import com.bytezone.diskbrowser.prodos.ProdosDisk;
|
||||
import com.bytezone.diskbrowser.utilities.FileFormatException;
|
||||
import com.bytezone.diskbrowser.utilities.NuFX;
|
||||
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
import com.bytezone.diskbrowser.wizardry.Wizardry4BootDisk;
|
||||
import com.bytezone.diskbrowser.wizardry.WizardryScenarioDisk;
|
||||
|
@ -32,6 +35,13 @@ public class DiskFactory
|
|||
{
|
||||
private static boolean debug = false;
|
||||
|
||||
private static final int DISK_800K = 819200;
|
||||
private static final int DISK_143K = 143360;
|
||||
private static final int DISK_116K = 116480;
|
||||
|
||||
private static NuFX nuFX;
|
||||
private static Binary2 binary2;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private DiskFactory ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -46,32 +56,22 @@ public class DiskFactory
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static FormattedDisk createDisk (String path)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
FormattedDisk disk = create (path);
|
||||
// if (disk.getDisk ().getInterleave () > 0)
|
||||
// {
|
||||
// System.out.println (disk);
|
||||
// System.out.println ();
|
||||
// }
|
||||
return disk;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private static FormattedDisk create (String path)
|
||||
public static FormattedDisk createDisk (String pathName)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (debug)
|
||||
System.out.println ("\nFactory : " + path);
|
||||
System.out.println ("\nFactory : " + pathName);
|
||||
|
||||
File file = new File (path);
|
||||
nuFX = null;
|
||||
binary2 = null;
|
||||
|
||||
File file = new File (pathName);
|
||||
if (!file.exists ())
|
||||
return null;
|
||||
|
||||
String suffix = path.substring (path.lastIndexOf (".") + 1).toLowerCase ();
|
||||
String suffix = pathName.substring (pathName.lastIndexOf (".") + 1).toLowerCase ();
|
||||
Boolean compressed = false;
|
||||
Path originalPath = Paths.get (path);
|
||||
Path originalPath = Paths.get (pathName);
|
||||
|
||||
if ("gz".equals (suffix))
|
||||
{
|
||||
|
@ -79,7 +79,7 @@ public class DiskFactory
|
|||
System.out.println (" ** gzip **");
|
||||
try
|
||||
{
|
||||
InputStream in = new GZIPInputStream (new FileInputStream (path));
|
||||
InputStream in = new GZIPInputStream (new FileInputStream (pathName));
|
||||
File tmp = File.createTempFile ("gzip", null);
|
||||
FileOutputStream fos = new FileOutputStream (tmp);
|
||||
|
||||
|
@ -98,7 +98,7 @@ public class DiskFactory
|
|||
}
|
||||
catch (IOException e) // can get EOFException: Unexpected end of ZLIB input stream
|
||||
{
|
||||
e.printStackTrace ();
|
||||
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ public class DiskFactory
|
|||
System.out.println (" ** zip **");
|
||||
try
|
||||
{
|
||||
ZipFile zipFile = new ZipFile (path);
|
||||
ZipFile zipFile = new ZipFile (pathName);
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries ();
|
||||
|
||||
while (entries.hasMoreElements ()) // loop until first valid name
|
||||
|
@ -141,37 +141,88 @@ public class DiskFactory
|
|||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace ();
|
||||
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (suffix.equals ("sdk"))
|
||||
if ("sdk".equals (suffix) // NuFX disk
|
||||
|| "shk".equals (suffix) // NuFX files or disk
|
||||
|| "bxy".equals (suffix)) // NuFX in Bin2
|
||||
{
|
||||
if (debug)
|
||||
System.out.println (" ** sdk **");
|
||||
System.out.println (" ** sdk/shk/bxy **");
|
||||
try
|
||||
{
|
||||
NuFX nuFX = new NuFX (file);
|
||||
File tmp = File.createTempFile ("sdk", null);
|
||||
nuFX = new NuFX (file.toPath ());
|
||||
if (nuFX.getTotalDisks () == 0 && nuFX.getTotalFiles () == 0)
|
||||
{
|
||||
if (debug)
|
||||
System.out.println ("Empty NuFX file");
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] diskBuffer = nuFX.getDiskBuffer ();
|
||||
if (diskBuffer == null)
|
||||
return null;
|
||||
|
||||
File tmp = File.createTempFile (suffix, null);
|
||||
FileOutputStream fos = new FileOutputStream (tmp);
|
||||
fos.write (nuFX.getBuffer ());
|
||||
fos.write (diskBuffer);
|
||||
fos.close ();
|
||||
tmp.deleteOnExit ();
|
||||
file = tmp;
|
||||
suffix = "dsk";
|
||||
compressed = true;
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace ();
|
||||
// e.printStackTrace ();
|
||||
if (e.getMessage () == null)
|
||||
System.out.println (e);
|
||||
else
|
||||
System.out.println (e.getMessage ());
|
||||
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
|
||||
// System.out.println (nuFX);
|
||||
return null;
|
||||
}
|
||||
catch (FileFormatException e)
|
||||
}
|
||||
else if ("bny".equals (suffix) || "bqy".equals (suffix)) // Binary2 uncompressed files
|
||||
{
|
||||
if (debug)
|
||||
System.out.println (" ** bny/bqy **");
|
||||
try
|
||||
{
|
||||
binary2 = new Binary2 (file.toPath ());
|
||||
byte[] diskBuffer = binary2.getDiskBuffer ();
|
||||
|
||||
File tmp = File.createTempFile (suffix, null);
|
||||
FileOutputStream fos = new FileOutputStream (tmp);
|
||||
fos.write (diskBuffer);
|
||||
fos.close ();
|
||||
|
||||
tmp.deleteOnExit ();
|
||||
file = tmp;
|
||||
suffix = "dsk";
|
||||
compressed = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// e.printStackTrace ();
|
||||
System.out.println (e.getMessage ());
|
||||
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
|
||||
// System.out.println (binary2);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if ("bsq".equals (suffix))
|
||||
{
|
||||
if (debug)
|
||||
System.out.println (" ** bsq **");
|
||||
byte[] prefix = getPrefix (file);
|
||||
System.out.println (HexFormatter.format (prefix));
|
||||
String key = "FiLeStArTfIlEsTaRt";
|
||||
}
|
||||
|
||||
FormattedDisk disk = null;
|
||||
FormattedDisk disk2 = null;
|
||||
|
@ -221,38 +272,38 @@ public class DiskFactory
|
|||
|
||||
// Toolkit.do = 143488
|
||||
if (((suffix.equals ("po") || suffix.equals ("dsk") || suffix.equals ("do"))
|
||||
&& file.length () > 143360))
|
||||
&& file.length () > DISK_143K))
|
||||
{
|
||||
if (file.length () < 143500) // slightly bigger than a floppy
|
||||
{
|
||||
System.out.println ("File length is wrong: " + file.length ());
|
||||
disk = checkDos (new AppleDisk (file, 35, 16));
|
||||
if (disk != null)
|
||||
return disk;
|
||||
return check (disk);
|
||||
}
|
||||
|
||||
if (debug)
|
||||
System.out.printf (" Checking po or dsk hard drive: %,d%n", file.length ());
|
||||
System.out.printf ("Checking po or dsk hard drive: %,d%n", file.length ());
|
||||
|
||||
disk = checkHardDisk (file);
|
||||
if (disk != null)
|
||||
{
|
||||
if (compressed)
|
||||
disk.setOriginalPath (originalPath);
|
||||
return disk;
|
||||
return check (disk);
|
||||
}
|
||||
|
||||
if (file.length () == 819200) // 800K 3.5"
|
||||
if (file.length () == DISK_800K) // 800K 3.5"
|
||||
{
|
||||
if (debug)
|
||||
System.out.println ("UniDos ?");
|
||||
// 2 x 400k disk images
|
||||
AppleDisk appleDisk1 = new AppleDisk (file, 50, 32);
|
||||
AppleDisk appleDisk2 = new AppleDisk (file, 50, 32, (int) (file.length () / 2));
|
||||
AppleDisk appleDisk2 = new AppleDisk (file, 50, 32, 0x64000);
|
||||
disk = checkUnidos (appleDisk1, 1);
|
||||
disk2 = checkUnidos (appleDisk2, 2);
|
||||
if (disk != null && disk2 != null)
|
||||
return new DualDosDisk (disk, disk2);
|
||||
return new HybridDisk (disk, disk2);
|
||||
}
|
||||
|
||||
if (debug)
|
||||
|
@ -299,9 +350,10 @@ public class DiskFactory
|
|||
{
|
||||
if (debug)
|
||||
System.out.println (" --> PRODOS hard disk");
|
||||
return new ProdosDisk (disk800);
|
||||
disk = new ProdosDisk (disk800);
|
||||
}
|
||||
disk = new DataDisk (disk800);
|
||||
else
|
||||
disk = new DataDisk (disk800);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -309,11 +361,15 @@ public class DiskFactory
|
|||
disk = checkDos (appleDisk256);
|
||||
if (disk == null)
|
||||
disk = checkProdos (new AppleDisk (wozFile, 35, 8));
|
||||
if (disk == null)
|
||||
disk = checkPascalDisk (new AppleDisk (wozFile, 35, 8));
|
||||
if (disk == null)
|
||||
disk = new DataDisk (appleDisk256);
|
||||
}
|
||||
}
|
||||
|
||||
disk.setOriginalPath (originalPath); // allow Save converted disk...
|
||||
|
||||
return disk;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -348,7 +404,7 @@ public class DiskFactory
|
|||
|
||||
long length = file.length ();
|
||||
|
||||
if (length == 116480) // 13 sector disk
|
||||
if (length == DISK_116K) // 13 sector floppy disk
|
||||
{
|
||||
if (debug)
|
||||
System.out.println (" ** 13 sector **");
|
||||
|
@ -360,7 +416,7 @@ public class DiskFactory
|
|||
return disk == null ? new DataDisk (appleDisk) : disk;
|
||||
}
|
||||
|
||||
if (length != 143360)
|
||||
if (length != DISK_143K) // 16 sector floppy disk
|
||||
{
|
||||
System.out.printf ("%s: invalid file length : %,d%n", file.getName (),
|
||||
file.length ());
|
||||
|
@ -372,7 +428,7 @@ public class DiskFactory
|
|||
|
||||
if (true)
|
||||
{
|
||||
// long checksum = appleDisk256.getBootChecksum ();
|
||||
// long checksum = appleDisk256.getBootChecksum ();
|
||||
long checksum = 0;
|
||||
|
||||
if (checksum == 227968344L) // empty boot sector
|
||||
|
@ -397,13 +453,13 @@ public class DiskFactory
|
|||
if (debug)
|
||||
System.out.println (" known DOS checksum : " + checksum);
|
||||
disk = checkDos (appleDisk256);
|
||||
// disk2 = checkProdos (appleDisk512); // no need for this
|
||||
// disk2 = checkProdos (appleDisk512); // no need for this
|
||||
if (disk2 != null && disk != null) // should be impossible
|
||||
{
|
||||
if (debug)
|
||||
System.out.println (" --> Dual dos/prodos 1");
|
||||
System.out.println ("** impossible **");
|
||||
disk = new DualDosDisk (disk, disk2);
|
||||
disk = new HybridDisk (disk, disk2);
|
||||
}
|
||||
}
|
||||
else if (checksum == 1737448647L //
|
||||
|
@ -418,7 +474,7 @@ public class DiskFactory
|
|||
{
|
||||
if (debug)
|
||||
System.out.println (" --> Dual prodos/dos 2");
|
||||
disk = new DualDosDisk (disk, disk2);
|
||||
disk = new HybridDisk (disk, disk2);
|
||||
}
|
||||
}
|
||||
else if (checksum == 2803644711L // Apple Pascal disk 0
|
||||
|
@ -431,7 +487,7 @@ public class DiskFactory
|
|||
disk = checkPascalDisk (appleDisk512);
|
||||
disk2 = checkDos (appleDisk256);
|
||||
if (disk2 != null)
|
||||
disk = new DualDosDisk (disk, disk2);
|
||||
disk = new HybridDisk (disk, disk2);
|
||||
}
|
||||
else if (checksum == 3028642627L //
|
||||
|| checksum == 2070151659L) // Enchanter
|
||||
|
@ -443,22 +499,22 @@ public class DiskFactory
|
|||
else if (debug)
|
||||
System.out.println (" unknown checksum : " + checksum);
|
||||
|
||||
// else if (checksum == 1212926910L || checksum == 1365043894L
|
||||
// || checksum == 2128073918L)
|
||||
// disk = checkCPMDisk (file);
|
||||
// else if (checksum == 1212926910L || checksum == 1365043894L
|
||||
// || checksum == 2128073918L)
|
||||
// disk = checkCPMDisk (file);
|
||||
|
||||
// System.out.println (checksum);
|
||||
// System.out.println (checksum);
|
||||
|
||||
if (disk != null)
|
||||
{
|
||||
if (compressed)
|
||||
disk.setOriginalPath (originalPath);
|
||||
return disk;
|
||||
return check (disk);
|
||||
}
|
||||
|
||||
// empty boot sector
|
||||
if (checksum != 227968344L && false)
|
||||
System.out.println ("Unknown checksum : " + checksum + " : " + path);
|
||||
System.out.println ("Unknown checksum : " + checksum + " : " + pathName);
|
||||
}
|
||||
|
||||
if (debug)
|
||||
|
@ -478,12 +534,25 @@ public class DiskFactory
|
|||
|
||||
disk2 = checkProdos (appleDisk512);
|
||||
if (disk2 != null)
|
||||
disk = new DualDosDisk (disk, disk2);
|
||||
{
|
||||
disk = new HybridDisk (disk, disk2);
|
||||
return disk;
|
||||
}
|
||||
|
||||
AppleDisk appleDisk = new AppleDisk (file, 35, 16);
|
||||
disk2 = checkCPMDisk (appleDisk);
|
||||
if (disk2 != null)
|
||||
disk = new DualDosDisk (disk, disk2);
|
||||
{
|
||||
disk = new HybridDisk (disk, disk2);
|
||||
return disk;
|
||||
}
|
||||
|
||||
disk2 = checkPascalDisk (appleDisk512);
|
||||
if (disk2 != null)
|
||||
{
|
||||
disk = new HybridDisk (disk, disk2);
|
||||
return disk;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (suffix.equals ("po"))
|
||||
|
@ -516,6 +585,21 @@ public class DiskFactory
|
|||
if (disk != null && compressed)
|
||||
disk.setOriginalPath (originalPath);
|
||||
|
||||
return check (disk);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private static FormattedDisk check (FormattedDisk disk)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (disk.getDisk () instanceof AppleDisk appleDisk)
|
||||
{
|
||||
if (nuFX != null)
|
||||
appleDisk.setNuFX (nuFX);
|
||||
else if (binary2 != null)
|
||||
appleDisk.setBinary2 (binary2);
|
||||
}
|
||||
|
||||
return disk;
|
||||
}
|
||||
|
||||
|
@ -532,7 +616,10 @@ public class DiskFactory
|
|||
{
|
||||
if (debug)
|
||||
System.out.println (" --> DOS");
|
||||
return new DosDisk (disk);
|
||||
DosDisk dosDisk = new DosDisk (disk);
|
||||
// disk.setDosVersion (dosDisk.getVersion ());
|
||||
|
||||
return dosDisk;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -599,7 +686,7 @@ public class DiskFactory
|
|||
{
|
||||
if (debug)
|
||||
{
|
||||
System.out.println ("\nChecking Prodos hard disk");
|
||||
System.out.println ("\nChecking Prodos/Pascal hard disk");
|
||||
System.out.printf ("Total blocks : %f%n", (float) file.length () / 512);
|
||||
System.out.printf ("Total tracks : %f%n", (float) file.length () / 4096);
|
||||
System.out.printf ("File length : %d%n", file.length ());
|
||||
|
@ -622,13 +709,19 @@ public class DiskFactory
|
|||
{
|
||||
System.out.println ("*** extended ***"); // System Addons.hdv
|
||||
}
|
||||
AppleDisk disk = new AppleDisk (file, tracks, 8);
|
||||
AppleDisk disk;
|
||||
// if (nuFX == null)
|
||||
disk = new AppleDisk (file, tracks, 8);
|
||||
// else
|
||||
// disk = new AppleDisk (file, tracks, 8, nuFX);
|
||||
|
||||
if (ProdosDisk.isCorrectFormat (disk))
|
||||
{
|
||||
if (debug)
|
||||
System.out.println (" --> PRODOS hard disk");
|
||||
return new ProdosDisk (disk);
|
||||
}
|
||||
|
||||
if (PascalDisk.isCorrectFormat (disk, debug))
|
||||
{
|
||||
if (debug)
|
||||
|
@ -639,11 +732,12 @@ public class DiskFactory
|
|||
catch (Exception e)
|
||||
{
|
||||
System.out.println (e);
|
||||
e.printStackTrace ();
|
||||
System.out.println ("Prodos hard disk had error");
|
||||
}
|
||||
|
||||
if (debug)
|
||||
System.out.println (" not a Prodos hard disk\n");
|
||||
System.out.println (" not a Prodos/Pascal hard disk\n");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -768,11 +862,16 @@ public class DiskFactory
|
|||
|
||||
if (Wizardry4BootDisk.isWizardryIVorV (disk, debug))
|
||||
{
|
||||
if (debug)
|
||||
System.out.println ("checking Wizardry IV or V");
|
||||
|
||||
String fileName = file.getAbsolutePath ().toLowerCase ();
|
||||
int pos = file.getAbsolutePath ().indexOf ('.');
|
||||
char c = fileName.charAt (pos - 1);
|
||||
// String suffix = fileName.substring (pos + 1);
|
||||
int requiredDisks = c == '1' ? 6 : c == 'a' ? 10 : 0;
|
||||
int pos = fileName.lastIndexOf ('.');
|
||||
char c = fileName.charAt (pos - 1); // '1' (wiz4) or 'a' (wiz5)
|
||||
int requiredDisks = c == '1' ? 7 : c == 'a' ? 10 : 0;
|
||||
|
||||
if (debug)
|
||||
System.out.printf ("Required disks: %d%n", requiredDisks);
|
||||
|
||||
if (requiredDisks > 0)
|
||||
{
|
||||
|
@ -793,7 +892,7 @@ public class DiskFactory
|
|||
}
|
||||
}
|
||||
if (debug)
|
||||
System.out.println ("Not a Wizardry IV disk");
|
||||
System.out.println ("Not a Wizardry IV or V disk");
|
||||
|
||||
PascalDisk pascalDisk = new PascalDisk (disk);
|
||||
return pascalDisk;
|
||||
|
@ -803,6 +902,9 @@ public class DiskFactory
|
|||
private static boolean collectDataDisks (String fileName, int dotPos, AppleDisk[] disks)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (debug)
|
||||
System.out.println ("Collecting Wizardry disks");
|
||||
|
||||
char c = fileName.charAt (dotPos - 1);
|
||||
String suffix = fileName.substring (dotPos + 1);
|
||||
|
||||
|
@ -810,14 +912,16 @@ public class DiskFactory
|
|||
{
|
||||
String old = new String (c + "." + suffix);
|
||||
String rep = new String ((char) (c + i - 1) + "." + suffix);
|
||||
|
||||
File f = new File (fileName.replace (old, rep));
|
||||
|
||||
if (debug)
|
||||
System.out.println (f);
|
||||
|
||||
if (!f.exists () || !f.isFile ())
|
||||
return false;
|
||||
|
||||
AppleDisk dataDisk = new AppleDisk (f, 35, 8);
|
||||
dataDisk.setInterleave (1);
|
||||
disks[i] = dataDisk;
|
||||
disks[i] = new AppleDisk (f, 35, 8);
|
||||
disks[i].setInterleave (1);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -858,4 +962,22 @@ public class DiskFactory
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private static byte[] getPrefix (File file)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
byte[] buffer = new byte[1024];
|
||||
try (BufferedInputStream bis = new BufferedInputStream (new FileInputStream (file)))
|
||||
{
|
||||
bis.read (buffer);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace ();
|
||||
System.exit (1);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
|
@ -81,7 +81,4 @@ public interface FormattedDisk
|
|||
public int falseNegativeBlocks ();
|
||||
|
||||
public String getName ();
|
||||
}
|
||||
|
||||
// getFileTypeList ()
|
||||
// getFiles (FileType type)
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.bytezone.diskbrowser.disk;
|
|||
|
||||
import java.awt.Dimension;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JTree;
|
||||
|
@ -16,21 +17,21 @@ import com.bytezone.diskbrowser.gui.DataSource;
|
|||
// Should be renamed MultiVolumeDisk (and allow >2 volumes)
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class DualDosDisk implements FormattedDisk
|
||||
public class HybridDisk implements FormattedDisk
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private final FormattedDisk[] disks = new FormattedDisk[2];
|
||||
private final List<FormattedDisk> disks = new ArrayList<> (2);
|
||||
private int currentDisk;
|
||||
private final JTree tree;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public DualDosDisk (FormattedDisk disk0, FormattedDisk disk1)
|
||||
public HybridDisk (FormattedDisk disk0, FormattedDisk disk1)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
assert disk0 != disk1;
|
||||
String diskName = disk0.getDisk ().getFile ().getName ();
|
||||
String text = "This disk contains files from DOS and another OS\n\n"
|
||||
+ disk0.getDisk () + "\n\n" + disk1.getDisk ();
|
||||
String text = "This disk is a hybrid of two or more OS\n\n" + disk0.getDisk ()
|
||||
+ "\n\n" + disk1.getDisk ();
|
||||
|
||||
DefaultAppleFileSource dafs = new DefaultAppleFileSource (diskName, text, this);
|
||||
DefaultMutableTreeNode root = new DefaultMutableTreeNode (dafs);
|
||||
|
@ -41,8 +42,8 @@ public class DualDosDisk implements FormattedDisk
|
|||
// allow empty nodes to appear as folders
|
||||
treeModel.setAsksAllowsChildren (true);
|
||||
|
||||
disks[0] = disk0;
|
||||
disks[1] = disk1;
|
||||
disks.add (disk0);
|
||||
disks.add (disk1);
|
||||
|
||||
disk0.setParent (this);
|
||||
disk1.setParent (this);
|
||||
|
@ -69,7 +70,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public List<DiskAddress> getFileSectors (int fileNo)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getFileSectors (fileNo);
|
||||
return disks.get (currentDisk).getFileSectors (fileNo);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -77,7 +78,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public List<AppleFileSource> getCatalogList ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getCatalogList ();
|
||||
return disks.get (currentDisk).getCatalogList ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -85,7 +86,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public DataSource getFormattedSector (DiskAddress da)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getFormattedSector (da);
|
||||
return disks.get (currentDisk).getFormattedSector (da);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -93,7 +94,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public SectorType getSectorType (DiskAddress da)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getSectorType (da);
|
||||
return disks.get (currentDisk).getSectorType (da);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -101,7 +102,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public SectorType getSectorType (int track, int sector)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getSectorType (track, sector);
|
||||
return disks.get (currentDisk).getSectorType (track, sector);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -109,7 +110,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public SectorType getSectorType (int block)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getSectorType (block);
|
||||
return disks.get (currentDisk).getSectorType (block);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -117,7 +118,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public List<SectorType> getSectorTypeList ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getSectorTypeList ();
|
||||
return disks.get (currentDisk).getSectorTypeList ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -125,23 +126,30 @@ public class DualDosDisk implements FormattedDisk
|
|||
public Disk getDisk ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getDisk ();
|
||||
return disks.get (currentDisk).getDisk ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void setCurrentDisk (FormattedDisk fd)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (disks[0] == fd)
|
||||
currentDisk = 0;
|
||||
else if (disks[1] == fd)
|
||||
currentDisk = 1;
|
||||
else
|
||||
{
|
||||
// this happens when the top-level folder is selected (i.e. neither disk)
|
||||
System.out.println ("Disk not found: " + fd);
|
||||
// Utility.printStackTrace ();
|
||||
}
|
||||
for (int i = 0; i < disks.size (); i++)
|
||||
if (disks.get (i) == fd)
|
||||
{
|
||||
currentDisk = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// if (disks[0] == fd)
|
||||
// currentDisk = 0;
|
||||
// else if (disks[1] == fd)
|
||||
// currentDisk = 1;
|
||||
// else
|
||||
// {
|
||||
// // this happens when the top-level folder is selected (i.e. neither disk)
|
||||
// System.out.println ("Disk not found: " + fd);
|
||||
// // Utility.printStackTrace ();
|
||||
// }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -162,7 +170,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public FormattedDisk getCurrentDisk ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk];
|
||||
return disks.get (currentDisk);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -170,7 +178,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public void writeFile (AbstractFile file)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disks[currentDisk].writeFile (file);
|
||||
disks.get (currentDisk).writeFile (file);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -178,10 +186,18 @@ public class DualDosDisk implements FormattedDisk
|
|||
public AppleFileSource getCatalog ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return new DefaultAppleFileSource ("text",
|
||||
disks[0].getCatalog ().getDataSource ().getText () + "\n\n"
|
||||
+ disks[1].getCatalog ().getDataSource ().getText (),
|
||||
this);
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
for (FormattedDisk disk : disks)
|
||||
{
|
||||
text.append (disk.getCatalog ().getDataSource ().getText ());
|
||||
text.append ("\n\n");
|
||||
}
|
||||
|
||||
text.deleteCharAt (text.length () - 1);
|
||||
text.deleteCharAt (text.length () - 1);
|
||||
|
||||
return new DefaultAppleFileSource ("text", text.toString (), this);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -189,7 +205,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public AppleFileSource getFile (String uniqueName)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getFile (uniqueName);
|
||||
return disks.get (currentDisk).getFile (uniqueName);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -197,7 +213,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public int clearOrphans ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].clearOrphans ();
|
||||
return disks.get (currentDisk).clearOrphans ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -205,7 +221,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public boolean isSectorFree (DiskAddress da)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].isSectorFree (da);
|
||||
return disks.get (currentDisk).isSectorFree (da);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -213,7 +229,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public void verify ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disks[currentDisk].verify ();
|
||||
disks.get (currentDisk).verify ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -221,7 +237,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public boolean stillAvailable (DiskAddress da)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].stillAvailable (da);
|
||||
return disks.get (currentDisk).stillAvailable (da);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -229,7 +245,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public void setSectorType (int block, SectorType type)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disks[currentDisk].setSectorType (block, type);
|
||||
disks.get (currentDisk).setSectorType (block, type);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -237,7 +253,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public void setSectorFree (int block, boolean free)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disks[currentDisk].setSectorFree (block, free);
|
||||
disks.get (currentDisk).setSectorFree (block, free);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -245,7 +261,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public int falseNegativeBlocks ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].falseNegativeBlocks ();
|
||||
return disks.get (currentDisk).falseNegativeBlocks ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -253,7 +269,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public int falsePositiveBlocks ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].falsePositiveBlocks ();
|
||||
return disks.get (currentDisk).falsePositiveBlocks ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -261,7 +277,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public Dimension getGridLayout ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getGridLayout ();
|
||||
return disks.get (currentDisk).getGridLayout ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -269,7 +285,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public boolean isSectorFree (int block)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].isSectorFree (block);
|
||||
return disks.get (currentDisk).isSectorFree (block);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -277,7 +293,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public boolean stillAvailable (int block)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].stillAvailable (block);
|
||||
return disks.get (currentDisk).stillAvailable (block);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -285,7 +301,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public void setOriginalPath (Path path)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disks[currentDisk].setOriginalPath (path);
|
||||
disks.get (currentDisk).setOriginalPath (path);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -293,7 +309,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public String getAbsolutePath ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getAbsolutePath ();
|
||||
return disks.get (currentDisk).getAbsolutePath ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -301,7 +317,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public String getDisplayPath ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getDisplayPath ();
|
||||
return disks.get (currentDisk).getDisplayPath ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -309,7 +325,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public FormattedDisk getParent ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getParent ();
|
||||
return disks.get (currentDisk).getParent ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -317,7 +333,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public void setParent (FormattedDisk disk)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disks[currentDisk].setParent (disk);
|
||||
disks.get (currentDisk).setParent (disk);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -325,7 +341,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public String getSectorFilename (DiskAddress da)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getSectorFilename (da);
|
||||
return disks.get (currentDisk).getSectorFilename (da);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -333,7 +349,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public String getName ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getName ();
|
||||
return disks.get (currentDisk).getName ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -341,7 +357,7 @@ public class DualDosDisk implements FormattedDisk
|
|||
public boolean isTempDisk ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].isTempDisk ();
|
||||
return disks.get (currentDisk).isTempDisk ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -349,6 +365,6 @@ public class DualDosDisk implements FormattedDisk
|
|||
public Path getOriginalPath ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return disks[currentDisk].getOriginalPath ();
|
||||
return disks.get (currentDisk).getOriginalPath ();
|
||||
}
|
||||
}
|
|
@ -7,13 +7,25 @@ import com.bytezone.diskbrowser.utilities.Utility;
|
|||
public class Prefix2mg
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
String[] creators = { "!nfc", "B2TR", "CTKG", "CdrP", "ShIm", "WOOF", "XGS!" };
|
||||
String[] images = { "Dos3.3", "Prodos", "Nibbized" };
|
||||
|
||||
String prefix;
|
||||
String creator;
|
||||
int headerSize;
|
||||
int version;
|
||||
byte format;
|
||||
int diskData;
|
||||
int format;
|
||||
int flags;
|
||||
int length;
|
||||
int blocks;
|
||||
int offset;
|
||||
int commentOffset;
|
||||
int commentLength;
|
||||
int creatorOffset;
|
||||
int creatorLength;
|
||||
|
||||
boolean flagsLocked;
|
||||
int flagsVolume;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public Prefix2mg (byte[] buffer)
|
||||
|
@ -21,14 +33,29 @@ public class Prefix2mg
|
|||
{
|
||||
prefix = new String (buffer, 0, 4);
|
||||
creator = new String (buffer, 4, 4);
|
||||
headerSize = Utility.getWord (buffer, 8);
|
||||
version = Utility.getWord (buffer, 10);
|
||||
format = buffer[12];
|
||||
headerSize = Utility.getShort (buffer, 0x08);
|
||||
version = Utility.getShort (buffer, 0x0A);
|
||||
format = Utility.getLong (buffer, 0x0C);
|
||||
flags = Utility.getLong (buffer, 0x10);
|
||||
blocks = Utility.getLong (buffer, 0x14); // 1600
|
||||
offset = Utility.getLong (buffer, 0x18);
|
||||
length = Utility.getLong (buffer, 0x1C);
|
||||
commentOffset = Utility.getLong (buffer, 0x20);
|
||||
commentLength = Utility.getLong (buffer, 0x24);
|
||||
creatorOffset = Utility.getLong (buffer, 0x28);
|
||||
creatorLength = Utility.getLong (buffer, 0x2C);
|
||||
|
||||
diskData = Utility.getLong (buffer, 28);
|
||||
blocks = Utility.intValue (buffer[20], buffer[21]); // 1600
|
||||
flagsLocked = (flags & 0x80000000) != 0;
|
||||
if ((flags & 0x0100) != 0)
|
||||
flagsVolume = flags & 0xFF;
|
||||
if (format == 0 && flagsVolume == 0)
|
||||
flagsVolume = 254;
|
||||
|
||||
if (length == 0)
|
||||
length = 512 * blocks;
|
||||
|
||||
// see /Asimov disks/images/gs/os/prodos16/ProDOS 16v1_3.2mg
|
||||
// System.out.println (this);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -38,14 +65,21 @@ public class Prefix2mg
|
|||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append (String.format ("Prefix : %s%n", prefix));
|
||||
text.append (String.format ("Creator : %s%n", creator));
|
||||
text.append (String.format ("Header : %d%n", headerSize));
|
||||
text.append (String.format ("Version : %d%n", version));
|
||||
text.append (String.format ("Format : %02X%n", format));
|
||||
|
||||
text.append (String.format ("Data size : %08X (%<,d)%n", diskData));
|
||||
text.append (String.format ("Blocks : %,d%n", blocks));
|
||||
text.append (String.format ("Prefix : %s%n", prefix));
|
||||
text.append (String.format ("Creator : %s%n", creator));
|
||||
text.append (String.format ("Header : %d%n", headerSize));
|
||||
text.append (String.format ("Version : %d%n", version));
|
||||
text.append (String.format ("Format : %02X%n", format));
|
||||
text.append (String.format ("Flags : %,d%n", flags));
|
||||
text.append (String.format ("Locked : %s%n", flagsLocked));
|
||||
text.append (String.format ("DOS Volume : %,d%n", flagsVolume));
|
||||
text.append (String.format ("Blocks : %,d%n", blocks));
|
||||
text.append (String.format ("Offset : %,d%n", offset));
|
||||
text.append (String.format ("Length : %08X (%<,d)%n", length));
|
||||
text.append (String.format ("Comment Offset : %,d%n", commentOffset));
|
||||
text.append (String.format ("Comment Length : %08X (%<,d)%n", commentLength));
|
||||
text.append (String.format ("Creator Offset : %,d%n", creatorOffset));
|
||||
text.append (String.format ("Creator Length : %08X (%<,d)", creatorLength));
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
|
|||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// https://www.discferret.com/wiki/Apple_DiskCopy_4.2
|
||||
// Apple II File Type Notes $E0/0005 (macintosh)
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class PrefixDiskCopy
|
||||
// -----------------------------------------------------------------------------------//
|
||||
|
@ -11,9 +12,11 @@ public class PrefixDiskCopy
|
|||
private String name;
|
||||
private int dataSize;
|
||||
private int tagSize;
|
||||
private int encoding;
|
||||
private int dataChecksum;
|
||||
private int tagChecksum;
|
||||
private int diskFormat;
|
||||
private int format;
|
||||
private int id;
|
||||
private int id; // should be 0x0100
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public PrefixDiskCopy (byte[] buffer)
|
||||
|
@ -24,9 +27,11 @@ public class PrefixDiskCopy
|
|||
name = HexFormatter.getPascalString (buffer, 0);
|
||||
dataSize = Utility.getLongBigEndian (buffer, 0x40);
|
||||
tagSize = Utility.getLongBigEndian (buffer, 0x44);
|
||||
encoding = buffer[0x50] & 0xFF;
|
||||
dataChecksum = Utility.getLongBigEndian (buffer, 0x48);
|
||||
tagChecksum = Utility.getLongBigEndian (buffer, 0x4C);
|
||||
diskFormat = buffer[0x50] & 0xFF;
|
||||
format = buffer[0x51] & 0xFF;
|
||||
id = Utility.getWordBigEndian (buffer, 0x52);
|
||||
id = Utility.getShortBigEndian (buffer, 0x52);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -43,12 +48,14 @@ public class PrefixDiskCopy
|
|||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append (String.format ("Name : %s%n", name));
|
||||
text.append (String.format ("Data size : %08X (%<,d)%n", dataSize));
|
||||
text.append (String.format ("Tag size : %08X (%<,d)%n", tagSize));
|
||||
text.append (String.format ("Encoding : %02X%n", encoding));
|
||||
text.append (String.format ("Format : %02X%n", format));
|
||||
text.append (String.format ("ID : %04X%n", id));
|
||||
text.append (String.format ("Name : %s%n", name));
|
||||
text.append (String.format ("Data size : %08X (%<,d)%n", dataSize));
|
||||
text.append (String.format ("Tag size : %08X (%<,d)%n", tagSize));
|
||||
text.append (String.format ("Data checksum : %08X (%<,d)%n", dataChecksum));
|
||||
text.append (String.format ("Tag checksum : %08X (%<,d)%n", tagChecksum));
|
||||
text.append (String.format ("Disk format : %02X%n", diskFormat));
|
||||
text.append (String.format ("Format byte : %02X%n", format));
|
||||
text.append (String.format ("ID : %04X%n", id));
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
|
|
@ -17,8 +17,7 @@ public class SectorListConverter
|
|||
sectors = new ArrayList<> ();
|
||||
sectorText = text;
|
||||
|
||||
String[] blocks = text.split (";");
|
||||
for (String s : blocks)
|
||||
for (String s : text.split (";"))
|
||||
{
|
||||
int pos = s.indexOf ('-');
|
||||
if (pos > 0)
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
package com.bytezone.diskbrowser.dos;
|
||||
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.AA;
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.ApplesoftBasic;
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.BB;
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.Binary;
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.IntegerBasic;
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.Relocatable;
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.SS;
|
||||
import static com.bytezone.diskbrowser.dos.DosDisk.FileType.Text;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -7,18 +16,18 @@ import java.util.List;
|
|||
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
||||
import com.bytezone.diskbrowser.applefile.ApplesoftBasicProgram;
|
||||
import com.bytezone.diskbrowser.applefile.AssemblerProgram;
|
||||
import com.bytezone.diskbrowser.applefile.BasicTextFile;
|
||||
import com.bytezone.diskbrowser.applefile.DefaultAppleFile;
|
||||
import com.bytezone.diskbrowser.applefile.DoubleHiResImage;
|
||||
import com.bytezone.diskbrowser.applefile.ErrorMessageFile;
|
||||
import com.bytezone.diskbrowser.applefile.FontFile;
|
||||
import com.bytezone.diskbrowser.applefile.HiResImage;
|
||||
import com.bytezone.diskbrowser.applefile.IntegerBasicProgram;
|
||||
import com.bytezone.diskbrowser.applefile.MagicWindowText;
|
||||
import com.bytezone.diskbrowser.applefile.MerlinSource;
|
||||
import com.bytezone.diskbrowser.applefile.OriginalHiResImage;
|
||||
import com.bytezone.diskbrowser.applefile.PrintShopGraphic;
|
||||
import com.bytezone.diskbrowser.applefile.ShapeTable;
|
||||
import com.bytezone.diskbrowser.applefile.SimpleText2;
|
||||
import com.bytezone.diskbrowser.applefile.BasicTextFile;
|
||||
import com.bytezone.diskbrowser.applefile.VisicalcFile;
|
||||
import com.bytezone.diskbrowser.disk.Disk;
|
||||
import com.bytezone.diskbrowser.disk.DiskAddress;
|
||||
|
@ -35,6 +44,7 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
protected DosDisk dosDisk;
|
||||
protected String name;
|
||||
protected String catalogName;
|
||||
protected String displayName;
|
||||
|
||||
protected FileType fileType;
|
||||
protected int reportedSize;
|
||||
|
@ -57,42 +67,32 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
this.catalogSectorDA = catalogSector;
|
||||
|
||||
name = getName ("", entryBuffer);
|
||||
reportedSize = Utility.unsignedShort (entryBuffer, 33);
|
||||
// if (reportedSize == 0)
|
||||
// System.out.printf ("%s size 0%n", name);
|
||||
reportedSize = Utility.getShort (entryBuffer, 33);
|
||||
|
||||
int type = entryBuffer[2] & 0x7F;
|
||||
locked = (entryBuffer[2] & 0x80) != 0;
|
||||
|
||||
if (type == 0)
|
||||
fileType = FileType.Text;
|
||||
else if ((type == 0x01))
|
||||
fileType = FileType.IntegerBasic;
|
||||
else if ((type == 0x02))
|
||||
fileType = FileType.ApplesoftBasic;
|
||||
else if ((type < 0x08))
|
||||
fileType = FileType.Binary;
|
||||
else if ((type < 0x10))
|
||||
fileType = FileType.SS;
|
||||
else if ((type < 0x20))
|
||||
fileType = FileType.Relocatable;
|
||||
else if ((type < 0x40))
|
||||
fileType = FileType.AA;
|
||||
// else if ((type == 0x40)) // Lisa
|
||||
else
|
||||
fileType = FileType.BB;
|
||||
// else
|
||||
// {
|
||||
// System.out.println ("Unknown file type : " + type);
|
||||
// }
|
||||
fileType = switch (type)
|
||||
{
|
||||
case 0x00 -> Text;
|
||||
case 0x01 -> IntegerBasic;
|
||||
case 0x02 -> ApplesoftBasic;
|
||||
case 0x04 -> Binary;
|
||||
case 0x08 -> SS;
|
||||
case 0x10 -> Relocatable;
|
||||
case 0x20 -> AA;
|
||||
case 0x40 -> BB;
|
||||
default -> Binary; // should never happen
|
||||
};
|
||||
|
||||
if (dosDisk.getVersion () >= 0x41)
|
||||
lastModified = Utility.getDateTime (entryBuffer, 0x1B);
|
||||
|
||||
// CATALOG command only formats the LO byte - see Beneath Apple DOS pp4-6
|
||||
String base =
|
||||
String.format ("%s%s %03d ", (locked) ? "*" : " ", getFileType (), reportedSize);
|
||||
catalogName = getName (base, entryBuffer).replace ("^", "");
|
||||
String.format ("%s%s %03d ", locked ? "*" : " ", getFileType (), reportedSize & 0xFF);
|
||||
catalogName = getName (base, entryBuffer);
|
||||
displayName = getDisplayName (entryBuffer);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -108,7 +108,7 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
for (int i = 3; i < max; i++)
|
||||
{
|
||||
int c = buffer[i] & 0xFF;
|
||||
if (c == 136 && !base.isEmpty ()) // allow backspaces
|
||||
if ((c == 0x88 || c == 0x8A) && !base.isEmpty ()) // allow backspaces
|
||||
{
|
||||
if (text.length () > 0)
|
||||
text.deleteCharAt (text.length () - 1);
|
||||
|
@ -116,8 +116,41 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
}
|
||||
if (c > 127)
|
||||
c -= c < 160 ? 64 : 128;
|
||||
// c &= 0x7F;
|
||||
if (c < 32)
|
||||
text.append ("^" + (char) (c + 64)); // non-printable ascii
|
||||
text.append ((char) (c + 64)); // non-printable ascii
|
||||
else
|
||||
text.append ((char) c); // standard ascii
|
||||
}
|
||||
|
||||
while (text.length () > 0 && text.charAt (text.length () - 1) == ' ')
|
||||
text.deleteCharAt (text.length () - 1); // rtrim()
|
||||
// System.out.println (text.toString ());
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private String getDisplayName (byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
int max = buffer[0] == (byte) 0xFF ? 32 : 33;
|
||||
if (dosDisk.getVersion () >= 0x41)
|
||||
max = 27;
|
||||
|
||||
for (int i = 3; i < max; i++)
|
||||
{
|
||||
int c = buffer[i] & 0xFF;
|
||||
if ((c == 0x88 || c == 0x8A)) // don't allow backspaces
|
||||
continue;
|
||||
|
||||
if (c > 127)
|
||||
c -= c < 160 ? 64 : 128;
|
||||
// c &= 0x7F;
|
||||
if (c < 32)
|
||||
text.append ((char) (c + 64)); // non-printable ascii
|
||||
else
|
||||
text.append ((char) c); // standard ascii
|
||||
}
|
||||
|
@ -201,29 +234,40 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
break;
|
||||
|
||||
case IntegerBasic:
|
||||
reportedLength = Utility.unsignedShort (buffer, 0);
|
||||
exactBuffer = new byte[reportedLength];
|
||||
System.arraycopy (buffer, 2, exactBuffer, 0, reportedLength);
|
||||
appleFile = new IntegerBasicProgram (name, exactBuffer);
|
||||
reportedLength = Utility.getShort (buffer, 0);
|
||||
if (reportedLength > 0)
|
||||
{
|
||||
exactBuffer = new byte[reportedLength];
|
||||
System.arraycopy (buffer, 2, exactBuffer, 0, reportedLength);
|
||||
appleFile = new IntegerBasicProgram (name, exactBuffer);
|
||||
}
|
||||
else
|
||||
appleFile = new DefaultAppleFile (name, buffer);
|
||||
break;
|
||||
|
||||
case ApplesoftBasic:
|
||||
reportedLength = Utility.unsignedShort (buffer, 0);
|
||||
exactBuffer = new byte[reportedLength];
|
||||
if (reportedLength > buffer.length)
|
||||
reportedLength = buffer.length - 2;
|
||||
System.arraycopy (buffer, 2, exactBuffer, 0, reportedLength);
|
||||
appleFile = new ApplesoftBasicProgram (name, exactBuffer);
|
||||
reportedLength = Utility.getShort (buffer, 0);
|
||||
if (reportedLength > 0)
|
||||
{
|
||||
exactBuffer = new byte[reportedLength];
|
||||
if (reportedLength > buffer.length)
|
||||
reportedLength = buffer.length - 2;
|
||||
System.arraycopy (buffer, 2, exactBuffer, 0, reportedLength);
|
||||
appleFile = new ApplesoftBasicProgram (name, exactBuffer);
|
||||
}
|
||||
else
|
||||
appleFile = new DefaultAppleFile (name, buffer);
|
||||
break;
|
||||
|
||||
case Binary: // binary file
|
||||
case Relocatable: // relocatable binary file
|
||||
int loadAddress = Utility.unsignedShort (buffer, 0);
|
||||
reportedLength = Utility.unsignedShort (buffer, 2);
|
||||
case BB:
|
||||
int loadAddress = Utility.getShort (buffer, 0);
|
||||
reportedLength = Utility.getShort (buffer, 2);
|
||||
if (reportedLength == 0)
|
||||
{
|
||||
System.out.println (name.trim () + " reported length : 0 - reverting to "
|
||||
+ (buffer.length - 4));
|
||||
System.out.println (
|
||||
name.trim () + " reported length : 0 - reverting to " + (buffer.length - 4));
|
||||
reportedLength = buffer.length - 4;
|
||||
}
|
||||
|
||||
|
@ -234,10 +278,11 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
exactBuffer = new byte[buffer.length - 4]; // reported length is too long
|
||||
System.arraycopy (buffer, 4, exactBuffer, 0, exactBuffer.length);
|
||||
|
||||
if ((name.endsWith (".FONT") || name.endsWith (" FONT")
|
||||
|| name.endsWith (".SET") || name.startsWith ("ASCII."))
|
||||
&& FontFile.isFont (exactBuffer))
|
||||
if ((name.endsWith (".FONT") || name.endsWith (" FONT") || name.endsWith (".SET")
|
||||
|| name.startsWith ("ASCII.")) && FontFile.isFont (exactBuffer))
|
||||
appleFile = new FontFile (name, exactBuffer, loadAddress);
|
||||
else if (name.endsWith (".MW"))
|
||||
appleFile = new MagicWindowText (name, exactBuffer);
|
||||
else if (ShapeTable.isShapeTable (exactBuffer))
|
||||
appleFile = new ShapeTable (name, exactBuffer);
|
||||
else if (name.endsWith (".S"))
|
||||
|
@ -269,8 +314,7 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
appleFile = new AssemblerProgram (name, exactBuffer, loadAddress);
|
||||
}
|
||||
else if (reportedLength == 0x240 //
|
||||
&& (loadAddress == 0x5800 || loadAddress == 0x6000
|
||||
|| loadAddress == 0x7800))
|
||||
&& (loadAddress == 0x5800 || loadAddress == 0x6000 || loadAddress == 0x7800))
|
||||
appleFile = new PrintShopGraphic (name, exactBuffer);
|
||||
else if (isRunCommand (exactBuffer))
|
||||
{
|
||||
|
@ -283,8 +327,8 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
{
|
||||
appleFile = new AssemblerProgram (name, exactBuffer, loadAddress);
|
||||
if ((exactBuffer.length + 4) < buffer.length)
|
||||
((AssemblerProgram) appleFile).setExtraBuffer (buffer,
|
||||
exactBuffer.length + 4, buffer.length - (exactBuffer.length + 4));
|
||||
((AssemblerProgram) appleFile).setExtraBuffer (buffer, exactBuffer.length + 4,
|
||||
buffer.length - (exactBuffer.length + 4));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -298,13 +342,10 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
appleFile = new DefaultAppleFile (name, buffer);
|
||||
break;
|
||||
|
||||
case BB: // Lisa
|
||||
loadAddress = Utility.intValue (buffer[0], buffer[1]);
|
||||
reportedLength = Utility.intValue (buffer[2], buffer[3]);
|
||||
exactBuffer = new byte[reportedLength];
|
||||
System.arraycopy (buffer, 4, exactBuffer, 0, reportedLength);
|
||||
appleFile = new SimpleText2 (name, exactBuffer, loadAddress);
|
||||
break;
|
||||
// case BB: // Lisa
|
||||
// loadAddress = Utility.intValue (buffer[0], buffer[1]);
|
||||
// appleFile = new SimpleText2 (name, getExactBuffer (buffer), loadAddress);
|
||||
// break;
|
||||
|
||||
default:
|
||||
System.out.println ("Unknown file type : " + fileType);
|
||||
|
@ -326,11 +367,11 @@ abstract class AbstractCatalogEntry implements AppleFileSource
|
|||
{
|
||||
byte[] exactBuffer;
|
||||
|
||||
int reportedLength = Utility.unsignedShort (buffer, 2);
|
||||
int reportedLength = Utility.getShort (buffer, 2);
|
||||
if (reportedLength == 0)
|
||||
{
|
||||
System.out.println (
|
||||
name.trim () + " reported length : 0 - reverting to " + (buffer.length - 4));
|
||||
System.out
|
||||
.println (name.trim () + " reported length : 0 - reverting to " + (buffer.length - 4));
|
||||
reportedLength = buffer.length - 4;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,12 +131,12 @@ class CatalogEntry extends AbstractCatalogEntry
|
|||
{
|
||||
case IntegerBasic:
|
||||
case ApplesoftBasic:
|
||||
length = Utility.intValue (buffer[0], buffer[1]);
|
||||
length = Utility.getShort (buffer, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
address = Utility.intValue (buffer[0], buffer[1]);
|
||||
length = Utility.intValue (buffer[2], buffer[3]);
|
||||
address = Utility.getShort (buffer, 0);
|
||||
length = Utility.getShort (buffer, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,16 +172,18 @@ class CatalogEntry extends AbstractCatalogEntry
|
|||
message = lastModified.toString ().replace ('T', ' ');
|
||||
|
||||
if (reportedSize != actualSize)
|
||||
message += "Bad size (" + reportedSize + ") ";
|
||||
if (dataSectors.size () == 0)
|
||||
message += "No data ";
|
||||
message += "Actual size (" + actualSize + ") ";
|
||||
// if (dataSectors.size () == 0)
|
||||
// message += "No data ";
|
||||
if (reportedSize > 999)
|
||||
message += "Reported " + reportedSize;
|
||||
|
||||
// String catName = catalogName.length () >= 8 ? catalogName.substring (7) : catalogName;
|
||||
String text = String.format ("%1s %1s %03d %-30.30s %-5s %-13s %3d %3d %s",
|
||||
lockedFlag, getFileType (), actualSize, catalogName, addressText, lengthText,
|
||||
tsSectors.size (), (dataSectors.size () - textFileGaps), message.trim ());
|
||||
if (actualSize == 0)
|
||||
text = text.substring (0, 50);
|
||||
String text =
|
||||
String.format ("%1s %1s %03d %-30.30s %-5s %-13s %3d %3d %s", lockedFlag,
|
||||
getFileType (), reportedSize % 1000, displayName, addressText, lengthText,
|
||||
tsSectors.size (), (dataSectors.size () - textFileGaps), message.trim ());
|
||||
// if (actualSize == 0)
|
||||
// text = text.substring (0, 50);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ class DeletedCatalogEntry extends AbstractCatalogEntry
|
|||
tsSectors.add (da);
|
||||
totalBlocks++;
|
||||
|
||||
DiskAddress sectorDA = da;
|
||||
byte[] sectorBuffer = disk.readBlock (da);
|
||||
for (int i = 12, max = disk.getBlockSize (); i < max; i += 2)
|
||||
{
|
||||
|
@ -82,15 +83,17 @@ class DeletedCatalogEntry extends AbstractCatalogEntry
|
|||
}
|
||||
}
|
||||
|
||||
da = getValidAddress (sectorBuffer, 1);
|
||||
DiskAddress nextDa = getValidAddress (sectorBuffer, 1);
|
||||
|
||||
if (da == null)
|
||||
if (nextDa == null)
|
||||
{
|
||||
System.out.printf ("Next T/S list in sector %s is invalid : %02X, %02X%n", da,
|
||||
sectorBuffer[1], sectorBuffer[2]);
|
||||
System.out.printf ("Next T/S list in sector %s is invalid : %02X, %02X%n",
|
||||
sectorDA, sectorBuffer[1], sectorBuffer[2]);
|
||||
break;
|
||||
}
|
||||
da = nextDa;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
System.out.printf ("Total blocks recoverable : %d%n", totalBlocks);
|
||||
if (totalBlocks != reportedSize)
|
||||
|
|
|
@ -46,7 +46,7 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
|
||||
enum FileType
|
||||
{
|
||||
Text, ApplesoftBasic, IntegerBasic, Binary, Relocatable, SS, AA, BB
|
||||
Text, ApplesoftBasic, IntegerBasic, Binary, SS, Relocatable, AA, BB
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -76,6 +76,7 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
|
||||
da = disk.getDiskAddress (CATALOG_TRACK, VTOC_SECTOR);
|
||||
sectorBuffer = disk.readBlock (da); // VTOC
|
||||
((AppleDisk) disk).setDosVersion (sectorBuffer[3] & 0xFF);
|
||||
dosVTOCSector = new DosVTOCSector (this, disk, sectorBuffer, da);
|
||||
sectorTypes[da.getBlockNo ()] = vtocSector;
|
||||
|
||||
|
@ -132,7 +133,7 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
|
||||
da = disk.getDiskAddress (track, sector);
|
||||
|
||||
} while (!da.isZero ());
|
||||
} while (!da.isZero () && da.isValidAddress ());
|
||||
|
||||
// same loop, but now all the catalog sectors are properly flagged
|
||||
da = disk.getDiskAddress (catalogStart.getBlockNo ());
|
||||
|
@ -247,13 +248,6 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
makeNodeVisible (volumeNode.getFirstLeaf ());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
// private int getVolumeNo ()
|
||||
// // ---------------------------------------------------------------------------------//
|
||||
// {
|
||||
// return volumeNo;
|
||||
// }
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void setOriginalPath (Path path)
|
||||
|
@ -332,6 +326,8 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
return "4.2";
|
||||
case 0x43:
|
||||
return "4.3";
|
||||
case 0x45:
|
||||
return "4.5";
|
||||
default:
|
||||
return "??";
|
||||
}
|
||||
|
@ -382,7 +378,7 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
int version = buffer[3] & 0xFF;
|
||||
if (debug)
|
||||
System.out.printf ("Version: %02X%n", buffer[3]);
|
||||
if (version == 0 || (version > 0x43 && version != 0xFF))
|
||||
if (version == 0 || (version > 0x45 && version != 0xFF))
|
||||
{
|
||||
if (debug)
|
||||
System.out.printf ("Bad version : %02X%n", version);
|
||||
|
@ -393,6 +389,8 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
if (debug)
|
||||
System.out.printf ("Catalog blocks: %s%n", catalogBlocks);
|
||||
|
||||
if (catalogBlocks > 0)
|
||||
disk.setDosVersion (version);
|
||||
return catalogBlocks;
|
||||
}
|
||||
|
||||
|
@ -400,6 +398,13 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
private static int countCatalogBlocks (AppleDisk disk, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (!disk.isValidAddress (buffer[1], buffer[2]))
|
||||
{
|
||||
if (debug)
|
||||
System.out.printf ("Invalid address1: %02X %02X%n", buffer[1], buffer[2]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DiskAddress catalogStart = disk.getDiskAddress (buffer[1], buffer[2]);
|
||||
DiskAddress da = disk.getDiskAddress (catalogStart.getBlockNo ());
|
||||
List<DiskAddress> catalogAddresses = new ArrayList<> ();
|
||||
|
@ -427,14 +432,13 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
if (!disk.isValidAddress (buffer[1], buffer[2]))
|
||||
{
|
||||
if (debug)
|
||||
System.out.printf ("Invalid address: %02X %02X%n", buffer[1], buffer[2]);
|
||||
System.out.printf ("Invalid address2: %02X %02X%n", buffer[1], buffer[2]);
|
||||
return catalogAddresses.size ();
|
||||
}
|
||||
|
||||
catalogAddresses.add (da);
|
||||
|
||||
da = disk.getDiskAddress (buffer[1], buffer[2]);
|
||||
|
||||
} while (!da.isZero ());
|
||||
|
||||
if (debug)
|
||||
|
@ -506,26 +510,44 @@ public class DosDisk extends AbstractFormattedDisk
|
|||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private int countEntries (AppleFileSource catalogEntry)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int count = 0;
|
||||
for (AppleFileSource ce : fileEntries)
|
||||
{
|
||||
if (ce.getUniqueName ().equals (catalogEntry.getUniqueName ()))
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public AppleFileSource getCatalog ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
String newLine = String.format ("%n");
|
||||
String line = "- --- --- ------------------------------ ----- -------------"
|
||||
String underline = "- --- --- ------------------------------ ----- -------------"
|
||||
+ " -- ---- -------------------" + newLine;
|
||||
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append (String.format ("Disk : %s%n%n", getDisplayPath ()));
|
||||
text.append (String.format ("File : %s%n%n", getDisplayPath ()));
|
||||
text.append ("L Typ Len Name Addr"
|
||||
+ " Length TS Data Comment" + newLine);
|
||||
text.append (line);
|
||||
text.append (underline);
|
||||
|
||||
for (AppleFileSource fileEntry : fileEntries)
|
||||
text.append (((CatalogEntry) fileEntry).getDetails () + newLine);
|
||||
{
|
||||
String entry = ((CatalogEntry) fileEntry).getDetails ();
|
||||
if (countEntries (fileEntry) > 1)
|
||||
entry += "** duplicate **";
|
||||
text.append (entry + newLine);
|
||||
}
|
||||
|
||||
text.append (line);
|
||||
text.append (underline);
|
||||
text.append (String.format (
|
||||
" Free sectors: %3d " + "Used sectors: %3d Total sectors: %3d",
|
||||
dosVTOCSector.freeSectors, dosVTOCSector.usedSectors,
|
||||
|
|
|
@ -82,7 +82,7 @@ class DosTSListSector extends AbstractSector
|
|||
addText (text, buffer, 7, 4, "Not used");
|
||||
addText (text, buffer, 11, 1, "Not used");
|
||||
|
||||
int sectorBase = Utility.intValue (buffer[5], buffer[6]);
|
||||
int sectorBase = Utility.getShort (buffer, 5);
|
||||
|
||||
for (int i = 12; i <= 255; i += 2)
|
||||
{
|
||||
|
|
|
@ -35,7 +35,7 @@ class DosVTOCSector extends AbstractSector
|
|||
direction = buffer[49];
|
||||
maxTracks = buffer[52] & 0xFF;
|
||||
maxSectors = buffer[53] & 0xFF;
|
||||
sectorSize = Utility.intValue (buffer[54], buffer[55]);
|
||||
sectorSize = Utility.getShort (buffer, 54);
|
||||
|
||||
flagSectors ();
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ public class DisksWindow extends JFrame
|
|||
}
|
||||
});
|
||||
|
||||
scrollPane.setPreferredSize (new Dimension (1200, 700));
|
||||
scrollPane.setPreferredSize (new Dimension (1200, 693));
|
||||
setDefaultCloseOperation (HIDE_ON_CLOSE);
|
||||
|
||||
deleteWindow = new DeleteWindow (rootFolderData);
|
||||
|
@ -188,11 +188,11 @@ public class DisksWindow extends JFrame
|
|||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < Utility.suffixes.size (); i++)
|
||||
for (int i = 0; i < Utility.getTotalSuffixes (); i++)
|
||||
{
|
||||
int total = rootFolderData.getTotalType (i);
|
||||
JCheckBox btn =
|
||||
new JCheckBox (String.format ("%s (%,d)", Utility.suffixes.get (i), total));
|
||||
new JCheckBox (String.format ("%s (%,d)", Utility.getSuffix (i), total));
|
||||
topPanel.add (btn);
|
||||
boxes.add (btn);
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import java.awt.Graphics;
|
|||
import java.awt.Graphics2D;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -20,11 +22,10 @@ import javax.swing.JFrame;
|
|||
import javax.swing.JPanel;
|
||||
|
||||
import com.bytezone.diskbrowser.gui.DuplicateAction.DiskTableSelectionListener;
|
||||
import com.bytezone.diskbrowser.gui.RootDirectoryChangeListener;
|
||||
import com.bytezone.diskbrowser.utilities.Utility;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class RootFolderData implements RootDirectoryChangeListener
|
||||
public class RootFolderData implements PropertyChangeListener
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final String header =
|
||||
|
@ -66,7 +67,7 @@ public class RootFolderData implements RootDirectoryChangeListener
|
|||
btnOK = new JButton ("OK");
|
||||
|
||||
progressPanel = new ProgressPanel ();
|
||||
progressPanel.setPreferredSize (new Dimension (560, 340));
|
||||
progressPanel.setPreferredSize (new Dimension (560, 380));
|
||||
|
||||
dialogTotals = new JFrame ("Disk Totals");
|
||||
dialogTotals.add (progressPanel, BorderLayout.CENTER);
|
||||
|
@ -154,7 +155,7 @@ public class RootFolderData implements RootDirectoryChangeListener
|
|||
private void clear ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
typeTotals = new int[4][Utility.suffixes.size ()];
|
||||
typeTotals = new int[4][Utility.getTotalSuffixes ()];
|
||||
totalDisks = 0;
|
||||
totalFolders = 0;
|
||||
|
||||
|
@ -177,38 +178,35 @@ public class RootFolderData implements RootDirectoryChangeListener
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void incrementType (File file, String filename)
|
||||
public void incrementType (File file, String fileName)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
int pos = Utility.getSuffixNo (filename);
|
||||
int pos = Utility.getSuffixNo (fileName);
|
||||
if (pos >= 0)
|
||||
{
|
||||
int cmp = 0;
|
||||
if (filename.endsWith (".gz"))
|
||||
cmp = 1;
|
||||
else if (filename.endsWith (".zip"))
|
||||
cmp = 2;
|
||||
int cmp = fileName.endsWith (".zip") ? 2 : fileName.endsWith (".gz") ? 1 : 0;
|
||||
|
||||
typeTotals[cmp][pos]++;
|
||||
typeTotals[3][pos]++;
|
||||
++totalDisks;
|
||||
}
|
||||
else
|
||||
System.out.println ("no suffix: " + filename);
|
||||
System.out.println ("no suffix: " + fileName);
|
||||
|
||||
checkDuplicates (file, filename);
|
||||
checkDuplicates (file, fileName);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void checkDuplicates (File file, String filename)
|
||||
private void checkDuplicates (File file, String fileName)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
String rootName = file.getAbsolutePath ().substring (rootFolderNameLength);
|
||||
DiskDetails diskDetails = new DiskDetails (file, rootName, filename, doChecksums);
|
||||
DiskDetails diskDetails = new DiskDetails (file, rootName, fileName, doChecksums);
|
||||
|
||||
if (fileNameMap.containsKey (filename))
|
||||
fileNameMap.get (filename).addDuplicateName (diskDetails);
|
||||
if (fileNameMap.containsKey (fileName))
|
||||
fileNameMap.get (fileName).addDuplicateName (diskDetails);
|
||||
else
|
||||
fileNameMap.put (filename, diskDetails);
|
||||
fileNameMap.put (fileName, diskDetails);
|
||||
|
||||
if (doChecksums)
|
||||
{
|
||||
|
@ -256,7 +254,7 @@ public class RootFolderData implements RootDirectoryChangeListener
|
|||
System.out.println (line);
|
||||
for (int i = 0; i < typeTotals[0].length; i++)
|
||||
{
|
||||
System.out.printf ("%14.14s ", Utility.suffixes.get (i) + " ...........");
|
||||
System.out.printf ("%14.14s ", Utility.getSuffix (i) + " ...........");
|
||||
for (int j = 0; j < typeTotals.length; j++)
|
||||
{
|
||||
System.out.printf ("%,7d ", typeTotals[j][i]);
|
||||
|
@ -277,8 +275,7 @@ public class RootFolderData implements RootDirectoryChangeListener
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void rootDirectoryChanged (File oldRootFolder, File newRootFolder)
|
||||
private void rootDirectoryChanged (File oldRootFolder, File newRootFolder)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
rootFolder = newRootFolder;
|
||||
|
@ -286,6 +283,15 @@ public class RootFolderData implements RootDirectoryChangeListener
|
|||
disksWindow = null; // force a recount
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void propertyChange (PropertyChangeEvent evt)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
if (evt.getPropertyName ().equals ("RootDirectory"))
|
||||
rootDirectoryChanged ((File) evt.getOldValue (), (File) evt.getNewValue ());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public String toString ()
|
||||
|
@ -326,16 +332,16 @@ public class RootFolderData implements RootDirectoryChangeListener
|
|||
int grandTotal[] = new int[4];
|
||||
|
||||
for (int i = 0; i < typeTotals[0].length; i++)
|
||||
{
|
||||
line = String.format ("%14.14s %,7d %,7d %,7d %,7d",
|
||||
Utility.suffixes.get (i) + " ...........", typeTotals[0][i], typeTotals[1][i],
|
||||
typeTotals[2][i], typeTotals[3][i]);
|
||||
g.drawString (line, x, y);
|
||||
for (int j = 0; j < typeTotals.length; j++)
|
||||
grandTotal[j] += typeTotals[j][i];
|
||||
|
||||
y += lineHeight;
|
||||
}
|
||||
if (typeTotals[3][i] > 0)
|
||||
{
|
||||
line = String.format ("%14.14s %,7d %,7d %,7d %,7d",
|
||||
Utility.getSuffix (i) + " ...........", typeTotals[0][i], typeTotals[1][i],
|
||||
typeTotals[2][i], typeTotals[3][i]);
|
||||
g.drawString (line, x, y);
|
||||
for (int j = 0; j < typeTotals.length; j++)
|
||||
grandTotal[j] += typeTotals[j][i];
|
||||
y += lineHeight;
|
||||
}
|
||||
|
||||
line = String.format ("Total %,7d %,7d %,7d %,7d%n%n", grandTotal[0],
|
||||
grandTotal[1], grandTotal[2], grandTotal[3]);
|
||||
|
|
58
src/com/bytezone/diskbrowser/gui/AbstractSaveAction.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
package com.bytezone.diskbrowser.gui;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import com.bytezone.diskbrowser.utilities.DefaultAction;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public abstract class AbstractSaveAction extends DefaultAction
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
protected JFileChooser fileChooser = new JFileChooser ();
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AbstractSaveAction (String menuText, String tip, String dialogTitle)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (menuText, tip);
|
||||
|
||||
fileChooser.setDialogTitle (dialogTitle);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void setSelectedFile (File file)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
fileChooser.setSelectedFile (file);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
void saveBuffer (byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
File file = fileChooser.getSelectedFile ();
|
||||
try
|
||||
{
|
||||
Files.write (file.toPath (), buffer, StandardOpenOption.CREATE_NEW);
|
||||
JOptionPane.showMessageDialog (null, String.format ("File %s saved", file.getName ()));
|
||||
}
|
||||
catch (FileAlreadyExistsException e)
|
||||
{
|
||||
JOptionPane.showMessageDialog (null, "File " + file.getName () + " already exists", "Failed",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace ();
|
||||
JOptionPane.showMessageDialog (null, "File failed to save - " + e.getMessage (), "Failed",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
62
src/com/bytezone/diskbrowser/gui/AnimationWorker.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
package com.bytezone.diskbrowser.gui;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
|
||||
import com.bytezone.diskbrowser.applefile.SHRPictureFile2;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
public class AnimationWorker extends SwingWorker<Void, Integer>
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
volatile boolean running;
|
||||
SHRPictureFile2 image;
|
||||
OutputPanel owner;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public AnimationWorker (OutputPanel owner, SHRPictureFile2 image)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
assert image.isAnimation ();
|
||||
this.image = image;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void cancel ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
protected Void doInBackground () throws Exception
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
running = true;
|
||||
try
|
||||
{
|
||||
while (running)
|
||||
{
|
||||
Thread.sleep (image.getDelay ());
|
||||
publish (0);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace ();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
protected void process (List<Integer> chunks)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
image.nextFrame ();
|
||||
owner.update ();
|
||||
}
|
||||
}
|
|
@ -17,8 +17,8 @@ import javax.swing.tree.TreeNode;
|
|||
|
||||
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
||||
import com.bytezone.diskbrowser.disk.DiskFactory;
|
||||
import com.bytezone.diskbrowser.disk.DualDosDisk;
|
||||
import com.bytezone.diskbrowser.disk.FormattedDisk;
|
||||
import com.bytezone.diskbrowser.disk.HybridDisk;
|
||||
import com.bytezone.diskbrowser.gui.RedoHandler.RedoEvent;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
|
@ -59,7 +59,7 @@ class AppleDiskTab extends AbstractTab
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (redoHandler, selector, font);
|
||||
// System.out.println ("File not found: " + lastFileUsed);
|
||||
|
||||
create (disk);
|
||||
|
||||
DefaultMutableTreeNode node = findNode (lastFileUsed);
|
||||
|
@ -101,7 +101,6 @@ class AppleDiskTab extends AbstractTab
|
|||
public void activate ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
// System.out.println ("=========== Activating AppleDiskTab =============");
|
||||
eventHandler.redo = true;
|
||||
eventHandler.fireDiskSelectionEvent (disk);
|
||||
eventHandler.redo = false;
|
||||
|
@ -145,7 +144,7 @@ class AppleDiskTab extends AbstractTab
|
|||
FileSelectedEvent fileSelectedEvent = (FileSelectedEvent) event.value;
|
||||
if (fileSelectedEvent.volumeNo >= 0)
|
||||
{
|
||||
DualDosDisk ddd = (DualDosDisk) afs.getFormattedDisk ().getParent ();
|
||||
HybridDisk ddd = (HybridDisk) afs.getFormattedDisk ().getParent ();
|
||||
ddd.setCurrentDiskNo (fileSelectedEvent.volumeNo);
|
||||
}
|
||||
selectNode (fileSelectedEvent.appleFileSource.getUniqueName ());
|
||||
|
@ -159,9 +158,9 @@ class AppleDiskTab extends AbstractTab
|
|||
|
||||
// check for multi-volume disk (only search the current branch)
|
||||
FormattedDisk fd = ((AppleFileSource) rootNode.getUserObject ()).getFormattedDisk ();
|
||||
if (fd instanceof DualDosDisk)
|
||||
if (fd instanceof HybridDisk hd)
|
||||
{
|
||||
int volume = ((DualDosDisk) fd).getCurrentDiskNo ();
|
||||
int volume = hd.getCurrentDiskNo ();
|
||||
rootNode = (DefaultMutableTreeNode) rootNode.getChildAt (volume);
|
||||
}
|
||||
|
||||
|
@ -170,8 +169,8 @@ class AppleDiskTab extends AbstractTab
|
|||
{
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) children.nextElement ();
|
||||
Object userObject = node.getUserObject ();
|
||||
if (userObject instanceof AppleFileSource
|
||||
&& nodeName.equals (((AppleFileSource) userObject).getUniqueName ()))
|
||||
if (userObject instanceof AppleFileSource afs
|
||||
&& nodeName.equals (afs.getUniqueName ()))
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -4,14 +4,31 @@ package com.bytezone.diskbrowser.gui;
|
|||
public class BasicPreferences
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
public boolean splitRem = false;
|
||||
public boolean alignAssign = true;
|
||||
public boolean showTargets = true;
|
||||
public boolean showHeader = true;
|
||||
public boolean onlyShowTargetLineNumbers = true;
|
||||
public boolean userFormat = true;
|
||||
public boolean showAllXref = true;
|
||||
|
||||
public boolean appleLineWrap = false;
|
||||
|
||||
public boolean splitRem = false;
|
||||
public boolean splitDim = false;
|
||||
public boolean alignAssign = true;
|
||||
public boolean showCaret = false;
|
||||
public int wrapPrintAt = 40;
|
||||
public int wrapRemAt = 60;
|
||||
public boolean showThen = true;
|
||||
public boolean blankAfterReturn = false;
|
||||
public boolean formatRem = false;
|
||||
public boolean deleteExtraDataSpace = false;
|
||||
|
||||
public boolean showGosubGoto = false;
|
||||
public boolean showCalls = false;
|
||||
public boolean showSymbols = false;
|
||||
public boolean showFunctions = false;
|
||||
public boolean showConstants = false;
|
||||
public boolean showDuplicateSymbols = false;
|
||||
|
||||
public int wrapPrintAt = 80;
|
||||
public int wrapRemAt = 80;
|
||||
public int wrapDataAt = 80;
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
|
@ -20,13 +37,27 @@ public class BasicPreferences
|
|||
{
|
||||
StringBuilder text = new StringBuilder ();
|
||||
|
||||
text.append (String.format ("Split remark .......... %s%n", splitRem));
|
||||
text.append (String.format ("Align assign .......... %s%n", alignAssign));
|
||||
text.append (String.format ("Show targets .......... %s%n", showTargets));
|
||||
text.append (
|
||||
String.format ("Only target lines ..... %s%n", onlyShowTargetLineNumbers));
|
||||
text.append (String.format ("Show header ........... %s%n", showHeader));
|
||||
text.append (String.format ("Show caret ............ %s", showCaret));
|
||||
text.append (String.format ("Split REM ................ %s%n", splitRem));
|
||||
text.append (String.format ("Align assign ............. %s%n", alignAssign));
|
||||
text.append (String.format ("Show header .............. %s%n", showHeader));
|
||||
text.append (String.format ("User format .............. %s%n", userFormat));
|
||||
text.append (String.format ("Show All Xref ............ %s%n", showAllXref));
|
||||
text.append (String.format ("Apple line wrap .......... %s%n", appleLineWrap));
|
||||
text.append (String.format ("Show caret ............... %s%n", showCaret));
|
||||
text.append (String.format ("Show THEN ................ %s%n", showThen));
|
||||
text.append (String.format ("Show GOTO/GOSUB .......... %s%n", showGosubGoto));
|
||||
text.append (String.format ("Show CALL ................ %s%n", showCalls));
|
||||
text.append (String.format ("Show symbols ............. %s%n", showSymbols));
|
||||
text.append (String.format ("Show constants ........... %s%n", showConstants));
|
||||
text.append (String.format ("Show functions ........... %s%n", showFunctions));
|
||||
text.append (String.format ("Show duplicate symbols ... %s%n", showDuplicateSymbols));
|
||||
text.append (String.format ("Blank after RETURN ....... %s%n", blankAfterReturn));
|
||||
text.append (String.format ("Format REM ............... %s%n", formatRem));
|
||||
text.append (String.format ("Delete extra DATA space .. %s%n", deleteExtraDataSpace));
|
||||
text.append (String.format ("Wrap PRINT at ............ %d%n", wrapPrintAt));
|
||||
text.append (String.format ("Wrap REM at .............. %d%n", wrapRemAt));
|
||||
text.append (String.format ("Wrap DATA at ............. %d%n", wrapDataAt));
|
||||
text.append (String.format ("Split DIM ................ %d", splitDim));
|
||||
|
||||
return text.toString ();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import java.awt.Dimension;
|
|||
import java.awt.Font;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EventObject;
|
||||
|
@ -25,8 +27,8 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
|||
import javax.swing.tree.TreePath;
|
||||
|
||||
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
||||
import com.bytezone.diskbrowser.disk.DualDosDisk;
|
||||
import com.bytezone.diskbrowser.disk.FormattedDisk;
|
||||
import com.bytezone.diskbrowser.disk.HybridDisk;
|
||||
import com.bytezone.diskbrowser.duplicates.DiskDetails;
|
||||
import com.bytezone.diskbrowser.gui.DuplicateAction.DiskTableSelectionListener;
|
||||
import com.bytezone.diskbrowser.gui.FontAction.FontChangeEvent;
|
||||
|
@ -36,9 +38,8 @@ import com.bytezone.diskbrowser.gui.RedoHandler.RedoListener;
|
|||
import com.bytezone.diskbrowser.gui.TreeBuilder.FileNode;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
class CatalogPanel extends JTabbedPane
|
||||
implements RedoListener, SectorSelectionListener, QuitListener, FontChangeListener,
|
||||
RootDirectoryChangeListener, DiskTableSelectionListener
|
||||
class CatalogPanel extends JTabbedPane implements RedoListener, SectorSelectionListener,
|
||||
QuitListener, FontChangeListener, DiskTableSelectionListener, PropertyChangeListener
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private static final String prefsLastDiskUsed = "Last disk used";
|
||||
|
@ -62,12 +63,11 @@ class CatalogPanel extends JTabbedPane
|
|||
this.redoHandler = redoHandler;
|
||||
|
||||
setTabPlacement (SwingConstants.BOTTOM);
|
||||
setPreferredSize (new Dimension (360, 802)); // width, height
|
||||
setPreferredSize (new Dimension (360, 802)); // width, height
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void rootDirectoryChanged (File oldRootFolder, File newRootFolder)
|
||||
private void rootDirectoryChanged (File oldRootFolder, File newRootFolder)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
rootFolder = newRootFolder;
|
||||
|
@ -82,6 +82,18 @@ class CatalogPanel extends JTabbedPane
|
|||
setSelectedIndex (0);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public void propertyChange (PropertyChangeEvent evt)
|
||||
// -------------------------------------------------------------------------------//
|
||||
{
|
||||
if (evt.getPropertyName ().equals ("RootDirectory"))
|
||||
rootDirectoryChanged ((File) evt.getOldValue (), (File) evt.getNewValue ());
|
||||
// else
|
||||
// closeCurrentTab ();
|
||||
// System.out.println (evt.getPropertyName ());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private void insertFileSystemTab (DiskSelectedEvent diskEvent)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -146,11 +158,10 @@ class CatalogPanel extends JTabbedPane
|
|||
tab.refresh ();
|
||||
|
||||
// Any newly created disk needs to appear in the FileSystemTab's tree
|
||||
if (tab instanceof AppleDiskTab)
|
||||
fileTab.replaceDisk (((AppleDiskTab) tab).disk);
|
||||
if (tab instanceof AppleDiskTab appleDiskTab)
|
||||
fileTab.replaceDisk (appleDiskTab.disk);
|
||||
}
|
||||
|
||||
// called from CloseTabAction
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void closeCurrentTab ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -174,10 +185,7 @@ class CatalogPanel extends JTabbedPane
|
|||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
Tab tab = (Tab) getSelectedComponent ();
|
||||
if (diskTabs.size () > 1 && tab instanceof AppleDiskTab)
|
||||
closeTabAction.setEnabled (true);
|
||||
else
|
||||
closeTabAction.setEnabled (false);
|
||||
closeTabAction.setEnabled (diskTabs.size () > 1 && tab instanceof AppleDiskTab);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -213,8 +221,8 @@ class CatalogPanel extends JTabbedPane
|
|||
|
||||
FormattedDisk fd = ((AppleDiskTab) selectedTab).disk;
|
||||
prefs.put (prefsLastDiskUsed, fd.getAbsolutePath ());
|
||||
if (fd instanceof DualDosDisk)
|
||||
prefs.putInt (prefsLastDosUsed, ((DualDosDisk) fd).getCurrentDiskNo ());
|
||||
if (fd instanceof HybridDisk hybridDisk)
|
||||
prefs.putInt (prefsLastDosUsed, hybridDisk.getCurrentDiskNo ());
|
||||
else
|
||||
prefs.putInt (prefsLastDosUsed, -1);
|
||||
|
||||
|
@ -223,16 +231,16 @@ class CatalogPanel extends JTabbedPane
|
|||
{
|
||||
EventObject event = redoEvent.value;
|
||||
|
||||
if (event instanceof FileSelectedEvent)
|
||||
if (event instanceof FileSelectedEvent fileSelectedEvent)
|
||||
{
|
||||
AppleFileSource afs = ((FileSelectedEvent) event).appleFileSource;
|
||||
AppleFileSource afs = fileSelectedEvent.appleFileSource;
|
||||
prefs.put (prefsLastFileUsed, afs == null ? "" : afs.getUniqueName ());
|
||||
prefs.put (prefsLastSectorsUsed, "");
|
||||
}
|
||||
else if (event instanceof SectorSelectedEvent)
|
||||
else if (event instanceof SectorSelectedEvent sectorSelectedEvent)
|
||||
{
|
||||
prefs.put (prefsLastFileUsed, "");
|
||||
prefs.put (prefsLastSectorsUsed, ((SectorSelectedEvent) event).toText ());
|
||||
prefs.put (prefsLastSectorsUsed, sectorSelectedEvent.toText ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,8 +273,8 @@ class CatalogPanel extends JTabbedPane
|
|||
if (diskEvent != null)
|
||||
{
|
||||
fd1 = diskEvent.getFormattedDisk ();
|
||||
if (lastDosUsed >= 0 && fd1 instanceof DualDosDisk)
|
||||
((DualDosDisk) fd1).setCurrentDiskNo (lastDosUsed);
|
||||
if (lastDosUsed >= 0 && fd1 instanceof HybridDisk hybridDisk)
|
||||
hybridDisk.setCurrentDiskNo (lastDosUsed);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -321,6 +329,14 @@ class CatalogPanel extends JTabbedPane
|
|||
selector.addDiskSelectionListener (listener);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void addDiskSelectionListener (DiskSelectionListener... listeners)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
for (DiskSelectionListener diskSelectionListener : listeners)
|
||||
selector.addDiskSelectionListener (diskSelectionListener);
|
||||
}
|
||||
|
||||
// Pass through to DiskSelector
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void addFileSelectionListener (FileSelectionListener listener)
|
||||
|
@ -329,6 +345,14 @@ class CatalogPanel extends JTabbedPane
|
|||
selector.addFileSelectionListener (listener);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void addFileSelectionListener (FileSelectionListener... listeners)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
for (FileSelectionListener fileSelectionListener : listeners)
|
||||
selector.addFileSelectionListener (fileSelectionListener);
|
||||
}
|
||||
|
||||
// Pass through to DiskSelector
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void addFileNodeSelectionListener (FileNodeSelectionListener listener)
|
||||
|
@ -337,6 +361,14 @@ class CatalogPanel extends JTabbedPane
|
|||
selector.addFileNodeSelectionListener (listener);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public void addFileNodeSelectionListener (FileNodeSelectionListener... listeners)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
for (FileNodeSelectionListener fileNodeSelectionListener : listeners)
|
||||
selector.addFileNodeSelectionListener (fileNodeSelectionListener);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
private class TabChangeListener implements ChangeListener
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -367,13 +399,13 @@ class CatalogPanel extends JTabbedPane
|
|||
{
|
||||
case "DiskEvent":
|
||||
case "FileNodeEvent":
|
||||
if (tab instanceof FileSystemTab)
|
||||
((FileSystemTab) tab).redoEvent (event);
|
||||
if (tab instanceof FileSystemTab fileSystemTab)
|
||||
fileSystemTab.redoEvent (event);
|
||||
break;
|
||||
|
||||
case "FileEvent":
|
||||
if (tab instanceof AppleDiskTab)
|
||||
((AppleDiskTab) tab).redoEvent (event);
|
||||
if (tab instanceof AppleDiskTab appleDiskTab)
|
||||
appleDiskTab.redoEvent (event);
|
||||
break;
|
||||
|
||||
case "SectorEvent":
|
||||
|
@ -394,8 +426,8 @@ class CatalogPanel extends JTabbedPane
|
|||
{
|
||||
// user has clicked in the DiskLayoutPanel, so turn off any current file selection
|
||||
Tab tab = (Tab) getSelectedComponent ();
|
||||
if (tab instanceof AppleDiskTab)
|
||||
((AppleDiskTab) tab).tree.setSelectionPath (null);
|
||||
if (tab instanceof AppleDiskTab appleDiskTab)
|
||||
appleDiskTab.tree.setSelectionPath (null);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
|
@ -430,7 +462,7 @@ class CatalogPanel extends JTabbedPane
|
|||
FileNode node = (FileNode) selectedNode.getUserObject ();
|
||||
if (node.file.isDirectory ())
|
||||
{
|
||||
// lister.catalogLister.setNode (selectedNode);
|
||||
// lister.catalogLister.setNode (selectedNode);
|
||||
}
|
||||
else if (e.getClickCount () == 2)
|
||||
addDiskPanel (node.getFormattedDisk (), true);
|
||||
|
|