mirror of https://github.com/lianthony/NT4.0
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.
345 lines
9.1 KiB
345 lines
9.1 KiB
/*****************************************************************************
|
|
* *
|
|
* COMPRESS.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1990. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* This module performs text compression at compile time and *
|
|
* decompression at run time using a list of phrases to be suppressed. *
|
|
* This list gets put in to the |Phrases file in the filesystem, which is *
|
|
* read in at runtime. *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
#include "help.h"
|
|
#include "inc\_compres.h"
|
|
#include "inc\compress.h"
|
|
|
|
INLINE static LPSTR STDCALL QchDecompressW(DWORD, LPSTR, QPHR);
|
|
INLINE static RC STDCALL RcLoadPhrases(HF hf, QPHR qphr, WORD wVersionNo);
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name HphrLoadTableHfs
|
|
-
|
|
* Purpose
|
|
* Loads the phrase table from the given help file.
|
|
*
|
|
* Arguments
|
|
* hfs -- A handle to the help file filesystem.
|
|
* wVersionNo - help ver no., needed to know whether to decompress.
|
|
*
|
|
* Returns
|
|
* A handle to the phrase table to be used for decompression. Returns
|
|
* NULL if the help file is not compressed, and hphrOOM on out of memory,
|
|
* meaning that the help file cannot be displayed properly.
|
|
*
|
|
***************************************************************************/
|
|
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg(".text", "CODE")
|
|
#endif
|
|
const char txtPhraseTable[] = "|Phrases";
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
HPHR STDCALL HphrLoadTableHfs(HFS hfs, WORD wVersionNo)
|
|
{
|
|
QPHR qphr;
|
|
HF hf;
|
|
|
|
ASSERT(hfs);
|
|
hf = HfOpenHfs(hfs, txtPhraseTable, fFSOpenReadOnly);
|
|
if (hf == NULL) {
|
|
if (RcGetFSError() != rcNoExists)
|
|
return hphrOOM;
|
|
return NULL;
|
|
}
|
|
|
|
qphr = (QPHR) GhAlloc(GMEM_FIXED, sizeof(PHR));
|
|
if (qphr == NULL) {
|
|
RcCloseHf(hf);
|
|
return hphrOOM;
|
|
}
|
|
|
|
qphr->hfs = hfs;
|
|
{
|
|
LONG cbHdrTmp =
|
|
(LONG) (wVersionNo == wVersion3_0 ? cbPhrHeader3_0 : cbPhrHeader);
|
|
if (LcbReadHf(hf, qphr, cbHdrTmp) != cbHdrTmp) {
|
|
ASSERT(FALSE);
|
|
|
|
// 16-Feb-1994 [ralphw]
|
|
// Return value is wrong, but 3.1 didn't return at all!
|
|
|
|
goto Fail;
|
|
}
|
|
}
|
|
#ifndef _X86_
|
|
/* SDFF map the phrase table header: */
|
|
LcbMapSDFF( ISdffFileIdHf( hf ),
|
|
(wVersionNo == wVersion3_0 ? SE_PHRASE_HEADER_30 : SE_PHRASE_HEADER),
|
|
qphr, qphr );
|
|
#endif
|
|
|
|
if (rcSuccess != RcLoadPhrases(hf, qphr, wVersionNo)) {
|
|
Fail:
|
|
RcCloseHf(hf);
|
|
FreeGh((GH) qphr);
|
|
return hphrOOM;
|
|
}
|
|
|
|
RcCloseHf(hf);
|
|
|
|
return (HPHR) qphr;
|
|
}
|
|
|
|
INLINE static RC STDCALL NEAR RcLoadPhrases(HF hf, QPHR qphr, WORD wVersionNo)
|
|
{
|
|
HANDLE hrgcb;
|
|
DWORD lcbRgcb, lcbCompressed, lcbOffsets;
|
|
INT16* qcb;
|
|
PBYTE pbCompressed;
|
|
|
|
if (wVersionNo == wVersion3_0) { // not zeck block compressed:
|
|
lcbRgcb = LcbSizeHf(hf) - cbPhrHeader3_0;
|
|
|
|
if (!(hrgcb = GhAlloc(GPTR, lcbRgcb)))
|
|
return(rcOutOfMemory);
|
|
qphr->hrgcb = hrgcb;
|
|
|
|
if (LcbReadHf(hf, PtrFromGh(hrgcb), lcbRgcb) != (LONG) lcbRgcb) {
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(wVersionNo == wVersion3_1 || wVersionNo == wVersion40);
|
|
|
|
/*
|
|
* The memory-size of the table is the size of the offset table +
|
|
* the size of the decompressed phrase listing. The size of the
|
|
* offset table is given by sizeof(INT)*cPhrases:
|
|
*/
|
|
|
|
lcbOffsets = (qphr->cPhrases + 1) * sizeof(INT16); // offset table size
|
|
lcbRgcb = lcbOffsets + qphr->cbPhrases; // Whole phrase table size
|
|
lcbCompressed = LcbSizeHf(hf) - cbPhrHeader - lcbOffsets;
|
|
|
|
// the compressed size may be GREATER (when small phrase tables), so
|
|
// use the max of compressed or decompressed sizes (ptr 558):
|
|
|
|
if (!(hrgcb = GhAlloc(GPTR, max(lcbRgcb, lcbCompressed + lcbOffsets))))
|
|
return rcOutOfMemory;
|
|
qphr->hrgcb = hrgcb;
|
|
qcb = PtrFromGh(hrgcb);
|
|
|
|
if (LcbReadHf(hf, qcb, lcbCompressed + lcbOffsets) !=
|
|
(LONG) (lcbCompressed + lcbOffsets)) {
|
|
|
|
// REVIEW: File is corrupted or cannot be read. We should die.
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/*
|
|
* Now must decompress raw phrase listing. Allocate another
|
|
* buffer, copy compressed data into it, then decompress it into the
|
|
* std dest buffer in hrgcb:
|
|
*
|
|
* +1 because lcbCompressed may == 0, and GhAlloc asserts on that.
|
|
*/
|
|
|
|
if (!(pbCompressed = (PBYTE) GhAlloc(GMEM_FIXED, lcbCompressed + 1)))
|
|
return(rcOutOfMemory);
|
|
MoveMemory(pbCompressed, ((PBYTE) qcb) + lcbOffsets, lcbCompressed);
|
|
LcbUncompressZeck(pbCompressed, ((PBYTE) qcb) + lcbOffsets,
|
|
lcbCompressed);
|
|
FreeGh((GH) pbCompressed);
|
|
#ifndef _X86_
|
|
/* Perform SDFF mapping on the offsets table. SDFF does not do the whole
|
|
* table automatically on it's own because the size of the table is
|
|
* determined via cPhrases, thus it does not fall into any of SDFF's
|
|
* "word size preceded" table types. Thus this loop.
|
|
*/
|
|
{
|
|
/* Assumes: qcb is a locked pointer to qhpr->hrgcb */
|
|
unsigned int i;
|
|
SDFF_FILEID fileid = ISdffFileIdHf( hf );
|
|
|
|
for( i = 0; i <= (unsigned int) qphr->cPhrases; i++ ) {
|
|
qcb[i] = WQuickMapSDFF( fileid, TE_WORD, &qcb[i] );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return rcSuccess;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name DestroyHphr
|
|
-
|
|
* Purpose
|
|
* Destroys resources allocated for the phrase table.
|
|
*
|
|
* Arguments
|
|
* A handle to the phrase table.
|
|
*
|
|
* Returns
|
|
* nothing.
|
|
*
|
|
***************************************************************************/
|
|
|
|
void STDCALL DestroyHphr(HPHR hphr)
|
|
{
|
|
if (hphr == NULL)
|
|
return; // No hphr to destroy!
|
|
|
|
FreeGh(((QPHR) PtrFromGh(hphr))->hrgcb);
|
|
FreeGh(hphr);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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,
|
|
LPSTR pszDst, QPHR qphr)
|
|
{
|
|
DWORD iPhrase;
|
|
BOOL fSpace;
|
|
WORD* pi;
|
|
int cbPhrase;
|
|
#ifdef _DEBUG
|
|
PSTR pszPhrases;
|
|
#endif
|
|
|
|
ASSERT(qphr != NULL);
|
|
|
|
pi = PtrFromGh(qphr->hrgcb);
|
|
|
|
// Calculate iPhrase and fSpace:
|
|
|
|
iPhrase = (DWORD) (wPhraseToken - qphr->wBaseToken);
|
|
fSpace = iPhrase & 1;
|
|
iPhrase >>= 1;
|
|
ASSERT(iPhrase < (WORD) qphr->cPhrases);
|
|
|
|
#ifdef _DEBUG
|
|
pszPhrases = 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;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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
|
|
* DECOMPRESS_NIL 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(PCSTR pszSrc, int lcb, LPSTR pszDest,
|
|
HPHR hphr, DWORD wVersionNo)
|
|
{
|
|
DWORD wPhraseToken, wTokenMin, wTokenMax;
|
|
PSTR pszStart;
|
|
PCSTR pszLast;
|
|
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) {
|
|
MoveMemory(pszDest, pszSrc, lcb);
|
|
return lcb;
|
|
}
|
|
|
|
qphr = PtrFromGh(hphr);
|
|
|
|
wTokenMin = qphr->wBaseToken;
|
|
wTokenMax = qphr->wBaseToken + 2 * qphr->cPhrases;
|
|
pszLast = pszSrc + lcb - 1; // Last possible position of a phrase token
|
|
pszStart = pszDest;
|
|
|
|
while (pszSrc < pszLast) {
|
|
wPhraseToken = (((WORD) pszSrc[0]) << 8) + (BYTE) pszSrc[1];
|
|
if (wPhraseToken >= wTokenMin && wPhraseToken < wTokenMax) {
|
|
pszDest = QchDecompressW(wPhraseToken, pszDest, qphr);
|
|
if (pszDest == NULL) {
|
|
return DECOMPRESS_NIL;
|
|
}
|
|
pszSrc += 2;
|
|
ASSERT( pszSrc <= pszLast + 1 );
|
|
}
|
|
else
|
|
*pszDest++ = *pszSrc++;
|
|
}
|
|
|
|
// Check for last character
|
|
|
|
if (pszSrc == pszLast)
|
|
*pszDest++ = *pszSrc++;
|
|
|
|
return (pszDest - pszStart);
|
|
}
|