949 lines
22 KiB
C++
949 lines
22 KiB
C++
// BMIO.CPP Copyright (C) Microsoft Corporation 1995, All Rights reserved.
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <pshpack1.h>
|
|
|
|
#include "hccom.h"
|
|
#include "bmio.h"
|
|
#include "cread.h"
|
|
#include "skip.h"
|
|
#include "zeck.h"
|
|
#include "pack.h"
|
|
|
|
// file mode flags
|
|
|
|
#define FS_READ_WRITE 0x00 // file (FS) is readwrite
|
|
#define FS_OPEN_READ_WRITE 0x00 // file (FS) is opened in read/write mode
|
|
|
|
#define FS_READ_ONLY 0x01 // file (FS) is readonly
|
|
#define FS_OPEN_READ_ONLY 0x02 // file (FS) is opened in readonly mode
|
|
|
|
#define FS_IS_DIRECTORY 0x04 // file is really the FS directory
|
|
#define FS_DIRTY 0x08 // file (FS) is dirty and needs writing
|
|
#define FS_NO_BLOCK 0x10 // file has no associated block yet
|
|
#define FS_CDROM 0x20 // align file optimally for CDROM
|
|
|
|
// header of a read/write file block
|
|
|
|
typedef struct {
|
|
LONG lcbBlock; // block size (including header)
|
|
LONG lcbFile; // file size (not including header)
|
|
BYTE bPerms; // low byte of file permissions
|
|
} FH;
|
|
|
|
static int STDCALL LcbUncompressHb(PBYTE pbSrc, PBYTE pbDest, int cbSrc);
|
|
static RC_TYPE STDCALL RcCopyToTempFile(QRWFO qrwfo);
|
|
static BOOL STDCALL FPlungeQfshr(QFSHR qfshr);
|
|
|
|
static int cbGraphics;
|
|
|
|
void ClearCbGraphics(void)
|
|
{
|
|
cbGraphics = 0;
|
|
}
|
|
|
|
int GetCbGraphics(void)
|
|
{
|
|
return cbGraphics;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: HbmhReadHelp30Fid
|
|
|
|
PURPOSE: Read a BMP file in either Windows or OS/2 format
|
|
|
|
PARAMETERS:
|
|
pcrFile
|
|
pibmh
|
|
|
|
RETURNS: Handle to BMH structure, RGBQUAD colors, and bitmap bits
|
|
|
|
COMMENTS:
|
|
RLE compression is not supported. It should be...
|
|
|
|
MODIFICATION DATES:
|
|
13-Feb-1994 [ralphw] -- complete rewrite
|
|
12-Aug-1995 [ralphw] -- moved to hwdll
|
|
|
|
***************************************************************************/
|
|
|
|
HBMH STDCALL HbmhReadHelp30Fid(CRead* pcrFile, int* pibmh)
|
|
{
|
|
int cb = sizeof(BGH) + ((*pibmh + 1) * sizeof(DWORD));
|
|
CMem memBgh(cb);
|
|
BGH* pbgh = (BGH*) memBgh.pb;
|
|
|
|
pcrFile->seek(0);
|
|
if (pcrFile->read(pbgh, cb) != cb)
|
|
return hbmhInvalid;
|
|
|
|
ASSERT(*pibmh < pbgh->cbmhMac);
|
|
|
|
if (*pibmh == pbgh->cbmhMac - 1) {
|
|
if (*pibmh > 0) {
|
|
#if 1
|
|
cb = pcrFile->seek(0, SK_END) - pbgh->acBmh[*pibmh];
|
|
#endif
|
|
// This ASSERT happens, but which is right?
|
|
#if 0
|
|
ASSERT(pbgh->acBmh[*pibmh] - pbgh->acBmh[*pibmh - 1] ==
|
|
pcrFile->seek(0, SK_END) - pbgh->acBmh[*pibmh]);
|
|
// cb = pbgh->acBmh[*pibmh] - pbgh->acBmh[*pibmh - 1];
|
|
#endif
|
|
}
|
|
else
|
|
cb = pcrFile->seek(0, SK_END) - pbgh->acBmh[*pibmh];
|
|
}
|
|
else
|
|
cb = pbgh->acBmh[*pibmh + 1] - pbgh->acBmh[*pibmh];
|
|
|
|
CMem mem(cb);
|
|
|
|
pcrFile->seek(pbgh->acBmh[*pibmh]);
|
|
if (pcrFile->read(mem.pb, cb) != cb)
|
|
return hbmhInvalid;
|
|
|
|
if (*pibmh == 0)
|
|
*pibmh = pbgh->cbmhMac;
|
|
|
|
return HbmhExpandQv(mem.pb);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name HbmhExpandQv
|
|
-
|
|
* Purpose
|
|
* This function decompresses the bitmap from its disk format to an
|
|
* in memory bitmap header, including bitmap and hotspot data.
|
|
*
|
|
* Arguments
|
|
* A pointer to a buffer containing the bitmap data as read off disk.
|
|
*
|
|
* Returns
|
|
* A handle to the bitmap header. Returns hbmhOOM on out of memory.
|
|
* Note that this is the same as NULL. Also returns hbmhInvalid on
|
|
* invalid format. This handle is non-discardable, but the code that
|
|
* deals with qbmi->hbmh can deal with discardable blocks, so after
|
|
* initialization this can be made discardable.
|
|
*
|
|
* +++
|
|
*
|
|
* Notes
|
|
*
|
|
***************************************************************************/
|
|
|
|
HBMH STDCALL HbmhExpandQv(void* qv)
|
|
{
|
|
PBMH qbmh;
|
|
BMH bmh;
|
|
int cbBits, cbUncompressedBits;
|
|
PBYTE pDst;
|
|
|
|
PBYTE pBase = (PBYTE) qv;
|
|
bmh.bmFormat = *((PBYTE) qv);
|
|
qv = QFromQCb(qv, sizeof(BYTE));
|
|
bmh.fCompressed = *((PBYTE) qv);
|
|
qv = QFromQCb(qv, sizeof(BYTE));
|
|
|
|
switch(bmh.bmFormat) {
|
|
case bmWbitmap:
|
|
case bmDIB:
|
|
qv = QVSkipQGB(qv, (&bmh.w.dib.biXPelsPerMeter));
|
|
qv = QVSkipQGB(qv, (&bmh.w.dib.biYPelsPerMeter));
|
|
qv = QVSkipQGA(qv, (&bmh.w.dib.biPlanes));
|
|
qv = QVSkipQGA(qv, (&bmh.w.dib.biBitCount));
|
|
|
|
qv = QVSkipQGB(qv, (&bmh.w.dib.biWidth));
|
|
qv = QVSkipQGB(qv, (&bmh.w.dib.biHeight));
|
|
qv = QVSkipQGB(qv, (&bmh.w.dib.biClrUsed));
|
|
qv = QVSkipQGB(qv, (&bmh.w.dib.biClrImportant));
|
|
|
|
qv = QVSkipQGB(qv, (&bmh.cbSizeBits));
|
|
qv = QVSkipQGB(qv, (&bmh.cbSizeExtra));
|
|
|
|
bmh.cbOffsetBits = *((DWORD *) qv);
|
|
qv = (PBYTE) qv + sizeof(DWORD);
|
|
bmh.cbOffsetExtra = *((DWORD *) qv);
|
|
qv = (PBYTE) qv + sizeof(DWORD);
|
|
|
|
// Fix up constant fields in DIB structure:
|
|
|
|
bmh.w.dib.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmh.w.dib.biCompression = 0L;
|
|
bmh.w.dib.biSizeImage = 0L;
|
|
|
|
// Determine size of bitmap header plus all data
|
|
|
|
if (bmh.fCompressed)
|
|
cbBits = LAlignLong (bmh.w.dib.biWidth * bmh.w.dib.biBitCount) *
|
|
bmh.w.dib.biHeight;
|
|
else
|
|
cbBits = bmh.cbSizeBits;
|
|
|
|
qbmh = (BMH*) lcCalloc(LcbSizeQbmh(&bmh) + cbBits
|
|
+ bmh.cbSizeExtra);
|
|
|
|
// Copy header and color table:
|
|
|
|
memmove(qbmh, &bmh, sizeof(BMH) - 2 * sizeof(RGBQUAD));
|
|
memmove(qbmh->rgrgb, qv, sizeof(RGBQUAD) * (int) bmh.w.dib.biClrUsed);
|
|
|
|
// Copy bits, decompressing if necessary:
|
|
|
|
qbmh->cbOffsetBits = LcbSizeQbmh(qbmh);
|
|
if (bmh.fCompressed == BMH_COMPRESS_30) {
|
|
qbmh->cbSizeBits = LcbUncompressHb(pBase + bmh.cbOffsetBits,
|
|
(PBYTE) QFromQCb(qbmh, qbmh->cbOffsetBits), bmh.cbSizeBits);
|
|
ASSERT(qbmh->cbSizeBits <= cbBits);
|
|
}
|
|
else if (bmh.fCompressed == BMH_COMPRESS_ZECK) {
|
|
qbmh->cbSizeBits = LcbUncompressZeck(
|
|
pBase + bmh.cbOffsetBits,
|
|
(PBYTE) qbmh + qbmh->cbOffsetBits, bmh.cbSizeBits);
|
|
ASSERT(qbmh->cbSizeBits <= cbBits);
|
|
}
|
|
else
|
|
memmove(QFromQCb(qbmh, qbmh->cbOffsetBits),
|
|
pBase + bmh.cbOffsetBits,
|
|
bmh.cbSizeBits);
|
|
qbmh->fCompressed = BMH_COMPRESS_NONE; // bits are no longer compressed
|
|
|
|
// Copy extra info:
|
|
|
|
qbmh->cbOffsetExtra = qbmh->cbOffsetBits + qbmh->cbSizeBits;
|
|
if (bmh.cbSizeExtra)
|
|
memmove((PBYTE) qbmh + qbmh->cbOffsetExtra,
|
|
pBase + bmh.cbOffsetExtra, bmh.cbSizeExtra);
|
|
break;
|
|
|
|
case bmWmetafile:
|
|
qv = QVSkipQGA(qv, (&bmh.w.mf.mm));
|
|
bmh.w.mf.xExt = *(INT16 *) qv;
|
|
qv = (PBYTE) qv + sizeof(INT16);
|
|
bmh.w.mf.yExt = *(INT16 *) qv;
|
|
qv = (PBYTE) qv + sizeof(INT16);
|
|
|
|
qv = QVSkipQGB(qv, &cbUncompressedBits);
|
|
qv = QVSkipQGB(qv, (&bmh.cbSizeBits));
|
|
qv = QVSkipQGB(qv, (&bmh.cbSizeExtra));
|
|
|
|
bmh.cbOffsetBits = *((DWORD *) qv);
|
|
qv = (PBYTE) qv + sizeof(DWORD);
|
|
bmh.cbOffsetExtra = *((DWORD *) qv);
|
|
qv = (PBYTE) qv + sizeof(DWORD);
|
|
|
|
qbmh = (BMH*) lcCalloc(sizeof(BMH) + bmh.cbSizeExtra);
|
|
|
|
*qbmh = bmh;
|
|
qbmh->cbOffsetExtra = sizeof(BMH);
|
|
memmove((PBYTE) qbmh + qbmh->cbOffsetExtra,
|
|
pBase + bmh.cbOffsetExtra, bmh.cbSizeExtra);
|
|
|
|
qbmh->w.mf.hMF =
|
|
(HMETAFILE) lcMalloc(cbUncompressedBits);
|
|
if (qbmh->w.mf.hMF == NULL) {
|
|
lcFree(qbmh);
|
|
return hbmhOOM;
|
|
}
|
|
|
|
// REVIEW: 18-Sep-1993 [ralphw] -- Don't lock!
|
|
|
|
pDst = (PBYTE) qbmh->w.mf.hMF;
|
|
switch (bmh.fCompressed) {
|
|
case BMH_COMPRESS_NONE:
|
|
memmove(pDst, pBase + bmh.cbOffsetBits, bmh.cbSizeBits);
|
|
break;
|
|
case BMH_COMPRESS_30:
|
|
LcbUncompressHb(pBase + bmh.cbOffsetBits, pDst, bmh.cbSizeBits);
|
|
break;
|
|
case BMH_COMPRESS_ZECK:
|
|
LcbUncompressZeck(pBase + bmh.cbOffsetBits,
|
|
pDst, bmh.cbSizeBits);
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// Invalidate this field, as the bits are in a separate handle:
|
|
|
|
qbmh->cbOffsetBits = 0L;
|
|
|
|
qbmh->cbSizeBits = cbUncompressedBits;
|
|
qbmh->fCompressed = BMH_COMPRESS_NONE;
|
|
|
|
break;
|
|
|
|
default:
|
|
return hbmhInvalid;
|
|
}
|
|
|
|
return (HBMH) qbmh;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name LcbUncompressHb
|
|
-
|
|
* Purpose
|
|
* Decompresses the bits in pbSrc.
|
|
*
|
|
* Arguments
|
|
* pbSrc: Huge pointer to compressed bits.
|
|
* pbDest: Buffer to copy decompressed bits to.
|
|
* cbSrc: Number of bytes in pbSrc.
|
|
*
|
|
* Returns
|
|
* Number of bytes copied to pbDest. This can only be used for
|
|
* real mode error checking, as the maximum size of pbDest must
|
|
* be determined before decompression.
|
|
*
|
|
* +++
|
|
*
|
|
* Notes
|
|
*
|
|
***************************************************************************/
|
|
|
|
#define fRLEBit 0x80
|
|
const int MAX_RUN = 127;
|
|
|
|
static int STDCALL LcbUncompressHb(PBYTE pbSrc, PBYTE pbDest, int cbSrc)
|
|
{
|
|
PBYTE pbStart;
|
|
WORD cbRun;
|
|
BYTE ch;
|
|
|
|
pbStart = pbDest;
|
|
|
|
while (cbSrc-- > 0) {
|
|
cbRun = *pbSrc++;
|
|
if (cbRun & fRLEBit) {
|
|
cbRun -= fRLEBit;
|
|
cbSrc -= cbRun;
|
|
while (cbRun-- > 0)
|
|
*pbDest++ = *pbSrc++;
|
|
}
|
|
else {
|
|
ch = *pbSrc++;
|
|
while (cbRun-- > 0)
|
|
*pbDest++ = ch;
|
|
cbSrc--;
|
|
}
|
|
}
|
|
|
|
return (int) (pbDest - pbStart);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name RcWriteRgrbmh
|
|
-
|
|
* Purpose
|
|
* Writes out a set of bitmap headers into a single bitmap group.
|
|
* Can write to a DOS file and/or a FS file.
|
|
*
|
|
* Arguments
|
|
* crbmh: Number of bitmaps in bitmap array.
|
|
* rgrbmh: Array of pointers to bitmap headers.
|
|
* hf: FS file to write to (may be nil).
|
|
* fmFile: DOS file to write to (may be nil).
|
|
*
|
|
* Returns
|
|
* RC_Success if successful, rc error code otherwise. RC_Failure
|
|
* means that it actually succeeded, but that the bitmap could not
|
|
* be compressed.
|
|
*
|
|
* +++
|
|
*
|
|
*
|
|
***************************************************************************/
|
|
|
|
__inline LONG Tell(HANDLE hf) {
|
|
return _llseek((int) hf, 0, SEEK_CUR);
|
|
};
|
|
|
|
// Special class for RcWriteRgrbmh so we can use the same functions
|
|
// irregardless of whether we are dealing with a fid or hf
|
|
|
|
class CHfFid
|
|
{
|
|
public:
|
|
CHfFid(PCSTR pszFileName) {
|
|
hf = _lcreat(pszFileName, 0);
|
|
fhf = FALSE;
|
|
};
|
|
|
|
CHfFid(HF hfAlreadyOpened) {
|
|
hf = (HFILE) hfAlreadyOpened;
|
|
fhf = TRUE;
|
|
};
|
|
|
|
~CHfFid(void) {
|
|
if (!fhf && hf != HFILE_ERROR)
|
|
_lclose(hf);
|
|
};
|
|
|
|
int STDCALL seek(int lPos, int wOrg) {
|
|
return (fhf) ?
|
|
LSeekHf((HF) hf, lPos, wOrg) :
|
|
_llseek(hf, lPos, wOrg); };
|
|
int STDCALL tell(void) {
|
|
return (fhf) ?
|
|
((QRWFO) hf)->lifCurrent :
|
|
Tell((HANDLE) hf); };
|
|
int STDCALL write(void* qv, int lcb) {
|
|
if (fhf) {
|
|
RC_TYPE rc = RcWriteHf((HF) hf, qv, lcb);
|
|
return (rc == RC_Success) ? lcb : -(int)rc;
|
|
}
|
|
else
|
|
return _lwrite(hf, (LPCSTR) qv, lcb);
|
|
};
|
|
|
|
RC_TYPE GetRcError(void) { return (RC_TYPE) (GetLastError() & ~SETERROR_MASK); };
|
|
|
|
HFILE hf;
|
|
protected:
|
|
BOOL fhf;
|
|
};
|
|
|
|
class CFMDirCurrent
|
|
{
|
|
public:
|
|
FM fm;
|
|
|
|
CFMDirCurrent(PCSTR szFileName) {
|
|
fm = FmNewSzDir(szFileName, DIR_CURRENT); };
|
|
|
|
~CFMDirCurrent() {
|
|
if (fm)
|
|
lcFree(fm);
|
|
};
|
|
|
|
void* Ptr(void) { return fm; };
|
|
};
|
|
|
|
RC_TYPE STDCALL RcWriteRgrbmh(int crbmh, PBMH * rgrbmh, HF hf,
|
|
PSTR qchFile, BOOL fTransparent, FM fmSrc, int TypeCompression)
|
|
{
|
|
int lcb, lcbBits, crgbColorTable, lcbUncompressedBits;
|
|
BMH bmh;
|
|
PVOID pvSrcBits, pvCompressedBits;
|
|
int ibmh;
|
|
RC_TYPE rc = RC_Success;
|
|
CMem* pRleBits = NULL;
|
|
CMem* pRleZeckBits = NULL;
|
|
|
|
ASSERT(qchFile != NULL || hf != NULL);
|
|
|
|
CHfFid* pcfile;
|
|
|
|
if (hf)
|
|
pcfile = new CHfFid(hf);
|
|
else {
|
|
CFMDirCurrent cfmFile(qchFile);
|
|
if (!cfmFile.fm)
|
|
return RC_OutOfMemory;
|
|
|
|
pcfile = new CHfFid(qchFile);
|
|
|
|
if (pcfile->hf == HFILE_ERROR)
|
|
return pcfile->GetRcError();
|
|
}
|
|
|
|
UINT lcbBgh = sizeof(BGH) + sizeof(DWORD) * (crbmh - 1);
|
|
CMem bgh(lcbBgh);
|
|
BGH* pbgh = (BGH*) bgh.pb;
|
|
|
|
// REVIEW: huh? Why does WinHelp care about this flag? The compression
|
|
// flag should specify what to do, not this general purpose flag
|
|
|
|
pbgh->wVersion = (TypeCompression & COMPRESS_BMP_ZECK) ?
|
|
BMP_VERSION3 : BMP_VERSION2;
|
|
pbgh->cbmhMac = crbmh;
|
|
|
|
pcfile->seek(lcbBgh, SEEK_SET);
|
|
|
|
for (ibmh = 0; ibmh < crbmh; ++ibmh) {
|
|
|
|
// Put offset to bitmap in group header
|
|
|
|
pbgh->acBmh[ibmh] = pcfile->tell();
|
|
|
|
/*
|
|
* Bitmaps must be uncompressed in memory, and get compressed when
|
|
* they are translated to disk. Currently, we only support Windows
|
|
* bitmaps, DIBs, and metafiles.
|
|
*/
|
|
|
|
PBMH pbmh = rgrbmh[ibmh];
|
|
ASSERT(pbmh->bmFormat == bmWbitmap || pbmh->bmFormat == bmDIB ||
|
|
pbmh->bmFormat == bmWmetafile);
|
|
ASSERT(pbmh->fCompressed == BMH_COMPRESS_NONE);
|
|
|
|
if (pbmh->bmFormat == bmWmetafile) {
|
|
crgbColorTable = 0;
|
|
if (pbmh->cbOffsetBits == 0)
|
|
pvSrcBits = (void*) pbmh->w.mf.hMF;
|
|
else
|
|
pvSrcBits = (PBYTE) pbmh + pbmh->cbOffsetBits;
|
|
}
|
|
else {
|
|
|
|
/*
|
|
* We must make sure that the number of bits we actually
|
|
* write out will not overflow the buffer we will allocate at
|
|
* run time.
|
|
*/
|
|
|
|
crgbColorTable = pbmh->w.dib.biClrUsed;
|
|
pbmh->cbSizeBits = min(pbmh->cbSizeBits,
|
|
LAlignLong(pbmh->w.dib.biWidth * pbmh->w.dib.biBitCount) *
|
|
pbmh->w.dib.biHeight);
|
|
|
|
pvSrcBits = QFromQCb(pbmh, pbmh->cbOffsetBits);
|
|
}
|
|
|
|
/*
|
|
* We clear out these values because Alchemy creates bogus ones
|
|
* and because WinHelp 3.1 doesn't handle them correctly. We then
|
|
* reserve biYPelsPerMeter for use with Zeck+RLE compression.
|
|
*/
|
|
|
|
if (pbmh->bmFormat != bmWmetafile) {
|
|
pbmh->w.dib.biXPelsPerMeter = 0;
|
|
pbmh->w.dib.biYPelsPerMeter = 0;
|
|
}
|
|
lcbUncompressedBits = pbmh->cbSizeBits;
|
|
|
|
/*
|
|
* Allocate enough for a Zeck byte every 8 bytes, plus 1 for the
|
|
* remainder of less than 8 bytes.
|
|
*/
|
|
|
|
// REVIEW: Is this sufficient for RLE?
|
|
|
|
int cbMem = (DWORD) pbmh->cbSizeBits + (pbmh->cbSizeBits >> 3) + 512;
|
|
CMem bits(cbMem);
|
|
|
|
ASSERT(pvSrcBits);
|
|
|
|
// REVIEW: BMH_COMPRESS_RLE_ZECK has been added to WinHelp, but
|
|
// we can't support this until we've had a chance to debug the
|
|
// code both here and in WinHelp.
|
|
|
|
int cRLE;
|
|
int cZeckRle;
|
|
|
|
// Zeck only compression?
|
|
|
|
if (TypeCompression & COMPRESS_BMP_ZECK &&
|
|
!(TypeCompression & COMPRESS_BMP_RLE)) {
|
|
lcbBits = LcbCompressZeck((PBYTE) pvSrcBits,
|
|
bits.pb, pbmh->cbSizeBits, cbMem);
|
|
ConfirmOrDie(lcbBits < cbMem);
|
|
if (lcbBits >= pbmh->cbSizeBits) {
|
|
pvCompressedBits = pvSrcBits;
|
|
lcbBits = pbmh->cbSizeBits;
|
|
pbmh->fCompressed = (BYTE) BMH_COMPRESS_NONE;
|
|
}
|
|
else {
|
|
pvCompressedBits = bits.pb;
|
|
pbmh->fCompressed = (BYTE) BMH_COMPRESS_ZECK;
|
|
}
|
|
}
|
|
|
|
// RLE only compression?
|
|
|
|
else if (TypeCompression & COMPRESS_BMP_RLE &&
|
|
!(TypeCompression & COMPRESS_BMP_ZECK)) {
|
|
lcbBits = RleCompress((PBYTE) pvSrcBits,
|
|
bits.pb, pbmh->cbSizeBits);
|
|
ConfirmOrDie(lcbBits < cbMem);
|
|
if (lcbBits >= pbmh->cbSizeBits) {
|
|
pvCompressedBits = pvSrcBits;
|
|
lcbBits = pbmh->cbSizeBits;
|
|
pbmh->fCompressed = BMH_COMPRESS_NONE;
|
|
}
|
|
else {
|
|
pvCompressedBits = bits.pb;
|
|
pbmh->fCompressed = BMH_COMPRESS_30;
|
|
}
|
|
}
|
|
|
|
// Use whatever compression gets the best results
|
|
|
|
else {
|
|
|
|
// RLE compression
|
|
|
|
pRleBits = new CMem(cbMem);
|
|
cRLE = RleCompress((PBYTE) pvSrcBits, pRleBits->pb,
|
|
pbmh->cbSizeBits);
|
|
ConfirmOrDie(cRLE < cbMem);
|
|
pRleBits->resize(cRLE);
|
|
|
|
// RLE + Zeck compression
|
|
|
|
pRleZeckBits = new CMem(cbMem);
|
|
cZeckRle = LcbCompressZeck(pRleBits->pb, pRleZeckBits->pb, cRLE,
|
|
cbMem);
|
|
ConfirmOrDie(cZeckRle < cbMem);
|
|
pRleZeckBits->resize(cZeckRle);
|
|
#ifdef _DEBUG
|
|
lcHeapCheck();
|
|
#endif
|
|
|
|
// Zeck compression
|
|
|
|
lcbBits = LcbCompressZeck((PBYTE) pvSrcBits,
|
|
bits.pb, pbmh->cbSizeBits, cbMem);
|
|
ConfirmOrDie(lcbBits < cbMem);
|
|
|
|
/*
|
|
* At this point we have RLE, RLE+Zeck and Zeck compression.
|
|
* Now we need to figure what gave us the best compression
|
|
* and act accordingly.
|
|
*/
|
|
|
|
if (cRLE < pbmh->cbSizeBits && cRLE < lcbBits &&
|
|
cRLE < cZeckRle) { // RLE?
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
pvCompressedBits = pRleBits->pb;
|
|
lcbBits = cRLE;
|
|
pbmh->fCompressed = BMH_COMPRESS_30;
|
|
}
|
|
|
|
// Can't combine compressions with metafiles because
|
|
// pbmh->w.dib.biYPelsPerMeter doesn't exist in a metafile structure
|
|
|
|
else if (pbmh->bmFormat != bmWmetafile &&
|
|
cZeckRle < pbmh->cbSizeBits && cZeckRle < cRLE &&
|
|
cZeckRle < lcbBits) { // RLE + Zeck?
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
pvCompressedBits = pRleZeckBits->pb;
|
|
lcbBits = cZeckRle;
|
|
pbmh->fCompressed = BMH_COMPRESS_RLE_ZECK;
|
|
|
|
/*
|
|
* We store the size of the block needed to decompress
|
|
* into the RLE block. This lets WinHelp know exactly how
|
|
* much memory to allocate in order to decompress the
|
|
* bitmap. We don't allow WinHelp to use these values the
|
|
* way they were originally intended both because WinHelp
|
|
* 3.1 didn't deal with them correctly and because Alchemy
|
|
* puts in bogus values.
|
|
*/
|
|
|
|
pbmh->w.dib.biYPelsPerMeter = cRLE;
|
|
}
|
|
else if (lcbBits < pbmh->cbSizeBits && lcbBits < cRLE &&
|
|
lcbBits < cZeckRle) { // Zeck?
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
pvCompressedBits = bits.pb;
|
|
pbmh->fCompressed = BMH_COMPRESS_ZECK;
|
|
}
|
|
else { // no compression is better
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
pvCompressedBits = pvSrcBits;
|
|
lcbBits = pbmh->cbSizeBits;
|
|
pbmh->fCompressed = BMH_COMPRESS_NONE;
|
|
}
|
|
}
|
|
|
|
// Now, compress the header into the stack.
|
|
|
|
bmh.bmFormat = pbmh->bmFormat;
|
|
bmh.fCompressed = pbmh->fCompressed;
|
|
void* pv = PfromPcb(&bmh, sizeof(WORD));
|
|
|
|
switch (pbmh->bmFormat) {
|
|
case bmWbitmap:
|
|
case bmDIB:
|
|
|
|
/*
|
|
* Note: These fields must be written in the same order that
|
|
* they are read in HbmhExpandQv() in bitmap.c
|
|
*/
|
|
|
|
pv = PVMakeQGB(pbmh->w.dib.biXPelsPerMeter, pv);
|
|
pv = PVMakeQGB(pbmh->w.dib.biYPelsPerMeter, pv);
|
|
pv = PVMakeQGA(pbmh->w.dib.biPlanes, pv);
|
|
pv = PVMakeQGA(pbmh->w.dib.biBitCount, pv);
|
|
|
|
pv = PVMakeQGB(pbmh->w.dib.biWidth, pv);
|
|
pv = PVMakeQGB(pbmh->w.dib.biHeight, pv);
|
|
pv = PVMakeQGB(pbmh->w.dib.biClrUsed, pv);
|
|
|
|
if (fTransparent) {
|
|
if (pbmh->w.dib.biBitCount != 1)
|
|
pbmh->w.dib.biClrImportant = 1;
|
|
}
|
|
|
|
pv = PVMakeQGB(pbmh->w.dib.biClrImportant, pv);
|
|
|
|
ASSERT(pbmh->w.dib.biCompression == 0L);
|
|
|
|
break;
|
|
|
|
case bmWmetafile:
|
|
pv = PVMakeQGA((UINT) pbmh->w.mf.mm, pv);
|
|
*(INT16 *) pv = (INT16)pbmh->w.mf.xExt;
|
|
pv = PfromPcb(pv, sizeof(INT16));
|
|
*(INT16 *) pv = (INT16)pbmh->w.mf.yExt;
|
|
pv = PfromPcb(pv, sizeof(INT16));
|
|
|
|
// Store size of uncompressed bits:
|
|
|
|
pv = PVMakeQGB(lcbUncompressedBits, pv);
|
|
break;
|
|
}
|
|
|
|
pv = PVMakeQGB(lcbBits, pv);
|
|
pv = PVMakeQGB(pbmh->cbSizeExtra, pv);
|
|
|
|
// Calculate and insert offsets.
|
|
|
|
lcb = ((PBYTE) pv - (PBYTE) &bmh) + 2 * sizeof(DWORD) +
|
|
sizeof(RGBQUAD) * crgbColorTable;
|
|
*((DWORD *) pv) = lcb;
|
|
pv = PfromPcb(pv, sizeof(DWORD));
|
|
lcb += lcbBits;
|
|
|
|
/*
|
|
* lcbSizeExtra is non-zero if this is a shed bitmap with hotspot
|
|
* information tacked onto the end.
|
|
*/
|
|
|
|
*((DWORD *) pv) =
|
|
(pbmh->cbSizeExtra == 0 ? 0L : lcb);
|
|
pv = PfromPcb(pv, sizeof(DWORD));
|
|
|
|
// Write out the header, color table, bits, and extra data
|
|
|
|
ASSERT(pvCompressedBits);
|
|
|
|
pcfile->write(&bmh, (int) ((PBYTE) pv - (PBYTE) &bmh));
|
|
if (crgbColorTable != 0) {
|
|
pcfile->write(pbmh->rgrgb, crgbColorTable * sizeof(RGBQUAD));
|
|
cbGraphics += crgbColorTable * sizeof(RGBQUAD);
|
|
}
|
|
if (pcfile->write(pvCompressedBits, lcbBits) != lcbBits) {
|
|
rc = RC_DiskFull;
|
|
break;
|
|
}
|
|
cbGraphics += lcbBits;
|
|
if (pbmh->cbSizeExtra != 0) {
|
|
if (pcfile->write(QFromQCb(pbmh, pbmh->cbOffsetExtra),
|
|
pbmh->cbSizeExtra) != pbmh->cbSizeExtra) {
|
|
rc = RC_DiskFull;
|
|
break;
|
|
}
|
|
cbGraphics += pbmh->cbSizeExtra;
|
|
}
|
|
if (pRleBits) {
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
}
|
|
if (pRleZeckBits) {
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
}
|
|
}
|
|
|
|
if (pRleBits) {
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
}
|
|
if (pRleZeckBits) {
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
}
|
|
|
|
if (rc == RC_Success) {
|
|
|
|
// Write out header with newly calculated offsets
|
|
|
|
pcfile->seek(0L, SEEK_SET);
|
|
pcfile->write(pbgh, lcbBgh);
|
|
cbGraphics += lcbBgh;
|
|
}
|
|
|
|
delete pcfile;
|
|
|
|
return rc;
|
|
}
|
|
|
|
RC_TYPE STDCALL RcWriteHf(HF hf, void* pvData, int lcb)
|
|
{
|
|
int lcbTotalWrote;
|
|
|
|
ASSERT(hf != NULL);
|
|
QRWFO qrwfo = (QRWFO) hf;
|
|
|
|
ASSERT(!(qrwfo->bFlags & FS_OPEN_READ_ONLY));
|
|
|
|
if (!(qrwfo->bFlags & FS_DIRTY)) {
|
|
|
|
ASSERT(!qrwfo->pTmpFile);
|
|
|
|
// make sure we have a temp file version
|
|
// FS permission is checked in RcCopyToTempFile()
|
|
|
|
if (RcCopyToTempFile(qrwfo) != RC_Success) {
|
|
FatalError:
|
|
return (RC_TYPE) (GetLastError() & ~SETERROR_MASK);
|
|
}
|
|
}
|
|
|
|
// position file pointer in temp file
|
|
|
|
if (qrwfo->pTmpFile->seek(sizeof(FH) + qrwfo->lifCurrent,
|
|
SEEK_SET) != (int) (sizeof(FH) + qrwfo->lifCurrent)) {
|
|
goto FatalError;
|
|
}
|
|
lcbTotalWrote = qrwfo->pTmpFile->write(pvData, lcb);
|
|
|
|
if (lcbTotalWrote != lcb) {
|
|
if (!qrwfo->pTmpFile->pszFileName)
|
|
return RC_OutOfMemory;
|
|
|
|
return RC_WriteError;
|
|
}
|
|
|
|
// update file pointer and file size
|
|
|
|
if (lcbTotalWrote > 0) {
|
|
qrwfo->lifCurrent += lcbTotalWrote;
|
|
if (qrwfo->lifCurrent > qrwfo->lcbFile)
|
|
qrwfo->lcbFile = qrwfo->lifCurrent;
|
|
}
|
|
|
|
return RC_Success;
|
|
}
|
|
|
|
int STDCALL RleCompress(PBYTE pbSrc, PBYTE pbDest, int cbSrc)
|
|
{
|
|
int cbRun, cb;
|
|
BYTE ch;
|
|
|
|
PBYTE pbStart = pbDest;
|
|
|
|
while (cbSrc > 0) {
|
|
|
|
// Find next run of dissimilar bytes:
|
|
|
|
cbRun = 0;
|
|
if (cbSrc <= 2)
|
|
cbRun = cbSrc;
|
|
else {
|
|
while (pbSrc[cbRun] != pbSrc[cbRun + 1] ||
|
|
pbSrc[cbRun] != pbSrc[cbRun + 2])
|
|
if (++cbRun >= cbSrc - 2) {
|
|
cbRun = cbSrc;
|
|
break;
|
|
}
|
|
}
|
|
cbSrc -= cbRun;
|
|
|
|
// Output run of dissimilar bytes:
|
|
|
|
while (cbRun > 0) {
|
|
cb = min(cbRun, MAX_RUN);
|
|
*pbDest++ = ((BYTE) cb) | fRLEBit;
|
|
cbRun -= cb;
|
|
while (cb-- > 0)
|
|
*pbDest++ = *pbSrc++;
|
|
}
|
|
|
|
if (cbSrc == 0)
|
|
break;
|
|
|
|
// Find next run of identical bytes:
|
|
|
|
ch = *pbSrc;
|
|
cbRun = 1;
|
|
while (cbRun < cbSrc && ch == pbSrc[cbRun])
|
|
cbRun++;
|
|
|
|
cbSrc -= cbRun;
|
|
pbSrc += cbRun;
|
|
|
|
// Output run of identical bytes:
|
|
|
|
while (cbRun > 0) {
|
|
cb = min(cbRun, MAX_RUN);
|
|
*pbDest++ = (BYTE) cb;
|
|
*pbDest++ = ch;
|
|
cbRun -= cb;
|
|
}
|
|
}
|
|
|
|
return (int) (pbDest - pbStart);
|
|
}
|
|
|
|
static RC_TYPE STDCALL RcCopyToTempFile(QRWFO qrwfo)
|
|
{
|
|
QFSHR qfshr = (QFSHR) qrwfo->hfs;
|
|
|
|
ConfirmOrDie(!(qfshr->fsh.bFlags & FS_OPEN_READ_ONLY));
|
|
|
|
if (!FPlungeQfshr(qfshr))
|
|
return RC_Failure;
|
|
|
|
qrwfo->bFlags |= FS_DIRTY;
|
|
|
|
qrwfo->pTmpFile = new CTmpFile();
|
|
|
|
// copy from FS file into temp file
|
|
|
|
if (_llseek(qfshr->fid, qrwfo->lifBase, SEEK_SET) != qrwfo->lifBase)
|
|
return RC_Failure;
|
|
|
|
ASSERT(qrwfo->pTmpFile);
|
|
|
|
if (qrwfo->pTmpFile->copyfromfile(qfshr->fid, qrwfo->lcbFile + sizeof(FH))
|
|
!= RC_Success) {
|
|
delete qrwfo->pTmpFile;
|
|
qrwfo->pTmpFile = NULL;
|
|
return RC_Failure;
|
|
}
|
|
return RC_Success;
|
|
}
|
|
|
|
static BOOL STDCALL FPlungeQfshr(QFSHR qfshr)
|
|
{
|
|
if (qfshr->fid == HFILE_ERROR) {
|
|
qfshr->fid = _lopen(qfshr->fm,
|
|
qfshr->fsh.bFlags & FS_OPEN_READ_ONLY ?
|
|
OF_READ : OF_READWRITE);
|
|
|
|
if (qfshr->fid == HFILE_ERROR) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Check size of file, then reset file pointer. Certain 0-length
|
|
* files (eg con) give us no end of grief if we try to read from
|
|
* them, and since a 0-length file could not possibly be a valid FS,
|
|
* we reject the notion.
|
|
*/
|
|
|
|
if (GetFileSize((HANDLE) qfshr->fid, NULL) < sizeof(FSH)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|