/* * 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='%ls' blockLen=%ld (dataLen=%ld) start=0x%08lx\n", (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 { WMSG1(" APFSHR (ignoring segment '%ls')\n", (LPCWSTR) 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: '%ls'\n", (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) { 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; }