/* * CiderPress * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. * See the file LICENSE for distribution terms. */ /* * Reformatter base class implementation. */ #include "StdAfx.h" #include "ReformatBase.h" #include /* * ========================================================================== * ReformatText * ========================================================================== */ /* * Set the output format and buffer. * * Clears our work buffer pointer so we don't free it. */ void ReformatText::SetResultBuffer(ReformatOutput* pOutput, bool multiFont) { char* buf; long len; fExpBuf.SeizeBuffer(&buf, &len); pOutput->SetTextBuf(buf, len, true); if (pOutput->GetTextBuf() == NULL) { /* * Force "raw" mode if there's no output. This can happen if we, * say, try to format an empty file as a hex dump. We never * produce any output, so no buffer gets allocated. * * We set the mode to "raw" so that applications can assume that * results of type "text" actually have text to look at -- though * it's possible the length will be zero, we promise that there'll * be a buffer there. I'm not sure it's important to do this, * but it does reduce the #of situations in which we have to * worry about NULL pointers. */ pOutput->SetOutputKind(ReformatOutput::kOutputRaw); LOGI("ReformatText returning a null pointer"); } else { if (fUseRTF) pOutput->SetOutputKind(ReformatOutput::kOutputRTF); else pOutput->SetOutputKind(ReformatOutput::kOutputText); } if (fUseRTF && multiFont) pOutput->SetMultipleFontsFlag(true); } /* * Output the RTF header. * * The color table is the standard MS Word color table, except that entry * #17 (dark grey) has been lightened from (51,51,51) because it's nearly * indistinguishable from black on the screen. * * The default font is Courier New (\f0) at 10 points (\fs20). */ void ReformatText::RTFBegin(int flags) { // static const char* rtfHdr = //"{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0 Courier New;}}\r\n" //"\\viewkind4\\uc1\\pard\\f0\\fs20 "; static const char* rtfHdrStart = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033\\deflangfe1033{\\fonttbl" "{\\f0\\fmodern\\fprq1\\fcharset0 Courier New;}" "{\\f1\\froman\\fprq2\\fcharset0 Times New Roman;}" "{\\f2\\fswiss\\fprq2\\fcharset0 Arial;}" "{\\f3\\froman\\fprq2\\fcharset2 Symbol;}" "}\r\n"; static const char* rtfColorTable = "{\\colortbl;" "\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green255\\blue255;\\red0\\green255\\blue0;" "\\red255\\green0\\blue255;\\red255\\green0\\blue0;\\red255\\green255\\blue0;\\red255\\green255\\blue255;" "\\red0\\green0\\blue128;\\red0\\green128\\blue128;\\red0\\green128\\blue0;\r\n" "\\red128\\green0\\blue128;\\red128\\green0\\blue0;\\red128\\green128\\blue0;\\red128\\green128\\blue128;" "\\red192\\green192\\blue192;\\red64\\green64\\blue64;\\red255\\green153\\blue0;}\r\n"; static const char* rtfHdrEnd = "\\viewkind4\\uc1\\pard\\f0\\fs20 "; if (fUseRTF) { BufPrintf("%s", rtfHdrStart); if ((flags & kRTFFlagColorTable) != 0) BufPrintf("%s", rtfColorTable); BufPrintf("%s", rtfHdrEnd); } fPointSize = 10; } /* * Output the RTF footer. */ void ReformatText::RTFEnd(void) { if (fUseRTF) BufPrintf("}\r\n%c", '\0'); } /* * Output RTF paragraph definition marker. Do this every time we change some * aspect of paragraph formatting, such as margins or justification. */ void ReformatText::RTFSetPara(void) { if (!fUseRTF) return; BufPrintf("\\pard\\nowidctlpar"); if (fLeftMargin != 0 || fRightMargin != 0) { /* looks like RTF thinks we're getting 12 chars per inch? */ if (fLeftMargin != 0) BufPrintf("\\li%d", (int) (fLeftMargin * (kRTFUnitsPerInch/12))); if (fLeftMargin != 0) BufPrintf("\\ri%d", (int) (fRightMargin * (kRTFUnitsPerInch/12))); } switch (fJustified) { case kJustifyLeft: break; case kJustifyRight: BufPrintf("\\qr"); break; case kJustifyCenter: BufPrintf("\\qc"); break; case kJustifyFull: BufPrintf("\\qj"); break; default: assert(false); break; } // Ideally we'd suppress this if the next thing is an RTF // formatting command, esp. "\\par". BufPrintf(" "); } /* * Output a new paragraph marker. * * If you're producing RTF output, this is the right way to output an * end-of-line character. */ void ReformatText::RTFNewPara(void) { if (fUseRTF) BufPrintf("\\par\r\n"); else BufPrintf("\r\n"); } /* * Insert a page break. This isn't supported by the Rich Edit control, * so it won't appear in CiderPress or WordPad, but it will come out in * Microsoft Word if you extract to a file. */ void ReformatText::RTFPageBreak(void) { if (fUseRTF) BufPrintf("\\page "); } /* * RTF tab character. */ void ReformatText::RTFTab(void) { if (fUseRTF) BufPrintf("\\tab "); } /* * Minor formatting. */ void ReformatText::RTFBoldOn(void) { if (fBoldEnabled) return; if (fUseRTF) { BufPrintf("\\b "); fBoldEnabled = true; } } void ReformatText::RTFBoldOff(void) { if (!fBoldEnabled) return; if (fUseRTF) { BufPrintf("\\b0 "); fBoldEnabled = false; } } void ReformatText::RTFItalicOn(void) { if (fItalicEnabled) return; if (fUseRTF) { BufPrintf("\\i "); fItalicEnabled = true; } } void ReformatText::RTFItalicOff(void) { if (!fItalicEnabled) return; if (fUseRTF) { BufPrintf("\\i0 "); fItalicEnabled = false; } } void ReformatText::RTFUnderlineOn(void) { if (fUnderlineEnabled) return; if (fUseRTF) { BufPrintf("\\ul "); fUnderlineEnabled = true; } } void ReformatText::RTFUnderlineOff(void) { if (!fUnderlineEnabled) return; if (fUseRTF) { BufPrintf("\\ulnone "); fUnderlineEnabled = false; } } void ReformatText::RTFInverseOn(void) { if (fInverseEnabled) return; if (fUseRTF) { RTFSetColor(TextColor::kColorWhite); BufPrintf("\\highlight1 "); // black fInverseEnabled = true; } } void ReformatText::RTFInverseOff(void) { if (!fInverseEnabled) return; if (fUseRTF) { RTFSetColor(TextColor::kColorNone); BufPrintf("\\highlight0 "); fInverseEnabled = false; } } void ReformatText::RTFOutlineOn(void) { if (fOutlineEnabled) return; if (fUseRTF) { BufPrintf("\\outl "); fOutlineEnabled = true; } } void ReformatText::RTFOutlineOff(void) { if (!fOutlineEnabled) return; if (fUseRTF) { BufPrintf("\\outl0 "); fOutlineEnabled = false; } } void ReformatText::RTFShadowOn(void) { if (fShadowEnabled) return; if (fUseRTF) { BufPrintf("\\shad "); fShadowEnabled = true; } } void ReformatText::RTFShadowOff(void) { if (!fShadowEnabled) return; if (fUseRTF) { BufPrintf("\\shad0 "); fShadowEnabled = false; } } void ReformatText::RTFSubscriptOn(void) { if (fSubscriptEnabled) return; if (fUseRTF) { BufPrintf("\\sub "); fSubscriptEnabled = true; } } void ReformatText::RTFSubscriptOff(void) { if (!fSubscriptEnabled) return; if (fUseRTF) { BufPrintf("\\nosupersub "); fSubscriptEnabled = false; } } void ReformatText::RTFSuperscriptOn(void) { if (fSuperscriptEnabled) return; if (fUseRTF) { BufPrintf("\\super "); fSuperscriptEnabled = true; } } void ReformatText::RTFSuperscriptOff(void) { if (!fSuperscriptEnabled) return; if (fUseRTF) { BufPrintf("\\nosupersub "); fSuperscriptEnabled = false; } } ReformatText::TextColor ReformatText::RTFSetColor(TextColor color) { TextColor oldColor = fTextColor; if (color != fTextColor && fUseRTF) { BufPrintf("\\cf%d ", color); fTextColor = color; } return oldColor; } /* * Change paragraph formatting. */ void ReformatText::RTFParaLeft(void) { if (fJustified != kJustifyLeft) { fJustified = kJustifyLeft; RTFSetPara(); } } void ReformatText::RTFParaRight(void) { if (fJustified != kJustifyRight) { fJustified = kJustifyRight; RTFSetPara(); } } void ReformatText::RTFParaCenter(void) { if (fJustified != kJustifyCenter) { fJustified = kJustifyCenter; RTFSetPara(); } } void ReformatText::RTFParaJustify(void) { if (fJustified != kJustifyFull) { fJustified = kJustifyFull; RTFSetPara(); } } /* * Page margins, in 1/10th inches. */ void ReformatText::RTFLeftMargin(int margin) { //LOGI("+++ Left margin now %d", margin); fLeftMargin = margin; RTFSetPara(); } void ReformatText::RTFRightMargin(int margin) { //LOGI("+++ Right margin now %d", margin); fRightMargin = margin; RTFSetPara(); } /* * Switch to a different font size. */ void ReformatText::RTFSetFontSize(int points) { if (fUseRTF && fPointSize != points) BufPrintf("\\fs%d ", points * 2); fPointSize = points; } /* * Switch to a different font. */ void ReformatText::RTFSetFont(RTFFont font) { if (fUseRTF) BufPrintf("\\f%d ", font); } /* * Set the font by specifying a IIgs QuickDraw II font family number. */ void ReformatText::RTFSetGSFont(uint16_t family) { float newMult; if (!fUseRTF) return; /* * Apple II fonts seem to be about 1.5x in a WYSIWYG way, except * for Times, which is about 1:1. */ switch (family) { case kGSFontTimes: RTFSetFont(kFontTimesRoman); newMult = 0.9f; break; case kGSFontNewYork: RTFSetFont(kFontTimesRoman); newMult = 1.1f; break; case kGSFontSymbol: RTFSetFont(kFontSymbol); newMult = 1.0f; break; case kGSFontMonaco: RTFSetFont(kFontCourierNew); newMult = 0.80f; break; case kGSFontCourier: case kGSFontPCMonospace: case kGSFontAppleM: case kGSFontGenesys: RTFSetFont(kFontCourierNew); newMult = 1.5f; break; case kGSFontClassical: case kGSFontGenoa: case kGSFontWestern: RTFSetFont(kFontArial); newMult = 0.80f; break; case kGSFontChicago: case kGSFontVenice: case kGSFontGeneva: case kGSFontStarfleet: case kGSFontUnknown1: case kGSFontUnknown2: RTFSetFont(kFontArial); newMult = 1.0f; break; case kGSFontLondon: case kGSFontAthens: case kGSFontSanFran: case kGSFontShaston: case kGSFontToronto: case kGSFontCairo: case kGSFontLosAngeles: case kGSFontHelvetica: case kGSFontTaliesin: RTFSetFont(kFontArial); newMult = 1.5f; break; default: LOGI("Unrecognized font family 0x%04x, using Arial", family); RTFSetFont(kFontArial); newMult = 1.0f; break; } if (newMult != fGSFontSizeMult) { fGSFontSizeMult = newMult; RTFSetGSFontSize(fPreMultPointSize); } } /* * Set the font size of a IIgs font. We factor the size multiplier in. * * BUG: we should track the state of the "underline" mode, and turn it * on and off based on the font size (8-point fonts aren't underlined). */ void ReformatText::RTFSetGSFontSize(int points) { RTFSetFontSize((int) roundf(points * fGSFontSizeMult)); fPreMultPointSize = points; } /* * Set bold/italic/underline etc. * * Note that "Teach" does not show underlining on text that is 8 points * or smaller. We have to emulate this behavior or some documents, such * as ModZap's "MZ.Manual", look terrible. * * Set the font size before calling here. * * Some characters, such as '=' in Shaston 8, look the same in * bold as they do in plain. This doesn't hold true for Windows * fonts, so we're going to look different in some circumstances. */ void ReformatText::RTFSetGSFontStyle(uint8_t qdStyle) { if (!fUseRTF) return; if ((qdStyle & kQDStyleBold) != 0) { RTFBoldOn(); } else { RTFBoldOff(); } if ((qdStyle & kQDStyleItalic) != 0) { RTFItalicOn(); } else { RTFItalicOff(); } if ((qdStyle & kQDStyleUnderline) != 0 && fPreMultPointSize > 8) { RTFUnderlineOn(); } else { RTFUnderlineOff(); } if ((qdStyle & kQDStyleOutline) != 0) { RTFOutlineOn(); } else { RTFOutlineOff(); } if ((qdStyle & kQDStyleShadow) != 0) { RTFShadowOn(); } else { RTFShadowOff(); } if ((qdStyle & kQDStyleSuperscript) != 0) { RTFSuperscriptOn(); } else { RTFSuperscriptOff(); } if ((qdStyle & kQDStyleSubscript) != 0) { RTFSubscriptOn(); } else { RTFSubscriptOff(); } } #if 0 void ReformatText::RTFProportionalOn(void) { if (fUseRTF) BufPrintf("\\f%d ", kFontTimesRoman); } void ReformatText::RTFProportionalOff(void) { if (fUseRTF) BufPrintf("\\f%d ", kFontCourierNew); } #endif /* * Convert the EOL markers in a buffer. The output is written to the work * buffer. The input buffer may be CR, LF, or CRLF. * * If "stripHiBits" is set, the high bit of each character is cleared before * the value is considered. */ void ReformatText::ConvertEOL(const uint8_t* srcBuf, long srcLen, bool stripHiBits) { /* Compatibility - assume we're not stripping nulls */ ConvertEOL(srcBuf, srcLen, stripHiBits, false); } /* * Convert the EOL markers in a buffer. The output is written to the work * buffer. The input buffer may be CR, LF, or CRLF. * * If "stripHiBits" is set, the high bit of each character is cleared before * the value is considered. * * If "stripNulls" is true, no null values will make it through. */ void ReformatText::ConvertEOL(const uint8_t* srcBuf, long srcLen, bool stripHiBits, bool stripNulls) { uint8_t ch; int mask; assert(!fUseRTF); // else we have to use RTFPrintChar if (stripHiBits) mask = 0x7f; else mask = 0xff; /* * Could probably speed this up by taking things a line at a time, * but this is fast enough and much more straightforward. */ while (srcLen) { ch = (*srcBuf++) & mask; srcLen--; if (ch == '\r') { /* got CR, check for CRLF */ if (srcLen != 0 && ((*srcBuf) & mask) == '\n') { srcBuf++; srcLen--; } BufPrintf("\r\n"); } else if (ch == '\n') { BufPrintf("\r\n"); } else { /* Strip out null bytes if requested */ if ((stripNulls && ch != 0x00) || !stripNulls) BufPrintf("%c", ch); } } } /* * Write a hex dump into the buffer. */ void ReformatText::BufHexDump(const uint8_t* srcBuf, long srcLen) { const uint8_t* origSrcBuf = srcBuf; char chBuf[17]; int i, remLen; ASSERT(srcBuf != NULL); ASSERT(srcLen >= 0); chBuf[16] = '\0'; while (srcLen > 0) { BufPrintf("%08lx: ", srcBuf - origSrcBuf); if (srcLen >= 16) { if (!fUseRTF) { /* the really easy (and relatively fast) way */ BufPrintf("%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x ", srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); } else { /* the fairly easy (and fairly fast) way */ RTFBoldOn(); BufPrintf("%02x %02x %02x %02x ", srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3]); RTFBoldOff(); BufPrintf("%02x %02x %02x %02x ", srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7]); RTFBoldOn(); BufPrintf("%02x %02x %02x %02x ", srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11]); RTFBoldOff(); BufPrintf("%02x %02x %02x %02x ", srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); } } else { /* the not-so-easy (and not-so-fast) way */ remLen = srcLen; for (i = 0; i < remLen; i++) { if (i == 0 || i == 8) RTFBoldOn(); else if (i == 4 || i == 12) RTFBoldOff(); BufPrintf("%02x ", srcBuf[i]); } RTFBoldOff(); for ( ; i < 16; i++) BufPrintf(" "); /* blank out the char buf, since we're only filling part in */ for (i = 0; i < 16; i++) chBuf[i] = ' '; } bool hosed = false; remLen = srcLen; if (remLen > 16) remLen = 16; int i; for (i = 0; i < remLen; i++) { chBuf[i] = PrintableChar(srcBuf[i]); if (fUseRTF && (chBuf[i] == '\\' || chBuf[i] == '{' || chBuf[i] == '}')) { hosed = true; break; } } if (!hosed) { BufPrintf(" %s", chBuf); } else { /* escaped chars in RTF mode; have to do this one the hard way */ ASSERT(fUseRTF); BufPrintf(" "); for (i = 0; i < remLen; i++) { RTFPrintChar(srcBuf[i]); } } RTFNewPara(); srcBuf += 16; srcLen -= 16; } } // Thanks to http://hoop-la.ca/apple2/docs/mousetext/unicode.html for Unicode. // Thanks to Hugh Hood for extracting the ASCII conversions from SEG.WP. static const struct { uint32_t unicode; // UTF-16 single code point or surrogate pair int8_t ascii; // 7-bit ASCII } gMouseTextConv[32] = { { 0xd83cdf4e, '@' }, // 00 U+1f34e RED APPLE { 0xd83cdf4f, '@' }, // 01 U+1f34f GREEN APPLE //{ 0x00002316, '^' }, // 02 U+2316 POSITION INDICATOR { 0x000025c4, '^' }, // 02 U+25c4 BLACK LEFT-POINTING POINTER { 0x000023f3, '&' }, // 03 U+23f3 HOURGLASS WITH FLOWING SAND { 0x00002713, '\'' }, // 04 U+2713 CHECK MARK { 0x00002705, '\'' }, // 05 U+2705 WHITE HEAVY CHECK MARK { 0x000023ce, '/' }, // 06 U+23ce RETURN SYMBOL //{ 0xd83cdf54, ':' }, // 07 U+1f354 HAMBURGER { 0x00002630, ':' }, // 07 U+2630 TRIGRAM FOR HEAVEN (actually want 4 lines, not 3) { 0x00002190, '<' }, // 08 U+2190 LEFTWARDS ARROW { 0x00002026, '_' }, // 09 U+2026 HORIZONTAL ELLIPSIS { 0x00002193, 'v' }, // 0a U+2193 DOWNWARDS ARROW { 0x00002191, '^' }, // 0b U+2191 UPWARDS ARROW { 0x00002594, '-' }, // 0c U+2594 UPPER ONE EIGHTH BLOCK { 0x000021b5, '/' }, // 0d U+21b5 DOWNWARDS ARROW WITH CORNER LEFTWARDS { 0x00002589, '$' }, // 0e U+2589 LEFT SEVEN EIGHTHS BLOCK { 0x000021e4, '{' }, // 0f U+21e4 LEFTWARDS ARROW TO BAR { 0x000021e5, '}' }, // 10 U+21e5 RIGHTWARDS ARROW TO BAR { 0x00002913, 'v' }, // 11 U+2913 DOWNWARDS ARROW TO BAR { 0x00002912, '^' }, // 12 U+2912 UPWARDS ARROW TO BAR { 0x00002500, '-' }, // 13 U+2500 BOX DRAWINGS LIGHT HORIZONTAL { 0x0000231e, 'L' }, // 14 U+231e BOTTOM LEFT CORNER { 0x00002192, '>' }, // 15 U+2192 UPWARDS ARROW TO BAR //{ 0xd83dde7e, '*' }, // 16 U+1f67e CHECKER BOARD { 0x00002591, '*' }, // 16 U+2591 LIGHT SHADE //{ 0xd83dde7f, '*' }, // 17 U+1f67f REVERSE CHECKER BOARD { 0x00002592, '*' }, // 17 U+2592 MEDIUM SHADE { 0xd83ddcc1, '[' }, // 18 U+1f4c1 FILE FOLDER { 0xd83ddcc2, ']' }, // 19 U+1f4c2 OPEN FILE FOLDER { 0x00002595, '|' }, // 1a U+2595 RIGHT ONE EIGHTH BLOCK { 0x00002666, '#' }, // 1b U+2666 BLACK DIAMOND SUIT { 0x0000203e, '=' }, // 1c U+203e OVERLINE -- wrong, want top/bottom { 0x0000256c, '#' }, // 1d U+256c BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL //{ 0xd83ddcbe, 'O' }, // 1e U+1f4be FLOPPY DISK { 0x000022a1, 'O' }, // 1e U+22a1 SQUARED DOT OPERATOR (seems better than 25a3) { 0x0000258f, '|' }, // 1f U+258f LEFT ONE EIGHTH BLOCK }; void ReformatText::MouseTextToUTF16(uint8_t mtVal, uint16_t* pLow, uint16_t* pHigh) { ASSERT(mtVal < 32); *pLow = gMouseTextConv[mtVal].unicode & 0xffff; *pHigh = gMouseTextConv[mtVal].unicode >> 16; } int8_t ReformatText::MouseTextToASCII(uint8_t mtVal) { ASSERT(mtVal < 32); return gMouseTextConv[mtVal].ascii; } /* * ========================================================================== * ReformatGraphics * ========================================================================== */ /* * Initialize the Apple II color palette, used for Hi-Res and DHR * conversions. Could also be used for lo-res mode. */ void ReformatGraphics::InitPalette(void) { ASSERT(kPaletteSize == 16); static const RGBQUAD stdPalette[kPaletteSize] = { /* blue, green, red, reserved */ { 0x00, 0x00, 0x00 }, // $0 black { 0x33, 0x00, 0xdd }, // $1 red (magenta) { 0x99, 0x00, 0x00 }, // $2 dark blue { 0xdd, 0x22, 0xdd }, // $3 purple (violet) { 0x22, 0x77, 0x00 }, // $4 dark green { 0x55, 0x55, 0x55 }, // $5 grey1 (dark) { 0xff, 0x22, 0x22 }, // $6 medium blue { 0xff, 0xaa, 0x66 }, // $7 light blue { 0x00, 0x55, 0x88 }, // $8 brown { 0x00, 0x66, 0xff }, // $9 orange { 0xaa, 0xaa, 0xaa }, // $A grey2 (light) { 0x88, 0x99, 0xff }, // $B pink { 0x00, 0xdd, 0x11 }, // $C green (a/k/a light green) { 0x00, 0xff, 0xff }, // $D yellow { 0x99, 0xff, 0x44 }, // $E aqua { 0xff, 0xff, 0xff }, // $F white }; memcpy(fPalette, stdPalette, sizeof(fPalette)); } /* * Stuff out DIB into the output fields, and set the appropriate flags. */ void ReformatGraphics::SetResultBuffer(ReformatOutput* pOutput, MyDIBitmap* pDib) { ASSERT(pOutput != NULL); ASSERT(pDib != NULL); pOutput->SetOutputKind(ReformatOutput::kOutputBitmap); pOutput->SetDIB(pDib); } /* * Unpack the Apple PackBytes format. * * Format is: * ... * * Flag values (first 6 bits of flag byte): * 00xxxxxx: (0-63) 1 to 64 bytes follow, all different * 01xxxxxx: (0-63) 1 to 64 repeats of next byte * 10xxxxxx: (0-63) 1 to 64 repeats of next 4 bytes * 11xxxxxx: (0-63) 1 to 64 repeats of next byte taken as 4 bytes * (as in 10xxxxxx case) * * Pass the destination buffer in "dst", source buffer in "src", source * length in "srcLen", and expected sizes of output in "dstRem". * * Returns the number of bytes unpacked on success, negative if the buffer is * overfilled. */ int ReformatGraphics::UnpackBytes(uint8_t* dst, const uint8_t* src, long dstRem, long srcLen) { const uint8_t* origDst = dst; while (srcLen > 0) { uint8_t flag = *src++; int count = (flag & 0x3f) +1; uint8_t val; uint8_t valSet[4]; int i; srcLen--; switch (flag & 0xc0) { case 0x00: for (i = 0; i < count; i++) { if (srcLen == 0 || dstRem == 0) { LOGI(" SHR unpack overrun1 (srcLen=%ld dstRem=%ld)", srcLen, dstRem); return -1; } *dst++ = *src++; srcLen--; dstRem--; } break; case 0x40: //if (count != 3 || count != 5 || count != 6 || count != 7) { // LOGI(" SHR unpack funky len %d?", count); //} if (srcLen == 0) { LOGI(" SHR unpack underrun2"); return -1; } val = *src++; srcLen--; for (i = 0; i < count; i++) { if (dstRem == 0) { LOGI(" SHR unpack overrun2 (srcLen=%d, i=%d of %d)", srcLen, i, count); return -1; } *dst++ = val; dstRem--; } break; case 0x80: if (srcLen < 4) { LOGI(" SHR unpack underrun3"); return -1; } valSet[0] = *src++; valSet[1] = *src++; valSet[2] = *src++; valSet[3] = *src++; srcLen -= 4; for (i = 0; i < count; i++) { if (dstRem < 4) { LOGI(" SHR unpack overrun3 (srcLen=%ld dstRem=%ld)", srcLen, dstRem); return -1; } *dst++ = valSet[0]; *dst++ = valSet[1]; *dst++ = valSet[2]; *dst++ = valSet[3]; dstRem -= 4; } break; case 0xc0: if (srcLen == 0) { LOGI(" SHR unpack underrun4"); return -1; } val = *src++; srcLen--; for (i = 0; i < count; i++) { if (dstRem < 4) { LOGI(" SHR unpack overrun4 (srcLen=%ld dstRem=%ld count=%d)", srcLen, dstRem, count); return -1; } *dst++ = val; *dst++ = val; *dst++ = val; *dst++ = val; dstRem -= 4; } break; default: ASSERT(false); break; } } ASSERT(srcLen == 0); if (false) { /* require that we completely fill the buffer */ if (dstRem != 0) { LOGI(" SHR unpack dstRem at %d", dstRem); return -1; } } return dst - origDst; } /* * Unpack Macintosh PackBits format. See Technical Note TN1023. * * Read a byte. * If the high bit is set, count is 2s complement +1 (i.e. count = (-byte)+1). * Read the next byte, then write that byte 'count' times. * If the high bit is clear, count is 1+value (i.e. count = byte+1). Read and * copy that many bytes. * After "destLen" bytes have been written, return (even if in the middle of * a run). * * NOTE: if the count byte is 0x80, Apple says it's an invalid value and * should be skipped over. Use the following byte as the count byte. This * is probably because PackBits is only supposed to crunch 127 bytes, though * that suggests 0x81 and 0x7f are also impossible. * * We have to watch for underruns on the input and overruns on the output. */ void ReformatGraphics::UnPackBits(const uint8_t** pSrcBuf, long* pSrcLen, uint8_t** pOutPtr, long dstLen, uint8_t xorVal) { const uint8_t* srcBuf = *pSrcBuf; long length = *pSrcLen; uint8_t* outPtr = *pOutPtr; int pixByte = 0; while (pixByte < dstLen && length > 0) { uint8_t countByte; int count; countByte = *srcBuf++; length--; if (countByte & 0x80) { /* RLE string */ uint8_t ch; count = (countByte ^ 0xff)+1 +1; ch = *srcBuf++; length--; while (count-- && pixByte < dstLen) { *outPtr++ = ch ^ xorVal; pixByte++; } } else { /* series of bytes */ count = countByte +1; while (count && pixByte < dstLen && length > 0) { *outPtr++ = *srcBuf++ ^ xorVal; count--; length--; pixByte++; } } } if (pixByte != 72) { /* can happen if we run out of input early */ LOGI(" MP unexpected pixByte=%d", pixByte); /* keep going */ } *pSrcBuf = srcBuf; *pSrcLen = length; *pOutPtr = outPtr; }