mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-27 08:49:20 +00:00
d35a9670b5
We weren't handling oddly sized images well. This was mostly a problem for Apple Preferred Format, PNT/$0002.
1232 lines
41 KiB
C++
1232 lines
41 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* Various super-hi-res conversions, including 3200 color images.
|
|
*/
|
|
#include "StdAfx.h"
|
|
#include "SuperHiRes.h"
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatSHR (base class)
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* The basic Super Hi-Res "PIC" file format ($c1/0000) is just a dump of
|
|
* memory from $2000-$9fff.
|
|
*
|
|
* $2000-9cff: pixel data, 160 bytes per line
|
|
* $9d00-9dc7: SCB bytes (8 bits each)
|
|
* 0-3 : color table number (0 to 15)
|
|
* 4 : reserved, must be 0
|
|
* 5 : fill mode (1=on, 0=off)
|
|
* 6 : interrupts (1=generated, 0=not)
|
|
* 7 : number of pixels (0=320, 1=640)
|
|
* $9e00-9fff: color tables (16 sets of 2 bytes)
|
|
* 0-3 : blue intensity
|
|
* 4-7 : green intensity
|
|
* 8-11: red intensity
|
|
* 12-15: reserved, must be 0
|
|
*
|
|
* Packed images ($c0/0001) are just the above packed with PackBytes. The
|
|
* "Apple Preferred" $c0/0002 format is funky, and has arbitrary resolution.
|
|
*
|
|
* 3200 color images do away with the SCB (they're always 320 mode and don't
|
|
* support fill mode) and instead follow the pixel data with 200 sets of
|
|
* color tables. According to the FTN for $c1/0002 ("Brooks" format), the
|
|
* color table is reversed, with color #15 appearing first in each table.
|
|
*/
|
|
|
|
MyDIBitmap* ReformatSHR::SHRScreenToBitmap8(const SHRScreen* pScreen)
|
|
{
|
|
return SHRDataToBitmap8(pScreen->pixels, pScreen->scb,
|
|
pScreen->colorTable, kPixelBytesPerLine, kNumLines,
|
|
kOutputWidth, kOutputHeight);
|
|
}
|
|
|
|
MyDIBitmap* ReformatSHR::SHRDataToBitmap8(const uint8_t* pPixels,
|
|
const uint8_t* pSCB, const uint8_t* pColorTable,
|
|
unsigned int bytesPerLine, unsigned int numScanLines,
|
|
unsigned int outputWidthPix, unsigned int outputHeightPix)
|
|
{
|
|
MyDIBitmap* pDib = new MyDIBitmap;
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
// We can have an odd number of pixels, which results in 1-3 empty
|
|
// spaces in the last byte. We double the 4bpp pixels, so we're
|
|
// always outputting 4 pixels per byte.
|
|
if (outputWidthPix > bytesPerLine * 4 ||
|
|
outputWidthPix < bytesPerLine * 4 - 3) {
|
|
LOGW(" Invalid params: outputWidthPix=%u bytesPerLine=%u",
|
|
outputWidthPix, bytesPerLine);
|
|
ASSERT(false);
|
|
goto bail;
|
|
}
|
|
|
|
const uint8_t* pSCBStart = pSCB; // sanity check only
|
|
|
|
/*
|
|
* Set up a DIB to hold the data. "Create" returns a pointer to the
|
|
* pixel storage.
|
|
*/
|
|
uint8_t* outBuf = (uint8_t*) pDib->Create(outputWidthPix, outputHeightPix,
|
|
8, kNumColorTables * kNumEntriesPerColorTable);
|
|
if (outBuf == NULL) {
|
|
delete pDib;
|
|
pDib = NULL;
|
|
goto bail;
|
|
}
|
|
int outputStride = pDib->GetPitch();
|
|
|
|
/*
|
|
* Convert color palette.
|
|
*/
|
|
const uint16_t* pClrTable;
|
|
pClrTable = (const uint16_t*) pColorTable;
|
|
for (int table = 0; table < kNumColorTables; table++) {
|
|
for (int entry = 0; entry < kNumEntriesPerColorTable; entry++) {
|
|
GSColor(*pClrTable++, &fColorTables[table][entry]);
|
|
}
|
|
}
|
|
pDib->SetColorTable((RGBQUAD*)fColorTables);
|
|
|
|
if (false) {
|
|
LOGI(" SHR color table 0");
|
|
for (int ii = 0; ii < kNumEntriesPerColorTable; ii++) {
|
|
LOGI(" %2d: 0x%02x %02x %02x",
|
|
ii,
|
|
fColorTables[0][ii].rgbRed,
|
|
fColorTables[0][ii].rgbGreen,
|
|
fColorTables[0][ii].rgbBlue);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the pixels to palette indices.
|
|
*/
|
|
unsigned int line;
|
|
for (line = 0; line < numScanLines; line++) {
|
|
bool mode640, fillMode;
|
|
int colorTableOffset;
|
|
unsigned int byteCount;
|
|
uint8_t pixelVal;
|
|
uint8_t colorIndex;
|
|
int x = 0;
|
|
|
|
mode640 = (*pSCB & kSCBNumPixels) != 0;
|
|
fillMode = (*pSCB & kSCBFillMode) != 0;
|
|
colorTableOffset = (*pSCB & kSCBColorTableMask) * kNumEntriesPerColorTable;
|
|
|
|
if (!line) {
|
|
LOGI(" SHR line 0 mode640=%d", mode640);
|
|
}
|
|
if (fillMode) {
|
|
/* I doubt anyone uses this in still images */
|
|
LOGI(" SHR FILL MODE!!");
|
|
DebugBreak();
|
|
}
|
|
|
|
#define SetPix(x, y, colridx) \
|
|
outBuf[((outputHeightPix-1) - (y)) * outputStride + (x)] = colridx
|
|
|
|
if (mode640) {
|
|
/* 640 mode, one byte becomes four non-doubled pixels (1:4) */
|
|
unsigned int fullBytesPerLine = outputWidthPix / 4;
|
|
ASSERT(fullBytesPerLine <= bytesPerLine);
|
|
|
|
for (byteCount = 0; byteCount < fullBytesPerLine; byteCount++) {
|
|
uint8_t pixelByte = *pPixels++;
|
|
|
|
pixelVal = (pixelByte >> 6) & 0x03;
|
|
colorIndex = colorTableOffset + pixelVal + 8;
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
|
|
pixelVal = (pixelByte >> 4) & 0x03;
|
|
colorIndex = colorTableOffset + pixelVal + 12;
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
|
|
pixelVal = (pixelByte >> 2) & 0x03;
|
|
colorIndex = colorTableOffset + pixelVal + 0;
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
|
|
pixelVal = pixelByte & 0x03;
|
|
//rgbval = fColorLookup[colorTable][pixelVal];
|
|
colorIndex = colorTableOffset + pixelVal + 4;
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
}
|
|
if (byteCount != bytesPerLine) {
|
|
// 1-3 pixels in last byte
|
|
uint8_t pixelByte = *pPixels++;
|
|
int rem = outputWidthPix - fullBytesPerLine * 4;
|
|
if (rem >= 3) {
|
|
pixelVal = (pixelByte >> 6) & 0x03;
|
|
colorIndex = colorTableOffset + pixelVal + 8;
|
|
SetPix(x, line * 2, colorIndex);
|
|
SetPix(x++, line * 2 + 1, colorIndex);
|
|
}
|
|
if (rem >= 2) {
|
|
pixelVal = (pixelByte >> 4) & 0x03;
|
|
colorIndex = colorTableOffset + pixelVal + 12;
|
|
SetPix(x, line * 2, colorIndex);
|
|
SetPix(x++, line * 2 + 1, colorIndex);
|
|
}
|
|
if (rem >= 1) {
|
|
pixelVal = (pixelByte >> 2) & 0x03;
|
|
colorIndex = colorTableOffset + pixelVal + 0;
|
|
SetPix(x, line * 2, colorIndex);
|
|
SetPix(x++, line * 2 + 1, colorIndex);
|
|
}
|
|
}
|
|
} else {
|
|
/* 320 mode, one byte becomes two doubled pixels (1:4) */
|
|
unsigned int fullBytesPerLine = outputWidthPix / 4;
|
|
ASSERT(fullBytesPerLine <= bytesPerLine);
|
|
|
|
for (byteCount = 0; byteCount < fullBytesPerLine; byteCount++) {
|
|
uint8_t pixelByte = *pPixels++;
|
|
|
|
pixelVal = (pixelByte >> 4) & 0x0f;
|
|
colorIndex = colorTableOffset + pixelVal;
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
|
|
pixelVal = pixelByte & 0x0f;
|
|
colorIndex = colorTableOffset + pixelVal;
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
SetPix(x, line*2, colorIndex);
|
|
SetPix(x++, line*2+1, colorIndex);
|
|
}
|
|
if (byteCount != bytesPerLine) {
|
|
// 1 pixel in last byte
|
|
uint8_t pixelByte = *pPixels++;
|
|
pixelVal = (pixelByte >> 4) & 0x0f;
|
|
colorIndex = colorTableOffset + pixelVal;
|
|
SetPix(x, line * 2, colorIndex);
|
|
SetPix(x++, line * 2 + 1, colorIndex);
|
|
SetPix(x, line * 2, colorIndex);
|
|
SetPix(x++, line * 2 + 1, colorIndex);
|
|
}
|
|
}
|
|
#undef SetPix
|
|
|
|
ASSERT(x == outputWidthPix);
|
|
|
|
pSCB++;
|
|
}
|
|
|
|
ASSERT(line == outputHeightPix/2);
|
|
ASSERT(pSCB == pSCBStart + numScanLines);
|
|
|
|
bail:
|
|
return pDib;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatUnpackedSHR
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*
|
|
* Occasionally somebody slaps the wrong aux type on, so if we're in
|
|
* "relaxed" mode we accept just about anything that's the right size.
|
|
*/
|
|
void ReformatUnpackedSHR::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
long fileType = pHolder->GetFileType();
|
|
long auxType = pHolder->GetAuxType();
|
|
long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
|
|
const char* nameExt = pHolder->GetNameExt();
|
|
bool relaxed;
|
|
|
|
relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;
|
|
|
|
/* uncompressed $c1/0000, but many variations on aux type */
|
|
if ((fileType == kTypePIC && auxType == 0x0000 && fileLen == 32768) ||
|
|
(relaxed &&
|
|
(
|
|
(fileType == kTypePIC && fileLen == 32768) ||
|
|
(fileType == kTypeBIN && fileLen == 32768 &&
|
|
(stricmp(nameExt, ".PIC") == 0 || stricmp(nameExt, ".SHR") == 0))
|
|
)
|
|
))
|
|
{
|
|
applies = ReformatHolder::kApplicProbably;
|
|
}
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatSHR_PIC, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
}
|
|
|
|
/*
|
|
* Convert an unpacked Super Hi-Res image.
|
|
*/
|
|
int ReformatUnpackedSHR::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
MyDIBitmap* pDib;
|
|
int retval = -1;
|
|
|
|
if (pHolder->GetSourceLen(part) != kTotalSize) {
|
|
LOGI(" SHR file is not %d bytes long!", kTotalSize);
|
|
goto bail;
|
|
}
|
|
|
|
ASSERT(sizeof(SHRScreen) == kTotalSize);
|
|
|
|
memcpy(&fScreen, pHolder->GetSourceBuf(part), sizeof(fScreen));
|
|
|
|
pDib = SHRScreenToBitmap8(&fScreen);
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
SetResultBuffer(pOutput, pDib);
|
|
retval = 0;
|
|
|
|
bail:
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatAppSHR
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*
|
|
* This file type seems exclusive to the IIgs version of "John Elway
|
|
* Quarterback".
|
|
*/
|
|
void ReformatJEQSHR::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
long fileType = pHolder->GetFileType();
|
|
long auxType = pHolder->GetAuxType();
|
|
long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
|
|
const char* nameExt = pHolder->GetNameExt();
|
|
bool relaxed;
|
|
|
|
relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;
|
|
|
|
/* uncompressed $c1/0000, but many variations on aux type */
|
|
if (fileType == kTypeBIN && auxType == 0x0000 &&
|
|
fileLen == kExpectedLen && stricmp(nameExt, ".APP") == 0)
|
|
{
|
|
applies = ReformatHolder::kApplicProbably;
|
|
}
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatSHR_JEQ, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
}
|
|
|
|
/*
|
|
* Convert one of these odd-format images. It appears to be a dump of the
|
|
* SHR screen minus the "reserved" section between the SCB and color table,
|
|
* and only one color table is stored. Total savings of 480 bytes -- less
|
|
* than a ProDOS block. Sorta dumb.
|
|
*/
|
|
int ReformatJEQSHR::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
MyDIBitmap* pDib;
|
|
const uint8_t* srcBuf = pHolder->GetSourceBuf(part);
|
|
long srcLen = pHolder->GetSourceLen(part);
|
|
int retval = -1;
|
|
|
|
if (pHolder->GetSourceLen(part) != kExpectedLen) {
|
|
LOGI(" SHR file is not %d bytes long!", kTotalSize);
|
|
goto bail;
|
|
}
|
|
|
|
ASSERT(sizeof(SHRScreen) == kTotalSize);
|
|
|
|
memcpy(&fScreen.pixels, srcBuf, 32000);
|
|
memcpy(&fScreen.scb, srcBuf + 0x7d00, 256);
|
|
memcpy(&fScreen.colorTable, srcBuf + 0x7e00, 32);
|
|
|
|
pDib = SHRScreenToBitmap8(&fScreen);
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
SetResultBuffer(pOutput, pDib);
|
|
retval = 0;
|
|
|
|
bail:
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatPaintworksSHR
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*/
|
|
void ReformatPaintworksSHR::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
long fileType = pHolder->GetFileType();
|
|
long auxType = pHolder->GetAuxType();
|
|
//long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
|
|
//const char* nameExt = pHolder->GetNameExt();
|
|
//bool relaxed;
|
|
|
|
//relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;
|
|
|
|
/* PNT $c0/0000 */
|
|
if (fileType == kTypePNT && auxType == 0x0000)
|
|
applies = ReformatHolder::kApplicProbably;
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatSHR_Paintworks, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
}
|
|
|
|
/*
|
|
* Convert a PaintWorks format Super Hi-Res image.
|
|
*
|
|
* The format is loosely documented in the file type note for $C0/0000:
|
|
* +$000 to +$01F: Bytes
|
|
* Super Hi-Res Palette
|
|
* +$020 to +$021: Word
|
|
* Background color
|
|
* +$022 to +$221: Bytes
|
|
* Patterns. 16 QuickDraw II patterns, each 32 bytes in length.
|
|
* +$222 to EOF: Bytes
|
|
* Packed graphics data. Note that the unpacked data could be longer
|
|
* than one Super Hi-Res screen (Paintworks allows full-page sized
|
|
* documents).
|
|
*
|
|
* Sometimes it runs a few bytes over. If it runs significantly under, it's
|
|
* probably a generic packed image (PNT/0001) with the wrong auxtype.
|
|
*/
|
|
int ReformatPaintworksSHR::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
MyDIBitmap* pDib;
|
|
const int kPWOutputLines = 396;
|
|
const int kPWOutputSize = kPWOutputLines * kPixelBytesPerLine;
|
|
const int kPWDataOffset = 0x222;
|
|
uint8_t scb[kPWOutputLines];
|
|
uint8_t colorTable[kNumEntriesPerColorTable * kColorTableEntrySize];
|
|
uint8_t* unpackBuf = NULL;
|
|
int retval = -1;
|
|
int i, result;
|
|
|
|
if (pHolder->GetSourceLen(part) < kMinSize) {
|
|
LOGW(" SHR file too short (%ld)", pHolder->GetSourceLen(part));
|
|
goto bail;
|
|
}
|
|
|
|
/*
|
|
* Copy the color table out.
|
|
*/
|
|
memcpy(colorTable, pHolder->GetSourceBuf(part), sizeof(colorTable));
|
|
|
|
/*
|
|
* Unpack the packed data.
|
|
*
|
|
* For some reason it's fairly common to have exactly 9 bytes left over
|
|
* in the input. It appears that these files have 400 lines of graphics,
|
|
* and the first 8 bytes are PackBytes for the 4 lines. The very last
|
|
* byte is a zero, for no apparent reason. Best guess is something other
|
|
* than PaintWorks wrote these, because SuperConvert expects them to
|
|
* have 396 lines and only recognizes that many.
|
|
*/
|
|
unpackBuf = new uint8_t[kPWOutputSize];
|
|
if (unpackBuf == NULL)
|
|
goto bail;
|
|
memset(unpackBuf, 0, sizeof(unpackBuf)); // in case we fall short
|
|
|
|
result = UnpackBytes(unpackBuf,
|
|
pHolder->GetSourceBuf(part) + kPWDataOffset,
|
|
kPWOutputSize,
|
|
pHolder->GetSourceLen(part) - kPWDataOffset);
|
|
if (result != kPWOutputSize) {
|
|
LOGI("WARNING: UnpackBytes wasn't happy");
|
|
|
|
#if 0
|
|
/* thd282.shk (rev76.2) has a large collection of these */
|
|
if (UnpackBytes((uint8_t*) &fScreen,
|
|
pHolder->GetSourceBuf(part),
|
|
kTotalSize,
|
|
pHolder->GetSourceLen(part)) == 0)
|
|
{
|
|
pDib = SHRScreenToBitmap8(&fScreen);
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
goto gotit;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
for (i = 0; i < kPWOutputLines; i++)
|
|
scb[i] = 0; // 320 mode
|
|
pDib = SHRDataToBitmap8(unpackBuf, scb, colorTable, kPixelBytesPerLine,
|
|
kPWOutputLines, kOutputWidth, kPWOutputLines * 2);
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
//gotit:
|
|
SetResultBuffer(pOutput, pDib);
|
|
retval = 0;
|
|
|
|
bail:
|
|
delete[] unpackBuf;
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatPackedSHR
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*/
|
|
void ReformatPackedSHR::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
long fileType = pHolder->GetFileType();
|
|
long auxType = pHolder->GetAuxType();
|
|
//long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
|
|
//const char* nameExt = pHolder->GetNameExt();
|
|
//bool relaxed;
|
|
|
|
//relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;
|
|
|
|
/* compressed $c0/0001 */
|
|
if (fileType == kTypePNT && auxType == 0x0001)
|
|
applies = ReformatHolder::kApplicProbably;
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatSHR_Packed, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
}
|
|
|
|
/*
|
|
* Convert a packed Super Hi-Res image (PNT/$0001).
|
|
*/
|
|
int ReformatPackedSHR::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
MyDIBitmap* pDib;
|
|
int retval = -1;
|
|
|
|
if (pHolder->GetSourceLen(part) < 4) {
|
|
LOGW(" SHR file too short (%ld)", pHolder->GetSourceLen(part));
|
|
goto bail;
|
|
}
|
|
|
|
ASSERT(sizeof(SHRScreen) == kTotalSize);
|
|
|
|
if (UnpackBytes((uint8_t*) &fScreen,
|
|
pHolder->GetSourceBuf(part), kTotalSize,
|
|
pHolder->GetSourceLen(part)) != kTotalSize)
|
|
{
|
|
LOGW(" SHR UnpackBytes failed");
|
|
goto bail;
|
|
}
|
|
|
|
pDib = SHRScreenToBitmap8(&fScreen);
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
SetResultBuffer(pOutput, pDib);
|
|
retval = 0;
|
|
|
|
bail:
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatAPFSHR
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*/
|
|
void ReformatAPFSHR::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
long fileType = pHolder->GetFileType();
|
|
long auxType = pHolder->GetAuxType();
|
|
//long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
|
|
//const char* nameExt = pHolder->GetNameExt();
|
|
//bool relaxed;
|
|
|
|
//relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;
|
|
|
|
/* compressed $c0/0002 */
|
|
if (fileType == kTypePNT && auxType == 0x0002)
|
|
applies = ReformatHolder::kApplicProbably;
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatSHR_APF, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
}
|
|
|
|
/*
|
|
* Convert a Super Hi-Res image in APF format (PNT/$0002).
|
|
*
|
|
* Blocks are a 32-bit length followed by a pascal string describing the
|
|
* contents. Common blocks are "MAIN", "PATS", and "SCIB", but could be
|
|
* anything (e.g. "Platinum Paint").
|
|
*
|
|
* All we're really interested in is the "MAIN" block, though we need
|
|
* to handle MULTIPAL as well for 3200-color images.
|
|
*/
|
|
int ReformatAPFSHR::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
MyDIBitmap* pDib;
|
|
const uint8_t* srcPtr = pHolder->GetSourceBuf(part);
|
|
long srcLen = pHolder->GetSourceLen(part);
|
|
uint8_t multiPal[kNumLines *
|
|
kNumEntriesPerColorTable * kColorTableEntrySize];
|
|
bool is3200 = false;
|
|
bool haveGraphics = false;
|
|
int retval = -1;
|
|
|
|
if (srcLen < 4) {
|
|
LOGW(" SHR file too short (%ld)", srcLen);
|
|
goto bail;
|
|
}
|
|
|
|
ASSERT(sizeof(SHRScreen) == kTotalSize);
|
|
|
|
while (srcLen > 0) {
|
|
CString blockName;
|
|
const uint8_t* nextBlock;
|
|
long blockLen, dataLen, nextLen;
|
|
int nameLen;
|
|
|
|
blockLen = Read32(&srcPtr, &srcLen);
|
|
if (blockLen > srcLen + 4) {
|
|
LOGI(" APFSHR WARNING: found blockLen=%ld, remaining len=%ld",
|
|
blockLen, srcLen);
|
|
break;
|
|
//goto bail;
|
|
}
|
|
nextBlock = srcPtr + (blockLen-4);
|
|
nextLen = srcLen - (blockLen-4);
|
|
|
|
nameLen = ::GetPascalString(srcPtr, srcLen, &blockName);
|
|
if (nameLen < 0) {
|
|
LOGI(" APFSHR failed getting pascal name, bailing");
|
|
goto bail;
|
|
}
|
|
|
|
srcPtr += nameLen+1;
|
|
srcLen -= nameLen+1;
|
|
|
|
dataLen = blockLen - (nameLen+1 + 4);
|
|
|
|
LOGI(" APFSHR block='%ls' blockLen=%ld (dataLen=%ld) start=0x%p",
|
|
(LPCWSTR) blockName, blockLen, dataLen, srcPtr);
|
|
|
|
if (blockName == "MAIN") {
|
|
if (UnpackMain(srcPtr, dataLen) != 0)
|
|
goto bail;
|
|
haveGraphics = true;
|
|
} else if (blockName == "MULTIPAL") {
|
|
if (UnpackMultipal(multiPal, srcPtr, dataLen) == 0)
|
|
is3200 = true;
|
|
} else if (blockName == "NOTE") {
|
|
UnpackNote(srcPtr, dataLen);
|
|
} else {
|
|
LOGI(" APFSHR (ignoring segment '%ls')", (LPCWSTR) blockName);
|
|
}
|
|
|
|
srcPtr = nextBlock;
|
|
srcLen = nextLen;
|
|
}
|
|
|
|
if (!haveGraphics)
|
|
goto bail;
|
|
|
|
// TODO: we don't currently handle MULTIPAL with anything other than
|
|
// a 320x200 image. Fortunately this is rare.
|
|
if (is3200 && !fNonStandard) {
|
|
Reformat3200SHR ref3200(&fScreen, multiPal);
|
|
|
|
pDib = ref3200.SHR3200ToBitmap24();
|
|
} else if (fNonStandard) {
|
|
pDib = SHRDataToBitmap8(fPixelStore, fSCBStore,
|
|
fScreen.colorTable, fPixelBytesPerLine, fNumScanLines,
|
|
fOutputWidth, fOutputHeight);
|
|
|
|
} else {
|
|
pDib = SHRScreenToBitmap8(&fScreen);
|
|
}
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
SetResultBuffer(pOutput, pDib);
|
|
retval = 0;
|
|
|
|
bail:
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Unpack the "MAIN" segment of an APF paint file into fScreen.
|
|
*
|
|
* +$00 MasterMode: 16-bit ModeWord value for MasterSCB. A ModeWord
|
|
* with a high byte of zero has the SCB in the low byte.
|
|
* +$02 PixelsPerScanLine: 16-bit value, usually 320 or 640 but may
|
|
* be something different.
|
|
* +$04 NumColorTables: 16-bit value, should be between 0 and 15
|
|
* +$06 ColorTableArray: [0..NumColorTables-1] of ColorTable, where
|
|
* a ColorTable is 16 sets of 16-bit $0RGB values.
|
|
* +$xx NumScanLines: 16-bit count of scan lines, often 200.
|
|
* +$xx+2 ScanLineDirectory: [0..NumScanLines-1] of DirEntry. Each
|
|
* DirEntry has two parts, a 16-bit count of packed-data-length
|
|
* for each line, followed by a 16-bit ModeWord value.
|
|
* +$yy PackedScanLines: [0..NumScanLines-1] of packed data. Each
|
|
* scan line is individually packed with PackBytes.
|
|
*
|
|
* Returns 0 on success, -1 on failure.
|
|
*
|
|
* Question: if the image is 640x200, is it in 640 mode? Can we depend on
|
|
* the MasterMode word to tell us if it's a 320-mode 4-bit-per-pixel image
|
|
* that happens to be 640 pixels wide? Did all //gs applications set this
|
|
* correctly? Can the format be different on every line?
|
|
*/
|
|
int ReformatAPFSHR::UnpackMain(const uint8_t* srcPtr, long srcLen)
|
|
{
|
|
const int kColorTableSize = kNumEntriesPerColorTable * kColorTableEntrySize;
|
|
uint16_t masterMode;
|
|
unsigned int numColorTables;
|
|
uint16_t* packedDataLen = NULL;
|
|
int retval = -1;
|
|
|
|
if (srcLen < 64) {
|
|
/* can't possibly be this small */
|
|
LOGI(" APFSHR unlikely srcLen %d", srcLen);
|
|
goto bail;
|
|
}
|
|
|
|
masterMode = Read16(&srcPtr, &srcLen);
|
|
fPixelsPerScanLine = Read16(&srcPtr, &srcLen);
|
|
numColorTables = Read16(&srcPtr, &srcLen);
|
|
if (fPixelsPerScanLine == 0 || fPixelsPerScanLine > kMaxPixelsPerScan) {
|
|
LOGI(" APFSHR unsupported pixelsPerScanLine %d",
|
|
fPixelsPerScanLine);
|
|
goto bail;
|
|
}
|
|
|
|
LOGD(" APFSHR masterMode=0x%04x, ppsl=%d, nct=%d",
|
|
masterMode, fPixelsPerScanLine, numColorTables);
|
|
if (numColorTables == 0 || numColorTables > kNumColorTables) {
|
|
// Zero is valid per the spec, but we don't know how to render that.
|
|
// Rather than showing an all-black bitmap, just fail.
|
|
LOGI(" APFSHR unsupported numColorTables %d", numColorTables);
|
|
goto bail;
|
|
}
|
|
|
|
/*
|
|
* Load the color tables.
|
|
*/
|
|
memset(fScreen.colorTable, 0, sizeof(fScreen.colorTable));
|
|
for (unsigned int i = 0; i < numColorTables; i++) {
|
|
if (srcLen < kColorTableSize) {
|
|
LOGI(" APFSHR underrun while copying color tables");
|
|
goto bail;
|
|
}
|
|
|
|
memcpy(&fScreen.colorTable[i * kColorTableSize], srcPtr,
|
|
kColorTableSize);
|
|
srcPtr += kColorTableSize;
|
|
srcLen -= kColorTableSize;
|
|
}
|
|
|
|
/*
|
|
* Get the scanline count.
|
|
*
|
|
* fOutputHeight is twice the #of scan lines because we use pixel
|
|
* doubling so we can handle both 320 mode and 640 mode.
|
|
*/
|
|
fNumScanLines = Read16(&srcPtr, &srcLen);
|
|
if (fNumScanLines == 0 || fNumScanLines > kMaxScanLines) {
|
|
LOGI(" APFSHR unsupported numScanLines %d", fNumScanLines);
|
|
goto bail;
|
|
}
|
|
if ((fPixelsPerScanLine == 320 || fPixelsPerScanLine == 640) &&
|
|
fNumScanLines == kNumLines)
|
|
{
|
|
/* standard-sized image, use fScreen */
|
|
ASSERT(!fNonStandard);
|
|
LOGD(" Assuming this is a standard, full-width SHR image");
|
|
fPixelBytesPerLine = kPixelBytesPerLine; // 160
|
|
fPixelStore = fScreen.pixels;
|
|
fSCBStore = fScreen.scb;
|
|
// These don't matter if "fNonStandard" is false, but set them anyway
|
|
// so the debug output looks okay.
|
|
fOutputWidth = 640;
|
|
fOutputHeight = kNumLines * 2;
|
|
} else {
|
|
/* non-standard image, allocate storage */
|
|
fNonStandard = true;
|
|
LOGI(" NOTE: non-standard image size %dx%d, 2-bit-mode=%d",
|
|
fPixelsPerScanLine, fNumScanLines,
|
|
(masterMode & kSCBNumPixels) != 0);
|
|
|
|
fOutputHeight = fNumScanLines * 2;
|
|
if ((masterMode & kSCBNumPixels) == 0) {
|
|
fOutputWidth = fPixelsPerScanLine * 2;
|
|
fPixelBytesPerLine = (fPixelsPerScanLine + 1) / 2; // "320 mode"
|
|
} else {
|
|
fOutputWidth = fPixelsPerScanLine;
|
|
fPixelBytesPerLine = (fPixelsPerScanLine + 3) / 4; // "640 mode"
|
|
}
|
|
fPixelStore = new uint8_t[fPixelBytesPerLine * fNumScanLines];
|
|
if (fPixelStore == NULL) {
|
|
LOGI(" APFSHR ERROR: alloc of %d bytes fPixelStore failed",
|
|
fPixelBytesPerLine * fNumScanLines);
|
|
goto bail;
|
|
}
|
|
fSCBStore = new uint8_t[fNumScanLines];
|
|
if (fSCBStore == NULL) {
|
|
LOGI(" APFSHR ERROR: alloc of %d bytes fSCBStore failed",
|
|
fNumScanLines);
|
|
goto bail;
|
|
}
|
|
}
|
|
LOGD(" APFSHR numScanLines=%d, outputWidth=%d, pixelBytesPerLine=%d",
|
|
fNumScanLines, fOutputWidth, fPixelBytesPerLine);
|
|
|
|
/*
|
|
* Get the per-scanline data.
|
|
*/
|
|
packedDataLen = new uint16_t[fNumScanLines];
|
|
if (packedDataLen == NULL)
|
|
goto bail;
|
|
for (unsigned int i = 0; i < fNumScanLines; i++) {
|
|
packedDataLen[i] = Read16(&srcPtr, &srcLen);
|
|
if (packedDataLen[i] > fPixelsPerScanLine) {
|
|
/* each pixel is 2 or 4 bits, so this is a 2-4x expansion */
|
|
LOGI(" APFSHR got funky packed len %d for line %d",
|
|
packedDataLen[i], i);
|
|
goto bail;
|
|
}
|
|
|
|
uint16_t mode = Read16(&srcPtr, &srcLen);
|
|
if (mode >> 8 == 0)
|
|
fSCBStore[i] = (uint8_t) mode;
|
|
else {
|
|
LOGI(" APFSHR odd mode 0x%04x on line %d", mode, i);
|
|
fSCBStore[i] = 0; // ?
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Unpack the scan lines into the pixel storage. Some APF files
|
|
* use PackBytes in a not-quite-safe way, e.g. a 28x30 image with
|
|
* 14 bytes per line uses the 32-bit pattern repeat mode (0x3fff),
|
|
* yielding 16 bytes. We need to unpack to a temp buffer.
|
|
*
|
|
* I've found a few from 816/Paint that appear to have the wrong width
|
|
* value; they claim 320 but look like they're actually 640 (the image
|
|
* seems to be cut in half, and we unpack 2x as much data as we expect).
|
|
*/
|
|
for (unsigned int i = 0; i < fNumScanLines; i++) {
|
|
// PackBytes can generate 256 bytes from a single pair of bytes.
|
|
uint8_t tmpBuf[ReformatSHR::kMaxPixelsPerScan];
|
|
|
|
if (srcLen <= 0) {
|
|
LOGI(" APFSHR ran out of data while unpacking pixels");
|
|
goto bail;
|
|
}
|
|
int actual = UnpackBytes(tmpBuf, srcPtr, sizeof(tmpBuf), packedDataLen[i]);
|
|
if (actual < (int) fPixelBytesPerLine) { // includes actual < 0
|
|
LOGI(" APFSHR UnpackBytes failed on line %d (pbpl=%d actual=%d)",
|
|
i, fPixelBytesPerLine, actual);
|
|
goto bail;
|
|
}
|
|
//LOGD("+++ unpackbytes %d pbpl=%d pdl=%d --> %d",
|
|
// i, fPixelBytesPerLine, packedDataLen[i], actual);
|
|
memcpy(&fPixelStore[i * fPixelBytesPerLine], tmpBuf, fPixelBytesPerLine);
|
|
|
|
srcPtr += packedDataLen[i];
|
|
srcLen -= packedDataLen[i];
|
|
}
|
|
|
|
retval = 0;
|
|
|
|
bail:
|
|
delete[] packedDataLen;
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Unpack the "multipal" data.
|
|
*
|
|
* Unfortunately the guy who wrote "SuperView" decided to un-swap the
|
|
* Brooks format by stuffing them into the file the wrong way. We could
|
|
* fix it here, but we can't reliably detect the files.
|
|
*/
|
|
int ReformatAPFSHR::UnpackMultipal(uint8_t* dstPtr,
|
|
const uint8_t* srcPtr, long srcLen)
|
|
{
|
|
const int kMultipalSize = kNumLines *
|
|
kNumEntriesPerColorTable * kColorTableEntrySize;
|
|
int numColorTables;
|
|
|
|
/* check the size; use (size+2) to factor in 16-bit count */
|
|
if (srcLen < kMultipalSize+2) {
|
|
LOGI(" APFSHR got too-small multipal size %ld", srcLen);
|
|
return -1;
|
|
} else if (srcLen > kMultipalSize+2) {
|
|
LOGI(" APFSHR WARNING: oversized multipal (%ld, expected %ld)",
|
|
srcLen, kMultipalSize);
|
|
/* keep going */
|
|
}
|
|
|
|
/* get the #of color tables; should always be 200 */
|
|
numColorTables = Read16(&srcPtr, &srcLen);
|
|
if (numColorTables != kNumLines) {
|
|
/* expecting one palette per line */
|
|
LOGI(" APFSHR ignoring multipal with %d color tables",
|
|
numColorTables);
|
|
return -1;
|
|
}
|
|
|
|
if (1) {
|
|
memcpy(dstPtr, srcPtr, kMultipalSize);
|
|
} else {
|
|
#if 0
|
|
/* swap entries */
|
|
const uint16_t* pSrcTable;
|
|
uint16_t* pDstTable = (uint16_t*) dst;
|
|
int table, entry;
|
|
for (table = 0; table < kNumLines; table++) {
|
|
pSrcTable = (const uint16_t*)src +
|
|
(kNumLines - table) * kNumEntriesPerColorTable;
|
|
for (entry = 0; entry < kNumEntriesPerColorTable; entry++) {
|
|
pDstTable[entry] = *pSrcTable++;
|
|
}
|
|
pDstTable += kNumEntriesPerColorTable;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Unpack a "NOTE" chunk. This seems to be a 16-bit count followed by
|
|
* ASCII data.
|
|
*/
|
|
void ReformatAPFSHR::UnpackNote(const uint8_t* srcPtr, long srcLen)
|
|
{
|
|
int numChars;
|
|
|
|
numChars = Read16(&srcPtr, &srcLen);
|
|
|
|
if (numChars != srcLen) {
|
|
LOGI(" APFSHR note chunk has numChars=%d but dataLen=%ld, bailing",
|
|
numChars, srcLen);
|
|
return;
|
|
}
|
|
|
|
CString str;
|
|
while (srcLen--) {
|
|
str += *srcPtr++;
|
|
}
|
|
|
|
LOGI(" APFSHR note: '%ls'", (LPCWSTR) str);
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* Reformat3200SHR
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*/
|
|
void Reformat3200SHR::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
long fileType = pHolder->GetFileType();
|
|
long auxType = pHolder->GetAuxType();
|
|
long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
|
|
const char* nameExt = pHolder->GetNameExt();
|
|
bool relaxed;
|
|
|
|
relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;
|
|
|
|
/* should be $c1/0002, but there are many variations */
|
|
if ((fileType == kTypePIC && auxType == 0x0002 && fileLen == 38400) ||
|
|
(fileType == kTypeBIN && fileLen == 38400 &&
|
|
stricmp(nameExt, ".3200") == 0) ||
|
|
(relaxed &&
|
|
(
|
|
(fileType == kTypePIC && fileLen == 38400)
|
|
)
|
|
))
|
|
{
|
|
applies = ReformatHolder::kApplicProbably;
|
|
}
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatSHR_3200, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
}
|
|
|
|
/*
|
|
* Convert a 3200-color Super Hi-Res Image.
|
|
*/
|
|
int Reformat3200SHR::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
MyDIBitmap* pDib;
|
|
int retval = -1;
|
|
|
|
if (pHolder->GetSourceLen(part) != kExtTotalSize) {
|
|
LOGI(" SHR file is not %d bytes long!", kExtTotalSize);
|
|
return retval;
|
|
}
|
|
|
|
ASSERT(sizeof(fScreen.pixels) + sizeof(fExtColorTable) == kExtTotalSize);
|
|
|
|
ASSERT(sizeof(fScreen.pixels) == 32000);
|
|
ASSERT(sizeof(fExtColorTable) == 32 * 200);
|
|
|
|
memcpy(fScreen.pixels, pHolder->GetSourceBuf(part), sizeof(fScreen.pixels));
|
|
//memcpy(fExtColorTable, (*ppBuf) + sizeof(fScreen.pixels),
|
|
// sizeof(fExtColorTable));
|
|
|
|
/* "Brooks" format color tables are stored in reverse order */
|
|
const uint16_t* pSrcTable = (const uint16_t*)
|
|
(pHolder->GetSourceBuf(part) + sizeof(fScreen.pixels));
|
|
uint16_t* pDstTable = (uint16_t*) fExtColorTable;
|
|
for (int table = 0; table < kExtNumColorTables; table++) {
|
|
int entry;
|
|
for (entry = 0; entry < kNumEntriesPerColorTable; entry++) {
|
|
pDstTable[(kNumEntriesPerColorTable-1) - entry] = *pSrcTable++;
|
|
}
|
|
pDstTable += kNumEntriesPerColorTable;
|
|
}
|
|
|
|
pDib = SHR3200ToBitmap24();
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
SetResultBuffer(pOutput, pDib);
|
|
retval = 0;
|
|
|
|
bail:
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Convert a SHRScreen struct to a 24-bit DIB, using a different
|
|
* color palette for each line.
|
|
*
|
|
* This is shared among the 3200-color SHR formats.
|
|
*/
|
|
MyDIBitmap* Reformat3200SHR::SHR3200ToBitmap24(void)
|
|
{
|
|
MyDIBitmap* pDib = new MyDIBitmap;
|
|
RGBTRIPLE colorLookup[kExtNumColorTables][kNumEntriesPerColorTable];
|
|
RGBTRIPLE* rgbBuf;
|
|
const uint8_t* pPixels = fScreen.pixels;
|
|
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
/*
|
|
* Set up a DIB to hold the data.
|
|
*/
|
|
rgbBuf = (RGBTRIPLE*) pDib->Create(kOutputWidth, kOutputHeight, 24, 0);
|
|
if (rgbBuf == NULL) {
|
|
delete pDib;
|
|
pDib = NULL;
|
|
goto bail;
|
|
}
|
|
|
|
/*
|
|
* Convert color palette data to RGBTRIPLE form.
|
|
*/
|
|
const uint16_t* pClrTable;
|
|
pClrTable = (const uint16_t*) fExtColorTable;
|
|
for (int table = 0; table < kExtNumColorTables; table++) {
|
|
for (int entry = 0; entry < kNumEntriesPerColorTable; entry++) {
|
|
GSColor(*pClrTable++, &colorLookup[table][entry]);
|
|
//if (!table) {
|
|
// LOGI(" table %2d entry %2d value=0x%04x",
|
|
// table, entry, *pClrTable);
|
|
//}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the pixels in our private RGB buffer.
|
|
*/
|
|
int line;
|
|
for (line = 0; line < kNumLines; line++) {
|
|
int byteCount, pixelByte;
|
|
uint8_t pixelVal;
|
|
RGBTRIPLE rgbval;
|
|
int x = 0;
|
|
|
|
#define SetPix(x, y, rgbval) \
|
|
rgbBuf[((kOutputHeight-1) - (y)) * kOutputWidth + (x)] = rgbval
|
|
|
|
for (byteCount = 0; byteCount < kPixelBytesPerLine; byteCount++) {
|
|
pixelByte = *pPixels++;
|
|
|
|
pixelVal = (pixelByte >> 4) & 0x0f;
|
|
rgbval = colorLookup[line][pixelVal];
|
|
SetPix(x, line*2, rgbval);
|
|
SetPix(x++, line*2+1, rgbval);
|
|
SetPix(x, line*2, rgbval);
|
|
SetPix(x++, line*2+1, rgbval);
|
|
|
|
pixelVal = pixelByte & 0x0f;
|
|
rgbval = colorLookup[line][pixelVal];
|
|
SetPix(x, line*2, rgbval);
|
|
SetPix(x++, line*2+1, rgbval);
|
|
SetPix(x, line*2, rgbval);
|
|
SetPix(x++, line*2+1, rgbval);
|
|
}
|
|
#undef SetPix
|
|
|
|
ASSERT(x == kOutputWidth);
|
|
}
|
|
ASSERT(line == kOutputHeight/2);
|
|
|
|
bail:
|
|
return pDib;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* Reformat3201SHR
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*
|
|
* This *might* also be $c0/0004, but there's no file type note
|
|
* for that.
|
|
*/
|
|
void Reformat3201SHR::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
long fileType = pHolder->GetFileType();
|
|
long auxType = pHolder->GetAuxType();
|
|
//long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
|
|
const char* nameExt = pHolder->GetNameExt();
|
|
//bool relaxed;
|
|
|
|
//relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;
|
|
|
|
/* only identified by name */
|
|
if (stricmp(nameExt, ".3201") == 0)
|
|
applies = ReformatHolder::kApplicProbably;
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatSHR_3201, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
}
|
|
|
|
/*
|
|
* Convert a 3201-format packed 3200-color Super Hi-Res Image.
|
|
*/
|
|
int Reformat3201SHR::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
MyDIBitmap* pDib;
|
|
const uint8_t* srcBuf = pHolder->GetSourceBuf(part);
|
|
long srcLen = pHolder->GetSourceLen(part);
|
|
long length = srcLen;
|
|
uint8_t* tmpBuf = NULL;
|
|
int retval = -1;
|
|
|
|
if (srcLen < 16 || srcLen > kExtTotalSize) {
|
|
LOGI(" SHR3201 file funky length (%d)", srcLen);
|
|
return retval;
|
|
}
|
|
const long* pMagic = (const long*) pHolder->GetSourceBuf(part);
|
|
if (*pMagic != 0x00d0d0c1) { // "APP\0"
|
|
LOGI(" SHR3201 didn't find magic 'APP'");
|
|
return retval;
|
|
}
|
|
srcBuf += 4;
|
|
length -= 4;
|
|
|
|
ASSERT(sizeof(fScreen.pixels) == 32000);
|
|
ASSERT(sizeof(fExtColorTable) == 32 * 200);
|
|
ASSERT(sizeof(fScreen.pixels) + sizeof(fExtColorTable) == kExtTotalSize);
|
|
|
|
/* "Brooks" format color tables are stored in reverse order */
|
|
const uint16_t* pSrcTable;
|
|
uint16_t* pDstTable;
|
|
pSrcTable = (const uint16_t*) srcBuf;
|
|
pDstTable = (uint16_t*) fExtColorTable;
|
|
int table;
|
|
for (table = 0; table < kExtNumColorTables; table++) {
|
|
int entry;
|
|
for (entry = 0; entry < kNumEntriesPerColorTable; entry++) {
|
|
pDstTable[(kNumEntriesPerColorTable-1) - entry] = *pSrcTable++;
|
|
}
|
|
pDstTable += kNumEntriesPerColorTable;
|
|
}
|
|
|
|
srcBuf = (const uint8_t*) pSrcTable;
|
|
length -= srcBuf - (pHolder->GetSourceBuf(part) +4);
|
|
|
|
/* now unpack the PackBytes-format pixels */
|
|
if (UnpackBytes(fScreen.pixels, srcBuf, sizeof(fScreen.pixels), length)
|
|
!= sizeof(fScreen.pixels)) {
|
|
goto bail;
|
|
}
|
|
|
|
pDib = SHR3200ToBitmap24();
|
|
if (pDib == NULL)
|
|
goto bail;
|
|
|
|
SetResultBuffer(pOutput, pDib);
|
|
retval = 0;
|
|
|
|
bail:
|
|
delete[] tmpBuf;
|
|
return retval;
|
|
}
|