dmolony-DiskBrowser/src/com/bytezone/diskbrowser/applefile/SHRPictureFile2.java

405 lines
13 KiB
Java
Raw Permalink Normal View History

2017-01-25 11:02:50 +00:00
package com.bytezone.diskbrowser.applefile;
2017-01-26 04:19:23 +00:00
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
2020-05-16 07:45:58 +00:00
import java.util.ArrayList;
import java.util.List;
2017-01-26 04:19:23 +00:00
2021-08-30 06:56:43 +00:00
import com.bytezone.diskbrowser.nufx.LZW3;
2017-01-26 04:19:23 +00:00
import com.bytezone.diskbrowser.prodos.ProdosConstants;
2017-02-01 21:15:30 +00:00
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
2017-01-26 04:19:23 +00:00
2019-11-19 05:25:10 +00:00
// -----------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
public class SHRPictureFile2 extends HiResImage
2019-11-19 05:25:10 +00:00
// -----------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
{
2017-01-26 04:19:23 +00:00
ColorTable[] colorTables;
2018-07-30 08:44:29 +00:00
byte[] controlBytes;
2019-11-19 11:41:48 +00:00
int rows = 200; // may change
2017-01-25 11:02:50 +00:00
2020-05-16 07:45:58 +00:00
List<Integer> framePointers = new ArrayList<> ();
int frameNumber;
int delay;
2018-07-25 05:48:14 +00:00
// see Graphics & Animation.2mg
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
public SHRPictureFile2 (String name, byte[] buffer, int fileType, int auxType, int eof)
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
{
super (name, buffer, fileType, auxType, eof);
2018-07-25 05:48:14 +00:00
switch (fileType)
2017-01-26 04:19:23 +00:00
{
2018-07-31 07:02:19 +00:00
case ProdosConstants.FILE_TYPE_PNT: // packed images
2019-11-09 13:23:30 +00:00
doPnt ();
2018-07-31 07:02:19 +00:00
break;
case ProdosConstants.FILE_TYPE_PIC: // unpacked images
doPic ();
break;
2020-05-15 10:08:48 +00:00
case ProdosConstants.FILE_TYPE_ANI:
2020-05-16 07:45:58 +00:00
doPic ();
doAnimation ();
2020-05-15 10:08:48 +00:00
break;
2018-07-31 07:02:19 +00:00
default:
System.out.println ("unknown filetype " + fileType);
}
if (colorTables != null)
createImage ();
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2019-11-09 13:23:30 +00:00
private void doPnt ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2018-07-31 07:02:19 +00:00
{
switch (auxType)
{
2018-08-01 07:23:12 +00:00
case 0: // packed Paintworks SHR
2019-08-21 00:31:11 +00:00
case 0x8000: // Paintworks Gold
2018-08-01 07:23:12 +00:00
colorTables = new ColorTable[1];
colorTables[0] = new ColorTable (0, this.buffer, 0);
2018-07-31 07:02:19 +00:00
2020-04-03 07:38:14 +00:00
byte[] newBuffer = new byte[calculateBufferSize (buffer, 0x222)];
unpack (buffer, 0x222, buffer.length, newBuffer, 0);
buffer = newBuffer;
2020-05-16 07:45:58 +00:00
2019-11-19 11:41:48 +00:00
rows = buffer.length / 160;
controlBytes = new byte[rows]; // all pointing to 0th color table
2018-08-01 07:23:12 +00:00
2018-07-31 07:02:19 +00:00
break;
case 1: // packed version of PIC/$00
2020-04-03 07:38:14 +00:00
newBuffer = new byte[calculateBufferSize (buffer, 0)];
unpack (buffer, 0, buffer.length, newBuffer, 0);
buffer = newBuffer;
2020-05-16 07:45:58 +00:00
2019-11-19 11:41:48 +00:00
controlBytes = new byte[rows];
2018-07-31 07:02:19 +00:00
System.arraycopy (this.buffer, 32000, controlBytes, 0, controlBytes.length);
colorTables = new ColorTable[16];
for (int i = 0; i < colorTables.length; i++)
2019-11-22 08:33:59 +00:00
colorTables[i] = new ColorTable (i, this.buffer, 32256 + i * COLOR_TABLE_SIZE);
2019-11-19 05:25:10 +00:00
2018-07-31 07:02:19 +00:00
break;
case 2: // handled in SHRPictureFile1
break;
case 3: // packed version of PIC/$01
2018-08-01 07:23:12 +00:00
System.out.printf ("%s: PNT aux 3 (QuickDraw PICT) not written yet%n", name);
failureReason = "not written yet";
2018-07-31 07:02:19 +00:00
// Apple IIGS Tech Note #46
// https://www.prepressure.com/library/file-formats/pict
2020-04-03 07:38:14 +00:00
newBuffer = new byte[calculateBufferSize (buffer, 0)];
unpack (buffer, 0, buffer.length, newBuffer, 0);
buffer = newBuffer;
2020-05-16 07:45:58 +00:00
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
2018-08-01 07:23:12 +00:00
2018-07-31 07:02:19 +00:00
break;
case 4: // packed version of PIC/$02
System.out.printf ("%s: PNT aux 4 (Packed SHR Brooks Image) not tested yet%n",
name);
2018-08-02 22:13:49 +00:00
// haven't seen one to test yet, for now drop through to .3201
2018-08-01 07:23:12 +00:00
case 99: // testing .3201 binary files
// 00000 - 00003 'APP' 0x00
// 00004 - 06403 200 color tables of 32 bytes each (one color table per scan line)
// 06404 - eof packed pixel data --> 32,000 bytes
2018-07-31 07:02:19 +00:00
colorTables = new ColorTable[200];
for (int i = 0; i < colorTables.length; i++)
2018-07-25 05:48:14 +00:00
{
2019-11-22 08:33:59 +00:00
colorTables[i] = new ColorTable (i, this.buffer, 4 + i * COLOR_TABLE_SIZE);
2018-07-31 07:02:19 +00:00
colorTables[i].reverse ();
2018-07-25 05:48:14 +00:00
}
2018-08-01 07:23:12 +00:00
2020-04-03 07:38:14 +00:00
newBuffer = new byte[calculateBufferSize (buffer, 6404)];
unpack (buffer, 6404, buffer.length, newBuffer, 0);
buffer = newBuffer;
2019-11-19 05:25:10 +00:00
2018-07-25 05:48:14 +00:00
break;
2017-01-27 07:11:00 +00:00
2019-11-21 08:13:41 +00:00
case 0x1000: // seems to be a PIC/$00
2019-11-24 13:26:45 +00:00
if (buffer.length < 32768)
{
failureReason = "file size not 32,768";
break;
}
2019-11-19 11:41:48 +00:00
controlBytes = new byte[rows];
2018-08-02 22:13:49 +00:00
System.arraycopy (buffer, 32000, controlBytes, 0, controlBytes.length);
colorTables = new ColorTable[16];
for (int i = 0; i < colorTables.length; i++)
2019-11-22 08:33:59 +00:00
colorTables[i] = new ColorTable (i, buffer, 32256 + i * COLOR_TABLE_SIZE);
2019-11-19 05:25:10 +00:00
2018-08-02 22:13:49 +00:00
break;
2021-08-30 06:56:43 +00:00
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;
2018-07-31 07:02:19 +00:00
default:
System.out.printf ("%s: PNT unknown aux: %04X%n", name, auxType);
2018-08-01 07:23:12 +00:00
failureReason = "unknown PNT aux";
2018-07-31 07:02:19 +00:00
}
}
2020-05-16 07:45:58 +00:00
// ---------------------------------------------------------------------------------//
private void doAnimation ()
// ---------------------------------------------------------------------------------//
{
// int len = HexFormatter.getLong (buffer, 0x8000);
delay = Utility.getLong (buffer, 0x8004);
2020-05-16 07:45:58 +00:00
if (delay > 60)
delay = 10;
delay = delay * 1000 / 60;
// int offset = HexFormatter.getLong (buffer, 0x8008);
2020-05-16 07:45:58 +00:00
// int blockLen = eof - 0x8008;
// System.out.printf ("Delay: %,d%n", delay);
// System.out.printf ("Blocklen: %,d%n", blockLen);
// System.out.printf ("Offset: %,d%n", offset);
// System.out.printf ("Len: %,d%n", len);
// System.out.println ();
int ptr = 0x800C;
int start = ptr;
while (ptr < buffer.length)
{
int off = Utility.getShort (buffer, ptr);
2020-05-16 07:45:58 +00:00
ptr += 4;
if (off == 0)
{
framePointers.add (start);
start = ptr;
}
}
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2018-07-31 07:02:19 +00:00
private void doPic ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2018-07-31 07:02:19 +00:00
{
switch (auxType)
{
case 0: // unpacked version of PNT/$01
2019-07-28 06:24:52 +00:00
case 0x2000: // see TotalReplay.2mg
2019-11-28 04:36:49 +00:00
case 0x0042:
case 0x0043:
2019-11-21 08:13:41 +00:00
case 0x4100: // no idea what this is
case 0x4950:
2018-07-31 07:02:19 +00:00
// 00000 - 31999 pixel data 32,000 bytes
// 32000 - 32199 200 control bytes (one per scan line)
// 32200 - 32255 empty
// 32256 - 32767 16 color tables of 32 bytes each
2019-11-19 11:41:48 +00:00
controlBytes = new byte[rows];
2018-07-31 07:02:19 +00:00
System.arraycopy (buffer, 32000, controlBytes, 0, controlBytes.length);
colorTables = new ColorTable[16];
for (int i = 0; i < colorTables.length; i++)
2020-03-30 04:57:07 +00:00
colorTables[i] =
new ColorTable (i, buffer, COLOR_TABLE_OFFSET_AUX_0 + i * COLOR_TABLE_SIZE);
2019-11-21 08:13:41 +00:00
2018-07-31 07:02:19 +00:00
break;
case 1: // unpacked version of PNT/$03
2018-08-01 07:23:12 +00:00
System.out.printf ("%s: PIC aux 1 (QuickDraw PICT) not written yet%n", name);
failureReason = "not written yet";
2018-07-31 07:02:19 +00:00
break;
case 2: // unpacked version of PNT/$04, .3200
// 00000 - 31999 pixel data 32,000 bytes
// 32000 - 38399 200 color tables of 32 bytes each (one color table per scan line)
2019-11-22 05:42:07 +00:00
if (buffer.length < 38400 && false)
2018-07-25 05:48:14 +00:00
{
2018-07-31 07:02:19 +00:00
failureReason = "Buffer should be 38,400 bytes";
return;
2018-07-25 05:48:14 +00:00
}
2020-03-30 04:57:07 +00:00
int maxTables = (buffer.length - COLOR_TABLE_OFFSET_AUX_2) / COLOR_TABLE_SIZE;
2019-11-22 05:42:07 +00:00
colorTables = new ColorTable[maxTables];
2018-07-31 07:02:19 +00:00
for (int i = 0; i < colorTables.length; i++)
2017-01-26 04:19:23 +00:00
{
2020-03-29 07:21:30 +00:00
colorTables[i] =
2020-03-30 04:57:07 +00:00
new ColorTable (i, buffer, COLOR_TABLE_OFFSET_AUX_2 + i * COLOR_TABLE_SIZE);
2018-07-31 07:02:19 +00:00
colorTables[i].reverse ();
2017-01-26 04:19:23 +00:00
}
2018-07-25 05:48:14 +00:00
break;
2018-07-31 07:02:19 +00:00
2018-07-25 05:48:14 +00:00
default:
2019-09-13 05:32:35 +00:00
System.out.printf ("PIC unknown aux: %04X%n ", auxType);
2018-08-01 07:23:12 +00:00
failureReason = "unknown PIC aux";
2017-01-26 04:19:23 +00:00
}
2017-01-25 11:02:50 +00:00
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
@Override
2018-07-30 08:44:29 +00:00
void createMonochromeImage ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
{
2019-11-28 04:36:49 +00:00
System.out.println ("monochrome not written");
2017-01-25 11:02:50 +00:00
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
@Override
2018-07-30 08:44:29 +00:00
void createColourImage ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
{
2019-11-20 10:25:16 +00:00
int imageWidth = 640;
image = new BufferedImage (imageWidth, rows * 2, BufferedImage.TYPE_INT_RGB);
2017-01-26 04:19:23 +00:00
DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
2019-11-20 10:25:16 +00:00
int element = 0;
2017-01-26 04:19:23 +00:00
int ptr = 0;
2018-08-02 05:13:09 +00:00
boolean mode320 = true;
boolean fillMode = false;
2018-08-01 07:23:12 +00:00
ColorTable colorTable = null;
2019-11-20 10:25:16 +00:00
int max = 160;
2018-08-01 07:23:12 +00:00
2019-11-19 11:41:48 +00:00
for (int line = 0; line < rows; line++)
2017-01-26 04:19:23 +00:00
{
2018-08-01 07:23:12 +00:00
if (controlBytes != null)
{
2018-08-02 05:13:09 +00:00
int controlByte = controlBytes[line] & 0xFF;
colorTable = colorTables[controlByte & 0x0F];
mode320 = (controlByte & 0x80) == 0;
fillMode = (controlByte & 0x20) != 0;
2018-08-01 07:23:12 +00:00
}
2020-03-31 11:00:40 +00:00
else if (line < colorTables.length)
2018-08-02 05:13:09 +00:00
colorTable = colorTables[line];
2020-03-31 11:00:40 +00:00
else
colorTable = null;
2017-01-26 04:19:23 +00:00
2019-11-19 19:52:03 +00:00
if (mode320) // two pixels per col
2019-11-20 10:47:12 +00:00
ptr = mode320Line (ptr, element, max, colorTable, dataBuffer, imageWidth);
2019-11-19 19:52:03 +00:00
else // four pixels per col
2019-11-20 10:47:12 +00:00
ptr = mode640Line (ptr, element, max, colorTable, dataBuffer, imageWidth);
2019-11-28 04:36:49 +00:00
2019-11-20 10:47:12 +00:00
element += imageWidth * 2; // skip line already drawn
2017-01-26 04:19:23 +00:00
}
2017-01-25 11:02:50 +00:00
}
2020-05-16 07:45:58 +00:00
// ---------------------------------------------------------------------------------//
public void nextFrame ()
// ---------------------------------------------------------------------------------//
{
int ptr = framePointers.get (frameNumber++);
frameNumber %= framePointers.size ();
while (true)
{
int offset = Utility.getShort (buffer, ptr);
2020-05-16 07:45:58 +00:00
if (offset == 0)
break;
buffer[offset] = buffer[ptr + 2];
buffer[offset + 1] = buffer[ptr + 3];
ptr += 4;
}
createImage ();
}
// ---------------------------------------------------------------------------------//
public int getDelay ()
// ---------------------------------------------------------------------------------//
{
return delay;
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
@Override
public String getText ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
StringBuilder text = new StringBuilder (super.getText ());
2019-11-28 04:36:49 +00:00
text.append ("\n\n");
2017-01-26 04:19:23 +00:00
2018-07-30 08:44:29 +00:00
if (controlBytes != null)
2017-01-26 11:30:16 +00:00
{
text.append ("SCB\n---\n");
2018-07-30 08:44:29 +00:00
for (int i = 0; i < controlBytes.length; i += 8)
2017-01-26 11:30:16 +00:00
{
for (int j = 0; j < 8; j++)
2019-11-19 11:41:48 +00:00
{
if (i + j >= controlBytes.length)
break;
2018-07-30 08:44:29 +00:00
text.append (String.format (" %3d: %02X ", i + j, controlBytes[i + j]));
2019-11-19 11:41:48 +00:00
}
2017-01-26 11:30:16 +00:00
text.append ("\n");
}
text.append ("\n");
}
2017-01-26 04:19:23 +00:00
2017-01-27 07:11:00 +00:00
if (colorTables != null)
2017-02-01 21:15:30 +00:00
{
text.append ("Color Table\n\n #");
for (int i = 0; i < 16; i++)
text.append (String.format (" %02X ", i));
text.append ("\n--");
for (int i = 0; i < 16; i++)
text.append (" ----");
text.append ("\n");
2017-01-27 07:11:00 +00:00
for (ColorTable colorTable : colorTables)
{
2017-02-01 21:15:30 +00:00
text.append (colorTable.toLine ());
text.append ("\n");
2017-01-27 07:11:00 +00:00
}
2017-02-01 21:15:30 +00:00
}
text.append ("\nScreen lines\n\n");
for (int i = 0; i < 200; i++)
{
2018-07-28 12:00:02 +00:00
text.append (String.format ("Line: %02X %<3d%n", i));
2017-02-01 21:15:30 +00:00
text.append (HexFormatter.format (buffer, i * 160, 160));
text.append ("\n\n");
}
2017-01-26 04:19:23 +00:00
text.deleteCharAt (text.length () - 1);
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
2017-01-25 11:02:50 +00:00
}