Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

562 lines
14 KiB

/************************************************************************
* *
* DECOMP.CPP *
* *
* Copyright (C) Microsoft Corporation 1993-1994 *
* All Rights reserved. *
* *
************************************************************************/
#include "stdafx.h"
#pragma hdrstop
#include "forage.h"
#include "skip.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
INLINE static PSTR STDCALL QchDecompressW(DWORD, PSTR, QPHR);
static RC_TYPE STDCALL RcResolveQLA(QLA qla, QDE qde);
static RC_TYPE STDCALL RcScanBlockOffset(QDE qde, GH gh, DWORD lcbRead, DWORD dwBlock, DWORD dwOffset, QVA qva, QOBJRG qobjrg);
/***************************************************************************
*
- Name: FixUpBlock
-
* Purpose:
* Fixes up the Prev and Next pointers in the in-memory block image
* from the MBHD. Once upon a time the compiler would generate the
* wrong next and previous pointers in the block header. This routine
* is called after calculating the correct values to place them into
* the cached block image, so as not to again need recalculation should
* the block again be requested while still in memory.
*
* Arguments:
* qmbhd - pointer to MBHD containing the correct Next/Prev info
* qbBuf - pointer to cached block, containing erroneous Next/Prev
* wVersion - version number of the file we're dealing with
*
* Returns:
* nothing
*
***************************************************************************/
void STDCALL FixUpBlock(LPVOID qmbhd, LPVOID qbBuf, WORD wVersion)
{
if (wVersion != wVersion3_0) {
((QMBHD)qbBuf)->vaFCPPrev = ((QMBHD)qmbhd)->vaFCPPrev;
((QMBHD)qbBuf)->vaFCPNext = ((QMBHD)qmbhd)->vaFCPNext;
}
else {
((QMBHD)qbBuf)->vaFCPPrev.dword =
VAToOffset30 (&((QMBHD)qmbhd)->vaFCPPrev);
((QMBHD)qbBuf)->vaFCPNext.dword =
VAToOffset30 (&((QMBHD)qmbhd)->vaFCPNext);
}
}
/***************************************************************************
*
- Name CbDecompressQch
-
* Purpose
* Decompresses the given string.
*
* Arguments
* qchSrc -- String to be decompressed.
* lcb -- size of string to be decompressed.
* qchDest -- place to put decompressed string.
* hphr -- handle to phrase table.
*
* Returns
* Number of characters placed into the decompressed string. Returns
* cbDecompressNil if it fails due to OOM.
*
* +++
*
* Notes
* Does not use huge pointers, so the source and destination buffers
* cannot cross segment boundaries. Then why is the size of the
* source passed as a long? I don't know.
*
***************************************************************************/
int STDCALL CbDecompressQch(PSTR qchSrc, int lcb, PSTR qchDest, HPHR hphr,
UINT wVersionNo)
{
DWORD wPhraseToken, wTokenMin, wTokenMax;
PSTR qchStart, qchLast;
QPHR qphr;
/*
* If hphr is NULL, then GetQFCINFO() should have thought we were
* uncompressed and not called us. If GetQFCINFO() thinks we're
* compressed, it means the uncompressed size is larger then the
* compressed size, which almost certainly means we will break here
* without hphr.
*/
ASSERT(hphr);
if (hphr == NULL) {
memmove(qchDest, qchSrc, lcb);
return (WORD) lcb;
}
qphr = (QPHR) hphr;
wTokenMin = qphr->wBaseToken;
wTokenMax = qphr->wBaseToken + 2 * qphr->cPhrases;
qchLast = qchSrc + lcb - 1; // Last possible position of a phrase token
qchStart = qchDest;
while (qchSrc < qchLast) {
wPhraseToken = (((WORD) qchSrc[0]) << 8) + (BYTE) qchSrc[1];
if (wPhraseToken >= wTokenMin && wPhraseToken < wTokenMax) {
qchDest = QchDecompressW(wPhraseToken, qchDest, qphr);
if (qchDest == NULL) {
return cbDecompressNil;
}
qchSrc += 2;
ASSERT( qchSrc <= qchLast + 1 );
}
else
*qchDest++ = *qchSrc++;
}
// Check for last character
if (qchSrc == qchLast)
*qchDest++ = *qchSrc++;
return qchDest - qchStart;
}
void STDCALL TranslateMFCP(LPVOID qvDst, LPVOID qvSrc, VA va, WORD wVersion)
{
QMFCP qmfcpSrc = (QMFCP) qvSrc;
QMFCP qmfcpDst = (QMFCP) qvDst;
// First copy whole structure to get the non-translated fields:
*qmfcpDst = *qmfcpSrc;
if (wVersion != wVersion3_0)
return;
OffsetToVA30(&(qmfcpDst->vaPrevFc),
VAToOffset30(&va) - qmfcpSrc->vaPrevFc.dword);
OffsetToVA30(&(qmfcpDst->vaNextFc),
VAToOffset30(&va) + qmfcpSrc->vaNextFc.dword);
}
VA STDCALL VAFromQLA(QLA qla, QDE qde)
{
FVerifyQLA(qla);
if (RcResolveQLA(qla, qde) == RC_Success)
return qla->mla.va;
else {
VA vanil;
vanil.dword = vaNil;
return vanil;
}
}
#ifdef _DEBUG
void STDCALL FVerifyQLA(QLA qla)
{
ASSERT(qla != NULL);
#ifdef MAGIC
ASSERT(qla->wMagic == wLAMagic);
#endif
if (FResolvedQLA(qla)) {
ASSERT(qla->mla.va.dword != vaNil);
ASSERT(qla->mla.objrg != objrgNil);
if (qla->wVersion != wVersion3_0)
ASSERT(!FIsInvalidPA(qla->pa));
}
}
void STDCALL FVerifyQMOPG(QMOPG qmopg)
{
#ifdef MAGIC
ASSERT(qmopg->bMagic == bMagicMOPG);
#endif
ASSERT(qmopg->libText >= 0);
ASSERT(!qmopg->fStyle);
ASSERT(!qmopg->fMoreFlags);
ASSERT(qmopg->justify >= 0 && qmopg->justify <= JUSTIFYMOST);
ASSERT(qmopg->ySpaceOver >= 0);
ASSERT(qmopg->ySpaceUnder >= 0);
ASSERT(qmopg->yLineSpacing >= -10000 && qmopg->yLineSpacing < 10000);
ASSERT(qmopg->xRightIndent >= 0);
ASSERT(qmopg->xFirstIndent >= -10000 && qmopg->xFirstIndent < 10000);
ASSERT(qmopg->xTabSpacing >= 0 && qmopg->xTabSpacing < 10000);
ASSERT(qmopg->cTabs >= 0 && qmopg->cTabs <= MAX_TABS);
}
#endif // _DEBUG
/***************************************************************************
*
- Name QchDecompressW
-
* Purpose
* Given a phrase token and a pointer to a buffer, copies the
* corresponding phrase to that buffer
*
* Arguments
* wPhraseToken -- phrase token to be inserted.
* qch -- buffer to place phrase.
* qphr -- pointer to phrase table.
*
* Returns
* A pointer to the character past the last character of the phrase
* placed in the buffer. Returns NULL if unable to load the phrase
* due to out of memory.
*
* +++
*
* Notes
* The phrase token includes an index into the phrase table, as
* well as a flag indicating whether or not a space should be
* appended to the phrase.
*
***************************************************************************/
INLINE static PSTR STDCALL QchDecompressW(DWORD wPhraseToken, PSTR pszDst,
QPHR qphr)
{
DWORD iPhrase;
BOOL fSpace;
WORD* pi;
int cbPhrase;
#ifdef _DEBUG
PSTR pszPhrases;
#endif
ASSERT(qphr);
pi = (WORD*) qphr->qcb;
// Calculate iPhrase and fSpace:
iPhrase = (DWORD) (wPhraseToken - qphr->wBaseToken);
fSpace = iPhrase & 1;
iPhrase >>= 1;
ASSERT(iPhrase < (WORD) qphr->cPhrases);
#ifdef _DEBUG
pszPhrases = (PSTR) QFromQCb(pi, pi[iPhrase]);
#endif
cbPhrase = (int) pi[iPhrase + 1] - (int) pi[iPhrase];
MoveMemory(pszDst, QFromQCb(pi, pi[iPhrase]), cbPhrase);
pszDst += cbPhrase;
if (fSpace)
*pszDst++ = ' ';
return pszDst;
}
static RC_TYPE STDCALL RcResolveQLA(QLA qla, QDE qde)
{
RC_TYPE rc;
int wErr;
GH gh;
int lcbRead;
FVerifyQLA(qla);
if (FResolvedQLA(qla))
return RC_Success;
if (QDE_HFTOPIC(qde) == NULL)
return RC_BadHandle;
/* Read in the (possibly cached) block */
/* REVIEW: error return types? */
gh = GhFillBuf(qde, qla->pa.blknum, &lcbRead, &wErr);
if (gh == NULL)
return RC_Failure;
rc = RcScanBlockOffset(qde, gh, lcbRead, qla->pa.blknum,
qla->pa.objoff, &qla->mla.va, &qla->mla.objrg);
if (rc != RC_Success)
return rc;
FVerifyQLA(qla);
return RC_Success;
}
/* Given a block and an offset, return the FCID and OBJRG */
/* OBJRG is relative to the FC */
/***************************************************************************
*
- Name: RcScanBlockOffset
-
* Purpose: ?
*
* Arguments: hf ?
* qb This is originally a QchFillBuf() object, which
* must be released by this procedure.
* fcidMax ?
* dwBlock ?
* dwOffset ?
* qfcid ?
* qobjrg ?
*
* Returns: RC_Success or RC_Failure
*
* Globals Used: RC_Failure, RC_Success, etc?
*
* +++
*
* Notes:
*
***************************************************************************/
static RC_TYPE STDCALL RcScanBlockOffset(QDE qde, GH gh, DWORD lcbRead, DWORD dwBlock,
DWORD dwOffset, QVA qva, QOBJRG qobjrg)
{
DWORD dwPrev;
VA vaCur, vaT;
MOBJ mobj;
QMFCP qmfcp;
MFCP mfcp;
int wErr;
PBYTE qb, qbBlock;
MBHD mbhd;
qbBlock = (PBYTE) gh;
TranslateMBHD(&mbhd, qbBlock, QDE_HHDR(qde).wVersionNo);
vaCur = mbhd.vaFCPNext;
dwPrev = 0;
for (;;) {
// Before using qb, we ensure that we will still be looking inside the blk
while (vaCur.bf.blknum == dwBlock && vaCur.bf.byteoff < lcbRead) {
qb = qbBlock + vaCur.bf.byteoff;
qmfcp = (QMFCP) qb;
TranslateMFCP(&mfcp, qmfcp, vaCur, QDE_HHDR(qde).wVersionNo);
CbUnpackMOBJ((QMOBJ)&mobj, (PBYTE)qmfcp + sizeof(MFCP));
/*
* Does our given offset fall in this FC's range of object-region
* numbers?
*/
if (dwOffset < dwPrev + mobj.wObjInfo)
goto found_it;
dwPrev += mobj.wObjInfo;
vaT = vaCur;
//ASSERT(qmfcp->ldichNextFc != (int) 0);
vaCur = mfcp.vaNextFc;
}
/* NOTE:
* In the case that a topic FC begins in the given block and ends
* in the next, the object FC following it will also begin in the NEXT
* block. But to make Larry's life easier we say that this object FC
* belongs to our given block (as well as the block it lives in). So
* if we are given an object offset bigger than the total object space
* of the given block, we continue counting in the next block(s).
*
* So we increment the block num, read the next block, and prepare to
* re-do the above WHILE loop until we find the FC we need.
*/
++dwBlock;
gh = GhFillBuf(qde, dwBlock, (int*) &lcbRead, &wErr);
if (gh == NULL) {
qva->dword = vaNil;
*qobjrg = objrgNil;
return RC_Failure;
}
qbBlock = (PBYTE) gh;
}
found_it:
ASSERT(dwOffset >= dwPrev);
*qva = vaCur;
*qobjrg = (OBJRG)(dwOffset - dwPrev);
return RC_Success;
}
// Perform 3.0 -> 3.5 addressing translation:
void STDCALL TranslateMBHD(LPVOID qvDst, LPVOID qvSrc, WORD wVersion)
{
QMBHD qmbhdSrc = (QMBHD) qvSrc;
QMBHD qmbhdDst = (QMBHD) qvDst;
if (wVersion != wVersion3_0)
*qmbhdDst = *qmbhdSrc;
else {
OffsetToVA30(&(qmbhdDst->vaFCPPrev), qmbhdSrc->vaFCPPrev.dword);
OffsetToVA30(&(qmbhdDst->vaFCPNext), qmbhdSrc->vaFCPNext.dword);
OffsetToVA30(&(qmbhdDst->vaFCPTopic), qmbhdSrc->vaFCPTopic.dword);
}
}
/*-------------------------------------------------------------------------
| CbUnpackMOBJ(qmobj, qv) |
| |
| Purpose: Unpack an MOBJ data structure. |
-------------------------------------------------------------------------*/
int STDCALL CbUnpackMOBJ(QMOBJ qmobj, void* qv)
{
LPVOID qvFirst = qv;
/*
* Topic FCs are not packed, because the topic size needs to be
* backpatched by the compiler.
*/
if (((QMOBJ) qv) ->bType == FCTYPE_TOPIC ||
((QMOBJ) qv) ->bType == FCTYPE_TOPIC_COUNT) {
qmobj->bType = *((LPBYTE) qv);
qv = (((LPBYTE) qv) + 1);
qmobj->lcbSize = *((int*) qv);
qv = (((int*) qv) + 1);
/*
* If FC is uncounted, then it doesn't contain the last field in the
* MOBJ, and we need to set wObjInfo to 0. Note that we cannot simply
* copy the MOBJ structure because it is longer in Help 3.5: the MOBJ
* for a Help 3.0 file (and any structure in general) may happen right
* at the end of a segment. (See H3.5 739)
*/
if (qmobj->bType == FCTYPE_TOPIC_COUNT) {
qmobj->wObjInfo = *((PWORD) qv);
qv = (((PWORD) qv) + 1);
}
else
qmobj->wObjInfo = 0;
return ((INT) ((LPBYTE) qv - (LPBYTE) qvFirst));
}
#ifdef MAGIC
qmobj->bMagic = *((LPBYTE) qv);
qv = (((LPBYTE) qv) + 1);
ASSERT(qmobj->bMagic == bMagicMOBJ);
#endif /* _DEBUG */
qmobj->bType = *((LPBYTE) qv);
qv = (((LPBYTE) qv) + 1);
qv = (LPVOID) QVSkipQGE((LPBYTE) qv, &qmobj->lcbSize);
ASSERT(qmobj->lcbSize >= 0);
if (qmobj->bType > MAX_UNCOUNTED_OBJ_TYPE)
qv = QVSkipQGA(qv, (PWORD) &qmobj->wObjInfo);
else
qmobj->wObjInfo = 0;
return((INT) ((LPBYTE) qv - (LPBYTE) qvFirst));
}
/*******************
*
- Name: WCopyContext
-
* Purpose: Copy the text of a full context into a global block of
* memory;
*
* Arguments: hhf - help file handle
* ichPos - position within that topic to start copying
* qchDest - Where to copy the topic to
* cb - number of bytes to copy
*
* Returns: wERRS_NO on success, other error code if it was unable
* to copy the text.
*
* Method: Copies partial or complete buffers to qchDest until
* cb bytes have been copied.
*
******************/
WORD STDCALL WCopyContext(QDE qde, VA vaPos, PSTR qchDest, int cb)
{
GH gh;
PBYTE qb;
int lcbRead, lcbT;
int wErr;
// Ignore cb of zero, will occur return wERRS_NONE; for beyond topic handles
ASSERT(cb >= 0);
if (cb <= 0L)
return wERRS_NONE;
// Initial fill of buffer -- should succeed
if ((gh = GhFillBuf(qde, vaPos.bf.blknum, &lcbRead, &wErr)) == NULL)
return wErr;
qb = (PBYTE) gh;
qb += vaPos.bf.byteoff;
qb += sizeof(MFCP);
lcbRead -= vaPos.bf.byteoff;
lcbRead -= sizeof(MFCP);
qchDest += sizeof(FCINFO);
cb -= sizeof(FCINFO);
ASSERT((int) lcbRead >= 0); // check for MFCP crossing 2K boundary.
// Loop reading successive blocks until we've read cb bytes:
for (;;) {
/*
* The first sizeof(MBHD) bytes of a block are the block header, so
* skip them.
*/
if (vaPos.bf.byteoff < sizeof(MBHD)) {
/*
* Fix for bug 1636 (kevynct)
* ichPos was not being updated by the size of the block header
* when the block was first read in.
*
* Note that we update ichPos using IBlock(qch), so that it
* must be done before qch is increased.
*/
qb += sizeof(MBHD) - vaPos.bf.byteoff;
lcbRead -= sizeof(MBHD) - vaPos.bf.byteoff;
}
/*
* ASSUMPTION!!! - the size of an FCP will never make it larger than
* the file.
*/
lcbT = min(cb, lcbRead);
memmove(qchDest, qb, lcbT);
cb -= lcbT;
vaPos.bf.blknum += 1;
vaPos.bf.byteoff = 0;
ASSERT(cb >= 0); // cb should never go negative
qchDest += lcbT;
if (cb == 0)
break; // FCP is now copied
ASSERT(cb >= 0);
if ((gh = GhFillBuf(qde, vaPos.bf.blknum, &lcbRead, &wErr)) == NULL)
return wErr;
qb = (PBYTE) gh;
}
return wERRS_NONE;
}