|
|
/*************************************************************************
* helpdec - HelpDecomp routine and Other ASM code * * Copyright <C> 1988, Microsoft Corporation * * Purpose: * * Revision History: * * 08-Oct-1990 RJSA Converted to C * 22-Dec-1988 LN Removed MASM High Level Lang support (Need * to control segments better than that will * let me) * 08-Dec-1988 LN CSEG * 16-Feb-1988 LN Rewrite for (some) speed * [] 17-Jan-1988 LN Created * **************************************************************************/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#if defined (OS2)
#define INCL_BASE
#include <os2.h>
#else
#include <windows.h>
#endif
#include <help.h>
#include <helpfile.h>
#pragma function( memset, memcpy, memcmp, strcpy, strcmp, strcat )
// In order to increase performance, and because of the functions
// decomp and NextChar being tightly coupled, global variables are
// used instead of passing parameters.
//
PBYTE pHuffmanRoot; // Root of Huffman Tree
PBYTE pCompTopic; // Current pointer to text (compressed)
BYTE BitMask; // Rotating bit mask
BOOL IsCompressed; // True if text is compressed
BYTE NextChar (void); BOOL pascal HelpCmp (PCHAR fpsz1, PCHAR fpsz2, USHORT cbCmp, BOOL fCase, BOOL fTerm);
/**************************************************************************
* * Decomp - Decompress Topic Text * f near pascal Decomp(fpHuffmanRoot, fpKeyphrase, fpTopic, fpDest) * uchar far *fpHuffmanRoot * uchar far *fpKeyphrase * uchar far *fpTopic * uchar far *fpDest * * Purpose: * Fully decompress topic text. Decompresses based on current file, from one * buffer to another. * * Entry: * fpHuffmanRoot - Pointer to root of huffman tree (or NULL if no huffman) * fpKeyphrase - Pointer to keyphrase table (or NULL if no keyphrase) * fpTopic - Pointer to compressed topic text * fpDest - Pointer to destination buffer * * Exit: * FALSE on successful completion * * Exceptions: * Returns TRUE on any error. * **************************************************************************/ BOOL pascal decomp ( PCHAR fpHuffmanRoot, PCHAR fpKeyphrase, PCHAR fpTopic, PCHAR fpDest ){
int cDecomp; /* count of totally decompressed */ BYTE c; /* byte read */
#ifdef BIGDEBUG
char DbgB[128]; char *DbgP = fpDest; #endif
// Initialize global variables.
pHuffmanRoot = (PBYTE)fpHuffmanRoot; pCompTopic = (PBYTE)fpTopic + sizeof(USHORT); BitMask = 0x01; IsCompressed = fpHuffmanRoot ? ((*(USHORT UNALIGNED *)((PBYTE)fpHuffmanRoot + 2)) != 0xFFFF) : FALSE;
cDecomp = *((USHORT UNALIGNED *)fpTopic);
#ifdef BIGDEBUG
sprintf(DbgB, "DECOMPRESSING: HuffmanRoot: %lx, Keyphrase: %lx\n", fpHuffmanRoot, fpKeyphrase ); OutputDebugString(DbgB); sprintf(DbgB, " Topic: %lx, Dest: %lx\n", fpTopic, fpDest ); OutputDebugString(DbgB); if ( IsCompressed ) { OutputDebugString(" The Topic IS Compressed\n"); } #endif
while ( cDecomp > 0 ) {
c = NextChar();
//
// At this point a valid character has been found and huffman decoded. We must
// now perform any other decoding on it that is required.
//
// Variables are:
// c = character
// cDecomp = Output count remaining
// BitMask = bit mask for interpreting input stream
//
// "Magic Cookie" decompression.
// The chararacter stream after huffman encoding is "cookie" encoded, in that
// certain characters are flags which when encountered mean something other than
// themselves. All characters which are NOT such flags (or cookies, as they seem
// to be called), are simply copied to the output stream.
//
// We first check the character to see if it IS a cookie. If it is NOT, we just
// store it, and get the next input byte
//
if ((c >= C_MIN) && (c <= C_MAX)) {
BYTE Cookie = c ;
#ifdef BIGDEBUG
OutputDebugString("Cookie\n"); #endif
// c is a cookie of some sort, jump to the appropriate
// cookie eater.
c = NextChar();
switch (Cookie) { case C_KEYPHRASE0: case C_KEYPHRASE1: case C_KEYPHRASE2: case C_KEYPHRASE3: case C_KEYPHRASE_SPACE0: case C_KEYPHRASE_SPACE1: case C_KEYPHRASE_SPACE2: case C_KEYPHRASE_SPACE3: { ULONG Index; /* Keyword index */ PBYTE pKey; /* Keyword */ BYTE Size; /* Keyword size */
if ((Cookie >= C_KEYPHRASE_SPACE0) && (Cookie <= C_KEYPHRASE_SPACE3)) { Index = (ULONG)((int)Cookie - C_MIN - 4); } else { Index = (ULONG)((int)Cookie - C_MIN); } Index = (ULONG)(((Index * 0x100) + c) * sizeof(PVOID));
pKey = *(PBYTE *)(((PBYTE)fpKeyphrase) + Index);
// pKey = *(PBYTE *)(fpKeyphrase + Index);
Size = *pKey++;
{ BYTE i = Size;
while (i--) { *fpDest++ = *pKey++; } cDecomp -=Size; } if ((Cookie >= C_KEYPHRASE_SPACE0) && (Cookie <= C_KEYPHRASE_SPACE3)) { *fpDest++ = ' '; cDecomp--; } break; }
case C_RUNSPACE: { BYTE Count = c;
while (Count--) { *fpDest++ = ' '; } cDecomp -= c; break; }
case C_RUN: { BYTE b = c; BYTE Cnt;
Cnt = c = NextChar();
while (Cnt--) { *fpDest++ = b; } cDecomp -= c; break; }
case C_QUOTE: *fpDest++ = c; cDecomp--; break;
}
} else {
// c is not a cookie
*fpDest++ = c; cDecomp--; } }
*fpDest++ = '\00'; // Null terminate string
#ifdef BIGDEBUG
sprintf( DbgB, "Decompressed topic: [%s]\n", DbgP ); OutputDebugString( DbgB );
if ( cDecomp < 0 ) { sprintf( DbgB, "DECOMPRESSION ERROR: cDecomp = %d!\n", cDecomp ); OutputDebugString(DbgB); } #endif
return FALSE; }
/**************************************************************************
* * NextChar - Return next character from input stream * * Purpose: * Returns next character from input stream, performing huffman decompression * if enabled. * * Entry: * fpHuffmanRoot = pointer to root of huffman tree * pfpTopic = pointer to pointer to Topic * pBitmask = pointer to bit mask of current bit * * Exit: * Returns character * *pfpTopic and *pBitMask updated. * ************************************************************************** * * Format of Huffman decode tree: * The Huffman decode tree is a binary tree used to decode a bitstream into a * character stream. The tree consists of nodes (internal nodes and leaves). * Each node is represented by a word. If the high bit in the word is set then * the node is a leaf. If the node is an internal node, then the value of the * node is the index of the right branch in the binary tree. The left branch is * the node following the current node (in memory). If the node is a leaf, then * the low byte of the node is a character. * * e.g. * 0: 0004 0 * 1: 0003 / \ * 2: 8020 / \ * 3: 8065 1 \------4 * 4: 0006 / \ / \ * 5: 806C / \ / \ * 6: 8040 2 3 5 6 * ' ' 'e' 'l' '@' * * Using the Huffman decode tree: * The huffman decode tree is used to decode a bitstream into a character * string. The bitstream is used to traverse the decode tree. Whenever a zero * is detected in the bit stream we take the right branch, when one is detected * we take the left branch. When a leaf is reached in the tree, the value of * the leaf (a character) is output, and the current node is set back to the * ********************************************************************/
BYTE NextChar ( void ) {
BYTE b; // current source byte
#ifdef BIGDEBUG
char DbgB[128]; OutputDebugString("NextChar:\n"); #endif
if (IsCompressed) {
USHORT HuffmanNode; // curent node in the huffman tree
USHORT UNALIGNED *pHuffmanNext; // next node in the huffman tree
//
// Huffman decoding.
// This first part of the decode loop performs the actual huffman decode. This
// code is very speed critical. We walk the tree, as defined by the bit pattern
// coming in, and exit this portion of the code when we reach a leaf which
// contains the character that the bit pattern represented.
//
pHuffmanNext = (USHORT UNALIGNED *)pHuffmanRoot; HuffmanNode = *pHuffmanNext;
b = *(pCompTopic - 1); // get last byte read
while (!(HuffmanNode & 0x8000)) { // while not leaf
BitMask >>= 1;
if (!(BitMask)) { //
// Get new byte from input
//
b = *pCompTopic++; BitMask = 0x80; #ifdef BIGDEBUG
sprintf(DbgB, "\tb=%02x Mask=%02x Node=%04x", b, BitMask, HuffmanNode ); OutputDebugString(DbgB); #endif
} else { #ifdef BIGDEBUG
sprintf(DbgB, "\tb=%02x Mask=%02x Node=%04x", b, BitMask, HuffmanNode ); OutputDebugString(DbgB); #endif
}
if (b & BitMask) { //
// one: take left branch
//
pHuffmanNext++; } else { //
// zero: take right branch
//
pHuffmanNext = (PUSHORT)((PBYTE)pHuffmanRoot + HuffmanNode); #ifdef BIGDEBUG
sprintf(DbgB, " <%04x+%02x=%04x (%04x)>", pHuffmanRoot, HuffmanNode, pHuffmanNext, *pHuffmanNext ); OutputDebugString( DbgB ); #endif
}
HuffmanNode = *pHuffmanNext;
#ifdef BIGDEBUG
sprintf(DbgB, " Next=%04x\n", HuffmanNode ); OutputDebugString(DbgB); #endif
}
b = (BYTE)HuffmanNode; // character is low byte of leaf node
} else { b = *pCompTopic++; // not compressed, simply return byte
}
#ifdef BIGDEBUG
sprintf(DbgB, "\t---->%2x [%c]\n", b,b); OutputDebugString(DbgB); #endif
return b; }
/**************************************************************************
* * HelpCmpSz - help system string comparison routine. * f near pascal HelpCmpSz (fpsz1, fpsz2) * uchar far *fpsz1* * uchar far *fpsz2* * * Purpose: * Perform string comparisons for help system look-up. * Default case of HelpCmp below. * * Entry: * fpsz1 = Far pointer to string 1. (Usually the constant string * being "looked-up". * fpsz2 = Far pointer to string 2. This is usually the string table * being searched. * * Exit: * TRUE on match * ********************************************************************/ BOOL pascal HelpCmpSz ( PCHAR fpsz1, PCHAR fpsz2 ){ return HelpCmp(fpsz1, fpsz2, (USHORT)0xFFFF, TRUE, FALSE); // fcase, fTerm
}
/**************************************************************************
* * HelpCmp - help system string comparison routine. * f near pascal HelpCmp (fpsz1, fpsz2, cbCmp, fCase, fTerm) * uchar far *fpsz1 * uchar far *fpsz2 * ushort cbCmp * f fCase * f fTerm * * Purpose: * Perform string comparisons for help system look-up. * * Entry: * fpsz1 = Far pointer to string 1. (Usually the constant string being * "looked-up"). NOTE THAT IF THIS STRING IS NULL, WE RETURN * TRUE! * fpsz2 = Far pointer to string 2. This is usually the string table * being searched. * cbCmp = Max number of bytes to compare. * fCase = TRUE if search is to be case sensitive. * fTerm = TRUE if we allow special termination processing. * * Exit: * TRUE on match * ********************************************************************/
BOOL pascal HelpCmp ( PCHAR fpsz1, PCHAR fpsz2, USHORT cbCmp, BOOL fCase, BOOL fTerm ){
register PBYTE p1 = (PBYTE)fpsz1; register PBYTE p2 = (PBYTE)fpsz2;
while (cbCmp--) {
if ((!*p1) && (!*p2)) { //
// Got a match
//
return TRUE; }
if (!fCase) { if (toupper((char)*p1) != toupper((char)*p2)) { break; } p1++; p2++; } else { if (*p1++ != *p2++) { break; } } }
if (!cbCmp) { return TRUE; }
// At this point, we have terminated the comparison. Termination conditions
// were:
//
// character count exausted: CX == zero. (Complete match, return TRUE)
// Null terminator found: CX != zero, & Zero flag set. (Complete match,
// return TRUE)
// non-match found CX != zero, & Zero flag clear.
//
// In the later case, if special termination processing is NOT selected, we
// return FALSE, having found a mis-match.
//
// If special termination processing is TRUE, then if the mismatched character
// from string 1 is a null, and the mismatched character from string 2 is any
// whitespace or CR, we declare a match. (This is used in minascii processing).
//
if (fTerm) { p1--; p2--; if ((! *p1) && ((*p2 == '\n') || (*p2 == '\t') || (*p2 == ' '))) { return TRUE; } } return FALSE; }
/*************************************************************************
* * hfstrlen - far string length * * Purpose: * return length of null terminated string. * * Entry: * fpszSrc = pointer to source * * Exit: * returns length * *************************************************************************/ USHORT hfstrlen ( PCHAR fpszSrc ){ return (USHORT)strlen(fpszSrc); }
/*************************************************************************
* * hfstrcpy - far string copy * * Purpose: * copy strings * * Entry: * fpszDst = pointer to destination * fpszSrc = pointer to source * * Exit: * pointer to terminating null in destination * *************************************************************************/ PCHAR hfstrcpy ( PCHAR fpszDst, PCHAR fpszSrc ) { return (PCHAR)strcpy(fpszDst, fpszSrc); }
/*************************************************************************
* * hfstrchr - search for character in far string * * Purpose: * a near, pascal routine (for size/speed) to search for a character in * a far string. * * Entry: * fpsz = far pointer to string * c = character to locate * * Exit: * returns far pointer into string * * Exceptions: * returns NULL on character not in string * *************************************************************************/ PCHAR hfstrchr ( PCHAR fpsz, char c ){ return (PCHAR)strchr(fpsz, c); }
/*************************************************************************
* * hfmemzer - zero out memory area. * * Purpose: * a near, pascal routine (for size/speed) to fill an area with zero * * Entry: * fpb = far pointer to buffer * cb = count of zeros to store * * Exit: * *************************************************************************/ void hfmemzer ( PVOID fpb, ULONG cb ) { memset(fpb, '\00', cb); }
/*************************************************************************
* * NctoFo - extract file offset from NC * * Purpose: * Extracts the file offset for a minascii file, and returns it as a long. * * Entry: * nc = context number * * Exit: * returns file offset * *************************************************************************/ ULONG NctoFo ( ULONG nc ) { nc = nc & 0x0000FFFF; nc *= 4; return nc; }
/*************************************************************************
* * combineNc - combine a minascii file offset and fdb handle into nc. * * Purpose: * Combines a minascii file offset and fdb memory handle into an NC. If the * file offset is 0xffffffff, we return zero. * * Entry: * offset = long file offset * mh = fdb mem handle * * Exit: * returns NC (DX = mem handle, AX = filepos/4), or 0L if offset==FFFFFFFF * *************************************************************************/ nc pascal combineNc ( ULONG offset, mh mh ){ nc ncRet = {0,0}; if (offset = 0xFFFFFFFF) { return ncRet; } ncRet.mh = mh; ncRet.cn = offset/4; return ncRet; }
/*************************************************************************
* * toupr - convert char to upper case * * Purpose: * * Entry: * chr = character * * Exit: * returns upper case character * *************************************************************************/ char toupr ( char chr ){ return (char)toupper(chr); }
/*************************************************************************
*kwPtrBuild - Build table of pointers to keywords. *void pascal near kwPtrBuild(uchar far *fpTable, ushort tsize) * *Purpose: * Builds a table of pointers to the keyword strings in the passed string array. * The table is built in the first 4k of the passed buffer. The strings are * assummed to start immediately thereafter. * *Entry: * fpTable - pointer to string table * tsize - size, in bytes, of strings * *Exit: * none * *******************************************************************************/ void kwPtrBuild ( PVOID fpTable, USHORT tsize ) { PBYTE fpStr = (PBYTE)fpTable + 1024 * sizeof (PVOID); PBYTE *fpTbl = fpTable; while (tsize > 0) { UCHAR sSize = (UCHAR)(*fpStr) + (UCHAR)1; *fpTbl++ = fpStr; tsize -= sSize; fpStr += sSize; } }
|