diff --git a/reformat/SuperHiRes.cpp b/reformat/SuperHiRes.cpp index 3a2f8c5..1dc1d77 100644 --- a/reformat/SuperHiRes.cpp +++ b/reformat/SuperHiRes.cpp @@ -1,1225 +1,1225 @@ -/* - * 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. - */ - -/* - * Convert a SHRScreen struct to a 256-color DIB. - * - * This is a reasonably generic routine shared by SHR functions. - */ -MyDIBitmap* -ReformatSHR::SHRScreenToBitmap8(const SHRScreen* pScreen) -{ - return SHRDataToBitmap8(pScreen->pixels, pScreen->scb, - pScreen->colorTable, kPixelBytesPerLine, kNumLines, - kOutputWidth, kOutputHeight); -} - -MyDIBitmap* -ReformatSHR::SHRDataToBitmap8(const unsigned char* pPixels, - const unsigned char* pSCB, const unsigned char* pColorTable, - int pixelBytesPerLine, int numScanLines, - int outputWidth, int outputHeight) -{ - MyDIBitmap* pDib = new MyDIBitmap; - const unsigned char* pSCBStart = pSCB; // sanity check only - unsigned char* outBuf; - int line; - - if (pDib == nil) - goto bail; - - /* - * Set up a DIB to hold the data. "Create" returns a pointer to the - * pixel storage. - */ - outBuf = (unsigned char*) pDib->Create(outputWidth, outputHeight, 8, - kNumColorTables * kNumEntriesPerColorTable); - if (outBuf == nil) { - delete pDib; - pDib = nil; - goto bail; - } - - /* - * Convert color palette. - */ - const unsigned short* pClrTable; - pClrTable = (const unsigned short*) pColorTable; - int table; - for (table = 0; table < kNumColorTables; table++) { - for (int entry = 0; entry < kNumEntriesPerColorTable; entry++) { - GSColor(*pClrTable++, &fColorTables[table][entry]); - } - } - pDib->SetColorTable((RGBQUAD*)fColorTables); - - /* - WMSG0(" SHR color table 0\n"); - int ii; - for (ii = 0; ii < kNumEntriesPerColorTable; ii++) { - WMSG4(" %2d: 0x%02x %02x %02x\n", - ii, - fColorTables[0][ii].rgbRed, - fColorTables[0][ii].rgbGreen, - fColorTables[0][ii].rgbBlue); - } - */ - - /* - * Set the pixels to palette indices. - */ - for (line = 0; line < numScanLines; line++) { - bool mode640, fillMode; - int colorTableOffset; - int byteCount, pixelByte; - unsigned char pixelVal; -// RGBTRIPLE rgbval; - unsigned char colorIndex; - int x = 0; - - mode640 = (*pSCB & kSCBNumPixels) != 0; - fillMode = (*pSCB & kSCBFillMode) != 0; - colorTableOffset = (*pSCB & kSCBColorTableMask) * kNumEntriesPerColorTable; - - if (!line) { - WMSG1(" SHR line 0 mode640=%d\n", mode640); - } - if (fillMode) { - /* I doubt anyone uses this in still images */ - WMSG0(" SHR FILL MODE!!\n"); - DebugBreak(); - } - -#define SetPix(x, y, colridx) \ - outBuf[((outputHeight-1) - (y)) * outputWidth + (x)] = colridx - - if (mode640) { - /* 320 mode, one byte becomes four non-doubled pixels (1:4) */ - int actualBytesPerLine = outputWidth / 4; - ASSERT(actualBytesPerLine <= pixelBytesPerLine); - - for (byteCount = 0; byteCount < actualBytesPerLine; byteCount++) { - pixelByte = *pPixels++; - - pixelVal = (pixelByte >> 6) & 0x03; - //rgbval = fColorLookup[colorTable][pixelVal + 12]; - colorIndex = colorTableOffset + pixelVal + 12; - SetPix(x, line*2, colorIndex); - SetPix(x++, line*2+1, colorIndex); - - pixelVal = (pixelByte >> 4) & 0x03; - //rgbval = fColorLookup[colorTable][pixelVal + 8]; - colorIndex = colorTableOffset + pixelVal + 8; - SetPix(x, line*2, colorIndex); - SetPix(x++, line*2+1, colorIndex); - - pixelVal = (pixelByte >> 2) & 0x03; - //rgbval = fColorLookup[colorTable][pixelVal + 4]; - colorIndex = colorTableOffset + pixelVal + 4; - SetPix(x, line*2, colorIndex); - SetPix(x++, line*2+1, colorIndex); - - pixelVal = pixelByte & 0x03; - //rgbval = fColorLookup[colorTable][pixelVal]; - colorIndex = colorTableOffset + pixelVal; - SetPix(x, line*2, colorIndex); - SetPix(x++, line*2+1, colorIndex); - } - for ( ; byteCount < pixelBytesPerLine; byteCount++) - pPixels++; - } else { - /* 320 mode, one byte becomes two doubled pixels (1:4) */ - int actualBytesPerLine = outputWidth / 4; - ASSERT(actualBytesPerLine <= pixelBytesPerLine); - - for (byteCount = 0; byteCount < actualBytesPerLine; byteCount++) { - pixelByte = *pPixels++; - - pixelVal = (pixelByte >> 4) & 0x0f; - //rgbval = fColorLookup[colorTable][pixelVal]; - 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; - //rgbval = fColorLookup[colorTable][pixelVal]; - 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); - } - for ( ; byteCount < pixelBytesPerLine; byteCount++) - pPixels++; - } -#undef SetPix - - /* pixel bytes can be 0-7 larger because of 8-byte boundary req */ - /* pixel count is 1x or 2x the pixel bytes */ - /* output width is 1x or 2x the pixel count */ - ASSERT(x >= outputWidth && x < outputWidth + (8*4)); - - pSCB++; - } - - ASSERT(line == outputHeight/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) { - WMSG1(" SHR file is not %d bytes long!\n", kTotalSize); - goto bail; - } - - ASSERT(sizeof(SHRScreen) == kTotalSize); - - memcpy(&fScreen, pHolder->GetSourceBuf(part), sizeof(fScreen)); - - pDib = SHRScreenToBitmap8(&fScreen); - if (pDib == nil) - 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 unsigned char* srcBuf = pHolder->GetSourceBuf(part); - long srcLen = pHolder->GetSourceLen(part); - int retval = -1; - - if (pHolder->GetSourceLen(part) != kExpectedLen) { - WMSG1(" SHR file is not %d bytes long!\n", 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 == nil) - 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; - unsigned char scb[kPWOutputLines]; - unsigned char colorTable[kNumEntriesPerColorTable * kColorTableEntrySize]; - unsigned char* unpackBuf = nil; - int retval = -1; - int i, result; - - if (pHolder->GetSourceLen(part) < kMinSize) { - WMSG1(" SHR file too short!\n", 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 unsigned char[kPWOutputSize]; - if (unpackBuf == nil) - 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 != 0) { - WMSG0("WARNING: UnpackBytes wasn't happy\n"); - -#if 0 - /* thd282.shk (rev76.2) has a large collection of these */ - if (UnpackBytes((unsigned char*) &fScreen, - pHolder->GetSourceBuf(part), - kTotalSize, - pHolder->GetSourceLen(part)) == 0) - { - pDib = SHRScreenToBitmap8(&fScreen); - if (pDib == nil) - 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 == nil) - 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) { - WMSG1(" SHR file too short!\n", pHolder->GetSourceLen(part)); - goto bail; - } - - ASSERT(sizeof(SHRScreen) == kTotalSize); - - if (UnpackBytes((unsigned char*) &fScreen, - pHolder->GetSourceBuf(part), kTotalSize, - pHolder->GetSourceLen(part)) != 0) - { - WMSG0(" SHR UnpackBytes failed\n"); - goto bail; - } - - pDib = SHRScreenToBitmap8(&fScreen); - if (pDib == nil) - 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 unsigned char* srcPtr = pHolder->GetSourceBuf(part); - long srcLen = pHolder->GetSourceLen(part); - unsigned char multiPal[kNumLines * - kNumEntriesPerColorTable * kColorTableEntrySize]; - bool is3200 = false; - bool haveGraphics = false; - int retval = -1; - - if (srcLen < 4) { - WMSG1(" SHR file too short!\n", srcLen); - goto bail; - } - - ASSERT(sizeof(SHRScreen) == kTotalSize); - - while (srcLen > 0) { - CString blockName; - const unsigned char* nextBlock; - long blockLen, dataLen, nextLen; - int nameLen; - - blockLen = Read32(&srcPtr, &srcLen); - if (blockLen > srcLen + 4) { - WMSG2(" APFSHR WARNING: found blockLen=%ld, remaining len=%ld\n", - blockLen, srcLen); - break; - //goto bail; - } - nextBlock = srcPtr + (blockLen-4); - nextLen = srcLen - (blockLen-4); - - nameLen = ::GetPascalString((const char*)srcPtr, srcLen, &blockName); - if (nameLen < 0) { - WMSG0(" APFSHR failed getting pascal name, bailing\n"); - goto bail; - } - - srcPtr += nameLen+1; - srcLen -= nameLen+1; - - dataLen = blockLen - (nameLen+1 + 4); - - WMSG4(" APFSHR block='%s' blockLen=%ld (dataLen=%ld) start=0x%08lx\n", - (LPCSTR) 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 { - WMSG1(" APFSHR (ignoring segment '%s')\n", blockName); - } - - srcPtr = nextBlock; - srcLen = nextLen; - } - - if (!haveGraphics) - goto bail; - - if (is3200) { - 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 == nil) - 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 unsigned char* srcPtr, long srcLen) -{ - const int kColorTableSize = kNumEntriesPerColorTable * kColorTableEntrySize; - unsigned short masterMode; - int numColorTables; - int* packedDataLen = nil; - int retval = -1; - - if (srcLen < 256) { - /* can't possibly be this small */ - WMSG1(" APFSHR unlikely srcLen %d\n", srcLen); - goto bail; - } - - masterMode = Read16(&srcPtr, &srcLen); - fPixelsPerScanLine = Read16(&srcPtr, &srcLen); - numColorTables = Read16(&srcPtr, &srcLen); - if (fPixelsPerScanLine < 8 || fPixelsPerScanLine > kMaxPixelsPerScan || - (fPixelsPerScanLine & 0x01) != 0) - { - WMSG1(" APFSHR unsupported pixelsPerScanLine %d\n", - fPixelsPerScanLine); - goto bail; - } - - WMSG3(" APFSHR masterMode=0x%04x, ppsl=%d, nct=%d\n", - masterMode, fPixelsPerScanLine, numColorTables); - if (numColorTables <= 0 || numColorTables > kNumColorTables) { - WMSG1(" APFSHR unexpected numColorTables %d\n", numColorTables); - goto bail; - } - - /* - * Load the color tables. - */ - memset(fScreen.colorTable, 0, sizeof(fScreen.colorTable)); - int i; - for (i = 0; i < numColorTables; i++) { - if (srcLen < kColorTableSize) { - WMSG0(" APFSHR ran out while copying color tables\n"); - 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 < 8 || fNumScanLines > kMaxScanLines) { - WMSG1(" APFSHR unsupported numScanLines %d\n", fNumScanLines); - goto bail; - } - if ((fPixelsPerScanLine == 320 || fPixelsPerScanLine == 640) && - fNumScanLines == kNumLines) - { - /* standard-sized image, use fScreen */ - ASSERT(!fNonStandard); - WMSG0(" Assuming this is a standard, full-width SHR image\n"); - 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; - WMSG3(" NOTE: non-standard image size %dx%d, 2-bit-mode=%d\n", - 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" - } - /* - * We want to create our storage such that the bytes per line is evenly - * divisible by 8. I have no idea why I'm doing this, but at this - * point I'm afraid to change it. Something with PackBytes? The APF - * filetype note doesn't say anything. It doesn't matter to the - * bitmap code, because we're feeding it in a pixel at a time, and any - * pitch requirememts are concealed within MyDIBitmap. - * - * Figure this out someday. - */ - fPixelBytesPerLine = ((fPixelBytesPerLine + 7) / 8) * 8; - fPixelStore = new unsigned char[fPixelBytesPerLine * fNumScanLines]; - if (fPixelStore == nil) { - WMSG1(" APFSHR ERROR: alloc of %d bytes fPixelStore failed\n", - fPixelBytesPerLine * fNumScanLines); - goto bail; - } - fSCBStore = new unsigned char[fNumScanLines]; - if (fSCBStore == nil) { - WMSG1(" APFSHR ERROR: alloc of %d bytes fSCBStore failed\n", - fNumScanLines); - goto bail; - } - } - WMSG3(" APFSHR numScanLines=%d, outputWidth=%d, pixelBytesPerLine=%d\n", - fNumScanLines, fOutputWidth, fPixelBytesPerLine); - - /* - * Get the per-scanline data. - */ - packedDataLen = new int[fNumScanLines]; - if (packedDataLen == nil) - goto bail; - for (i = 0; i < fNumScanLines; i++) { - unsigned short mode; - - packedDataLen[i] = Read16(&srcPtr, &srcLen); - if (packedDataLen[i] > fPixelsPerScanLine) { - /* each pixel is 2 or 4 bits, so this is a 2-4x expansion */ - WMSG2(" APFSHR got funky packed len %d for line %d\n", - packedDataLen, i); - goto bail; - } - - mode = Read16(&srcPtr, &srcLen); - if (mode >> 8 == 0) - fSCBStore[i] = (unsigned char)mode; - else { - WMSG2(" APFSHR odd mode 0x%04x on line %d\n", mode, i); - } - } - - /* - * Unpack the scan lines. - */ - for (i = 0; i < fNumScanLines; i++) { - if (srcLen <= 0) { - WMSG0(" APFSHR ran out of data while unpacking pixels\n"); - goto bail; - } - if (UnpackBytes(&fPixelStore[i * fPixelBytesPerLine], - srcPtr, fPixelBytesPerLine, packedDataLen[i]) != 0) - { - WMSG1(" APFSHR UnpackBytes failed on line %d\n", i); - goto bail; - } - - 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(unsigned char* dstPtr, - const unsigned char* 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) { - WMSG1(" APFSHR got too-small multipal size %ld\n", srcLen); - return -1; - } else if (srcLen > kMultipalSize+2) { - WMSG2(" APFSHR WARNING: oversized multipal (%ld, expected %ld)\n", - 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 */ - WMSG1(" APFSHR ignoring multipal with %d color tables\n", - numColorTables); - return -1; - } - - if (1) { - memcpy(dstPtr, srcPtr, kMultipalSize); - } else { -#if 0 - /* swap entries */ - const unsigned short* pSrcTable; - unsigned short* pDstTable = (unsigned short*) dst; - int table, entry; - for (table = 0; table < kNumLines; table++) { - pSrcTable = (const unsigned short*)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 unsigned char* srcPtr, long srcLen) -{ - int numChars; - - numChars = Read16(&srcPtr, &srcLen); - - if (numChars != srcLen) { - WMSG2(" APFSHR note chunk has numChars=%d but dataLen=%ld, bailing\n", - numChars, srcLen); - return; - } - - CString str; - while (srcLen--) - str += *srcPtr++; - - WMSG1(" APFSHR note: '%s'\n", 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) { - WMSG1(" SHR file is not %d bytes long!\n", 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 unsigned short* pSrcTable = (const unsigned short*) - (pHolder->GetSourceBuf(part) + sizeof(fScreen.pixels)); - unsigned short* pDstTable = (unsigned short*) fExtColorTable; - int table; - for (table = 0; table < kExtNumColorTables; table++) { - int entry; - for (entry = 0; entry < kNumEntriesPerColorTable; entry++) { - pDstTable[(kNumEntriesPerColorTable-1) - entry] = *pSrcTable++; - } - pDstTable += kNumEntriesPerColorTable; - } - - pDib = SHR3200ToBitmap24(); - if (pDib == nil) - 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 unsigned char* pPixels = fScreen.pixels; - - if (pDib == nil) - goto bail; - - /* - * Set up a DIB to hold the data. - */ - rgbBuf = (RGBTRIPLE*) pDib->Create(kOutputWidth, kOutputHeight, 24, 0); - if (rgbBuf == nil) { - delete pDib; - pDib = nil; - goto bail; - } - - /* - * Convert color palette data to RGBTRIPLE form. - */ - const unsigned short* pClrTable; - pClrTable = (const unsigned short*) fExtColorTable; - int table; - for (table = 0; table < kExtNumColorTables; table++) { - for (int entry = 0; entry < kNumEntriesPerColorTable; entry++) { - GSColor(*pClrTable++, &colorLookup[table][entry]); - //if (!table) { - // WMSG3(" table %2d entry %2d value=0x%04x\n", - // table, entry, *pClrTable); - //} - } - } - - /* - * Set the pixels in our private RGB buffer. - */ - int line; - for (line = 0; line < kNumLines; line++) { - int byteCount, pixelByte; - unsigned char 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 unsigned char* srcBuf = pHolder->GetSourceBuf(part); - long srcLen = pHolder->GetSourceLen(part); - long length = srcLen; - unsigned char* tmpBuf = nil; - int retval = -1; - - if (srcLen < 16 || srcLen > kExtTotalSize) { - WMSG1(" SHR3201 file funky length (%d)\n", srcLen); - return retval; - } - const long* pMagic = (const long*) pHolder->GetSourceBuf(part); - if (*pMagic != 0x00d0d0c1) { // "APP\0" - WMSG0(" SHR3201 didn't find magic 'APP'\n"); - 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 unsigned short* pSrcTable; - unsigned short* pDstTable; - pSrcTable = (const unsigned short*) srcBuf; - pDstTable = (unsigned short*) 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 unsigned char*) pSrcTable; - length -= srcBuf - (pHolder->GetSourceBuf(part) +4); - - /* now unpack the PackBytes-format pixels */ - if (UnpackBytes(fScreen.pixels, srcBuf, sizeof(fScreen.pixels), length) != 0) - goto bail; - - pDib = SHR3200ToBitmap24(); - if (pDib == nil) - goto bail; - - SetResultBuffer(pOutput, pDib); - retval = 0; - -bail: - delete[] tmpBuf; - return retval; -} +/* + * 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. + */ + +/* + * Convert a SHRScreen struct to a 256-color DIB. + * + * This is a reasonably generic routine shared by SHR functions. + */ +MyDIBitmap* +ReformatSHR::SHRScreenToBitmap8(const SHRScreen* pScreen) +{ + return SHRDataToBitmap8(pScreen->pixels, pScreen->scb, + pScreen->colorTable, kPixelBytesPerLine, kNumLines, + kOutputWidth, kOutputHeight); +} + +MyDIBitmap* +ReformatSHR::SHRDataToBitmap8(const unsigned char* pPixels, + const unsigned char* pSCB, const unsigned char* pColorTable, + int pixelBytesPerLine, int numScanLines, + int outputWidth, int outputHeight) +{ + MyDIBitmap* pDib = new MyDIBitmap; + const unsigned char* pSCBStart = pSCB; // sanity check only + unsigned char* outBuf; + int line; + + if (pDib == nil) + goto bail; + + /* + * Set up a DIB to hold the data. "Create" returns a pointer to the + * pixel storage. + */ + outBuf = (unsigned char*) pDib->Create(outputWidth, outputHeight, 8, + kNumColorTables * kNumEntriesPerColorTable); + if (outBuf == nil) { + delete pDib; + pDib = nil; + goto bail; + } + + /* + * Convert color palette. + */ + const unsigned short* pClrTable; + pClrTable = (const unsigned short*) pColorTable; + int table; + for (table = 0; table < kNumColorTables; table++) { + for (int entry = 0; entry < kNumEntriesPerColorTable; entry++) { + GSColor(*pClrTable++, &fColorTables[table][entry]); + } + } + pDib->SetColorTable((RGBQUAD*)fColorTables); + + /* + WMSG0(" SHR color table 0\n"); + int ii; + for (ii = 0; ii < kNumEntriesPerColorTable; ii++) { + WMSG4(" %2d: 0x%02x %02x %02x\n", + ii, + fColorTables[0][ii].rgbRed, + fColorTables[0][ii].rgbGreen, + fColorTables[0][ii].rgbBlue); + } + */ + + /* + * Set the pixels to palette indices. + */ + for (line = 0; line < numScanLines; line++) { + bool mode640, fillMode; + int colorTableOffset; + int byteCount, pixelByte; + unsigned char pixelVal; +// RGBTRIPLE rgbval; + unsigned char colorIndex; + int x = 0; + + mode640 = (*pSCB & kSCBNumPixels) != 0; + fillMode = (*pSCB & kSCBFillMode) != 0; + colorTableOffset = (*pSCB & kSCBColorTableMask) * kNumEntriesPerColorTable; + + if (!line) { + WMSG1(" SHR line 0 mode640=%d\n", mode640); + } + if (fillMode) { + /* I doubt anyone uses this in still images */ + WMSG0(" SHR FILL MODE!!\n"); + DebugBreak(); + } + +#define SetPix(x, y, colridx) \ + outBuf[((outputHeight-1) - (y)) * outputWidth + (x)] = colridx + + if (mode640) { + /* 320 mode, one byte becomes four non-doubled pixels (1:4) */ + int actualBytesPerLine = outputWidth / 4; + ASSERT(actualBytesPerLine <= pixelBytesPerLine); + + for (byteCount = 0; byteCount < actualBytesPerLine; byteCount++) { + pixelByte = *pPixels++; + + pixelVal = (pixelByte >> 6) & 0x03; + //rgbval = fColorLookup[colorTable][pixelVal + 12]; + colorIndex = colorTableOffset + pixelVal + 8; + SetPix(x, line*2, colorIndex); + SetPix(x++, line*2+1, colorIndex); + + pixelVal = (pixelByte >> 4) & 0x03; + //rgbval = fColorLookup[colorTable][pixelVal + 8]; + colorIndex = colorTableOffset + pixelVal + 12; + SetPix(x, line*2, colorIndex); + SetPix(x++, line*2+1, colorIndex); + + pixelVal = (pixelByte >> 2) & 0x03; + //rgbval = fColorLookup[colorTable][pixelVal + 4]; + 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); + } + for ( ; byteCount < pixelBytesPerLine; byteCount++) + pPixels++; + } else { + /* 320 mode, one byte becomes two doubled pixels (1:4) */ + int actualBytesPerLine = outputWidth / 4; + ASSERT(actualBytesPerLine <= pixelBytesPerLine); + + for (byteCount = 0; byteCount < actualBytesPerLine; byteCount++) { + pixelByte = *pPixels++; + + pixelVal = (pixelByte >> 4) & 0x0f; + //rgbval = fColorLookup[colorTable][pixelVal]; + 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; + //rgbval = fColorLookup[colorTable][pixelVal]; + 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); + } + for ( ; byteCount < pixelBytesPerLine; byteCount++) + pPixels++; + } +#undef SetPix + + /* pixel bytes can be 0-7 larger because of 8-byte boundary req */ + /* pixel count is 1x or 2x the pixel bytes */ + /* output width is 1x or 2x the pixel count */ + ASSERT(x >= outputWidth && x < outputWidth + (8*4)); + + pSCB++; + } + + ASSERT(line == outputHeight/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) { + WMSG1(" SHR file is not %d bytes long!\n", kTotalSize); + goto bail; + } + + ASSERT(sizeof(SHRScreen) == kTotalSize); + + memcpy(&fScreen, pHolder->GetSourceBuf(part), sizeof(fScreen)); + + pDib = SHRScreenToBitmap8(&fScreen); + if (pDib == nil) + 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 unsigned char* srcBuf = pHolder->GetSourceBuf(part); + long srcLen = pHolder->GetSourceLen(part); + int retval = -1; + + if (pHolder->GetSourceLen(part) != kExpectedLen) { + WMSG1(" SHR file is not %d bytes long!\n", 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 == nil) + 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; + unsigned char scb[kPWOutputLines]; + unsigned char colorTable[kNumEntriesPerColorTable * kColorTableEntrySize]; + unsigned char* unpackBuf = nil; + int retval = -1; + int i, result; + + if (pHolder->GetSourceLen(part) < kMinSize) { + WMSG1(" SHR file too short!\n", 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 unsigned char[kPWOutputSize]; + if (unpackBuf == nil) + 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 != 0) { + WMSG0("WARNING: UnpackBytes wasn't happy\n"); + +#if 0 + /* thd282.shk (rev76.2) has a large collection of these */ + if (UnpackBytes((unsigned char*) &fScreen, + pHolder->GetSourceBuf(part), + kTotalSize, + pHolder->GetSourceLen(part)) == 0) + { + pDib = SHRScreenToBitmap8(&fScreen); + if (pDib == nil) + 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 == nil) + 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) { + WMSG1(" SHR file too short!\n", pHolder->GetSourceLen(part)); + goto bail; + } + + ASSERT(sizeof(SHRScreen) == kTotalSize); + + if (UnpackBytes((unsigned char*) &fScreen, + pHolder->GetSourceBuf(part), kTotalSize, + pHolder->GetSourceLen(part)) != 0) + { + WMSG0(" SHR UnpackBytes failed\n"); + goto bail; + } + + pDib = SHRScreenToBitmap8(&fScreen); + if (pDib == nil) + 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 unsigned char* srcPtr = pHolder->GetSourceBuf(part); + long srcLen = pHolder->GetSourceLen(part); + unsigned char multiPal[kNumLines * + kNumEntriesPerColorTable * kColorTableEntrySize]; + bool is3200 = false; + bool haveGraphics = false; + int retval = -1; + + if (srcLen < 4) { + WMSG1(" SHR file too short!\n", srcLen); + goto bail; + } + + ASSERT(sizeof(SHRScreen) == kTotalSize); + + while (srcLen > 0) { + CString blockName; + const unsigned char* nextBlock; + long blockLen, dataLen, nextLen; + int nameLen; + + blockLen = Read32(&srcPtr, &srcLen); + if (blockLen > srcLen + 4) { + WMSG2(" APFSHR WARNING: found blockLen=%ld, remaining len=%ld\n", + blockLen, srcLen); + break; + //goto bail; + } + nextBlock = srcPtr + (blockLen-4); + nextLen = srcLen - (blockLen-4); + + nameLen = ::GetPascalString((const char*)srcPtr, srcLen, &blockName); + if (nameLen < 0) { + WMSG0(" APFSHR failed getting pascal name, bailing\n"); + goto bail; + } + + srcPtr += nameLen+1; + srcLen -= nameLen+1; + + dataLen = blockLen - (nameLen+1 + 4); + + WMSG4(" APFSHR block='%s' blockLen=%ld (dataLen=%ld) start=0x%08lx\n", + (LPCSTR) 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 { + WMSG1(" APFSHR (ignoring segment '%s')\n", blockName); + } + + srcPtr = nextBlock; + srcLen = nextLen; + } + + if (!haveGraphics) + goto bail; + + if (is3200) { + 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 == nil) + 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 unsigned char* srcPtr, long srcLen) +{ + const int kColorTableSize = kNumEntriesPerColorTable * kColorTableEntrySize; + unsigned short masterMode; + int numColorTables; + int* packedDataLen = nil; + int retval = -1; + + if (srcLen < 256) { + /* can't possibly be this small */ + WMSG1(" APFSHR unlikely srcLen %d\n", srcLen); + goto bail; + } + + masterMode = Read16(&srcPtr, &srcLen); + fPixelsPerScanLine = Read16(&srcPtr, &srcLen); + numColorTables = Read16(&srcPtr, &srcLen); + if (fPixelsPerScanLine < 8 || fPixelsPerScanLine > kMaxPixelsPerScan || + (fPixelsPerScanLine & 0x01) != 0) + { + WMSG1(" APFSHR unsupported pixelsPerScanLine %d\n", + fPixelsPerScanLine); + goto bail; + } + + WMSG3(" APFSHR masterMode=0x%04x, ppsl=%d, nct=%d\n", + masterMode, fPixelsPerScanLine, numColorTables); + if (numColorTables <= 0 || numColorTables > kNumColorTables) { + WMSG1(" APFSHR unexpected numColorTables %d\n", numColorTables); + goto bail; + } + + /* + * Load the color tables. + */ + memset(fScreen.colorTable, 0, sizeof(fScreen.colorTable)); + int i; + for (i = 0; i < numColorTables; i++) { + if (srcLen < kColorTableSize) { + WMSG0(" APFSHR ran out while copying color tables\n"); + 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 < 8 || fNumScanLines > kMaxScanLines) { + WMSG1(" APFSHR unsupported numScanLines %d\n", fNumScanLines); + goto bail; + } + if ((fPixelsPerScanLine == 320 || fPixelsPerScanLine == 640) && + fNumScanLines == kNumLines) + { + /* standard-sized image, use fScreen */ + ASSERT(!fNonStandard); + WMSG0(" Assuming this is a standard, full-width SHR image\n"); + 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; + WMSG3(" NOTE: non-standard image size %dx%d, 2-bit-mode=%d\n", + 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" + } + /* + * We want to create our storage such that the bytes per line is evenly + * divisible by 8. I have no idea why I'm doing this, but at this + * point I'm afraid to change it. Something with PackBytes? The APF + * filetype note doesn't say anything. It doesn't matter to the + * bitmap code, because we're feeding it in a pixel at a time, and any + * pitch requirememts are concealed within MyDIBitmap. + * + * Figure this out someday. + */ + fPixelBytesPerLine = ((fPixelBytesPerLine + 7) / 8) * 8; + fPixelStore = new unsigned char[fPixelBytesPerLine * fNumScanLines]; + if (fPixelStore == nil) { + WMSG1(" APFSHR ERROR: alloc of %d bytes fPixelStore failed\n", + fPixelBytesPerLine * fNumScanLines); + goto bail; + } + fSCBStore = new unsigned char[fNumScanLines]; + if (fSCBStore == nil) { + WMSG1(" APFSHR ERROR: alloc of %d bytes fSCBStore failed\n", + fNumScanLines); + goto bail; + } + } + WMSG3(" APFSHR numScanLines=%d, outputWidth=%d, pixelBytesPerLine=%d\n", + fNumScanLines, fOutputWidth, fPixelBytesPerLine); + + /* + * Get the per-scanline data. + */ + packedDataLen = new int[fNumScanLines]; + if (packedDataLen == nil) + goto bail; + for (i = 0; i < fNumScanLines; i++) { + unsigned short mode; + + packedDataLen[i] = Read16(&srcPtr, &srcLen); + if (packedDataLen[i] > fPixelsPerScanLine) { + /* each pixel is 2 or 4 bits, so this is a 2-4x expansion */ + WMSG2(" APFSHR got funky packed len %d for line %d\n", + packedDataLen, i); + goto bail; + } + + mode = Read16(&srcPtr, &srcLen); + if (mode >> 8 == 0) + fSCBStore[i] = (unsigned char)mode; + else { + WMSG2(" APFSHR odd mode 0x%04x on line %d\n", mode, i); + } + } + + /* + * Unpack the scan lines. + */ + for (i = 0; i < fNumScanLines; i++) { + if (srcLen <= 0) { + WMSG0(" APFSHR ran out of data while unpacking pixels\n"); + goto bail; + } + if (UnpackBytes(&fPixelStore[i * fPixelBytesPerLine], + srcPtr, fPixelBytesPerLine, packedDataLen[i]) != 0) + { + WMSG1(" APFSHR UnpackBytes failed on line %d\n", i); + goto bail; + } + + 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(unsigned char* dstPtr, + const unsigned char* 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) { + WMSG1(" APFSHR got too-small multipal size %ld\n", srcLen); + return -1; + } else if (srcLen > kMultipalSize+2) { + WMSG2(" APFSHR WARNING: oversized multipal (%ld, expected %ld)\n", + 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 */ + WMSG1(" APFSHR ignoring multipal with %d color tables\n", + numColorTables); + return -1; + } + + if (1) { + memcpy(dstPtr, srcPtr, kMultipalSize); + } else { +#if 0 + /* swap entries */ + const unsigned short* pSrcTable; + unsigned short* pDstTable = (unsigned short*) dst; + int table, entry; + for (table = 0; table < kNumLines; table++) { + pSrcTable = (const unsigned short*)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 unsigned char* srcPtr, long srcLen) +{ + int numChars; + + numChars = Read16(&srcPtr, &srcLen); + + if (numChars != srcLen) { + WMSG2(" APFSHR note chunk has numChars=%d but dataLen=%ld, bailing\n", + numChars, srcLen); + return; + } + + CString str; + while (srcLen--) + str += *srcPtr++; + + WMSG1(" APFSHR note: '%s'\n", 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) { + WMSG1(" SHR file is not %d bytes long!\n", 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 unsigned short* pSrcTable = (const unsigned short*) + (pHolder->GetSourceBuf(part) + sizeof(fScreen.pixels)); + unsigned short* pDstTable = (unsigned short*) fExtColorTable; + int table; + for (table = 0; table < kExtNumColorTables; table++) { + int entry; + for (entry = 0; entry < kNumEntriesPerColorTable; entry++) { + pDstTable[(kNumEntriesPerColorTable-1) - entry] = *pSrcTable++; + } + pDstTable += kNumEntriesPerColorTable; + } + + pDib = SHR3200ToBitmap24(); + if (pDib == nil) + 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 unsigned char* pPixels = fScreen.pixels; + + if (pDib == nil) + goto bail; + + /* + * Set up a DIB to hold the data. + */ + rgbBuf = (RGBTRIPLE*) pDib->Create(kOutputWidth, kOutputHeight, 24, 0); + if (rgbBuf == nil) { + delete pDib; + pDib = nil; + goto bail; + } + + /* + * Convert color palette data to RGBTRIPLE form. + */ + const unsigned short* pClrTable; + pClrTable = (const unsigned short*) fExtColorTable; + int table; + for (table = 0; table < kExtNumColorTables; table++) { + for (int entry = 0; entry < kNumEntriesPerColorTable; entry++) { + GSColor(*pClrTable++, &colorLookup[table][entry]); + //if (!table) { + // WMSG3(" table %2d entry %2d value=0x%04x\n", + // table, entry, *pClrTable); + //} + } + } + + /* + * Set the pixels in our private RGB buffer. + */ + int line; + for (line = 0; line < kNumLines; line++) { + int byteCount, pixelByte; + unsigned char 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 unsigned char* srcBuf = pHolder->GetSourceBuf(part); + long srcLen = pHolder->GetSourceLen(part); + long length = srcLen; + unsigned char* tmpBuf = nil; + int retval = -1; + + if (srcLen < 16 || srcLen > kExtTotalSize) { + WMSG1(" SHR3201 file funky length (%d)\n", srcLen); + return retval; + } + const long* pMagic = (const long*) pHolder->GetSourceBuf(part); + if (*pMagic != 0x00d0d0c1) { // "APP\0" + WMSG0(" SHR3201 didn't find magic 'APP'\n"); + 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 unsigned short* pSrcTable; + unsigned short* pDstTable; + pSrcTable = (const unsigned short*) srcBuf; + pDstTable = (unsigned short*) 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 unsigned char*) pSrcTable; + length -= srcBuf - (pHolder->GetSourceBuf(part) +4); + + /* now unpack the PackBytes-format pixels */ + if (UnpackBytes(fScreen.pixels, srcBuf, sizeof(fScreen.pixels), length) != 0) + goto bail; + + pDib = SHR3200ToBitmap24(); + if (pDib == nil) + goto bail; + + SetResultBuffer(pOutput, pDib); + retval = 0; + +bail: + delete[] tmpBuf; + return retval; +}