home *** CD-ROM | disk | FTP | other *** search
/ Windows Undocumented File Formats / Windows Undocumented File Formats.img / CHAP4 / HLPDUMP2.C next >
Encoding:
C/C++ Source or Header  |  1997-07-21  |  33.3 KB  |  1,131 lines

  1. /**********************************************************************
  2.  *
  3.  * PROGRAM: HLPDUMP2.C
  4.  *
  5.  * PURPOSE: A dump program that lets you view internal WinHelp files
  6.  *
  7.  * Copyright 1997, Mike Wallace and Pete Davis
  8.  *
  9.  * Chapter 4, Windows Help File Format, from Undocumented Windows
  10.  * File Formats, published by R&D Books, an imprint of Miller Freeman, Inc.
  11.  *
  12.  **********************************************************************/
  13.  
  14. #define MEM_DEBUG 1
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <time.h>
  19. #include <conio.h>
  20. #include <ctype.h>
  21. #include <limits.h>
  22. #include <string.h>
  23. #include "winhelp.h"
  24. #include "hlpdump2.h"
  25.  
  26. #define HLP_DEBUG 1
  27.  
  28. #define CHECK_SIGNATURE(is, shouldbe) \
  29.          {if (is != shouldbe) { \
  30.             printf("Signature should be %x, but is %x\n"); \
  31.             return;} }
  32.              
  33.  
  34. /* Tells us if a particular bit is set or not */
  35. #define BITSET(bitmap, bit) \
  36.         ((bitmap & (1 << bit)) ? 1 : 0 )
  37.  
  38. /* Sum of set bits in a byte + 8 */
  39. #define BYTESTOREAD(bitmap) \
  40.         ( BITSET(bitmap, 0) + BITSET(bitmap, 1) + \
  41.           BITSET(bitmap, 2) + BITSET(bitmap, 3) + \
  42.           BITSET(bitmap, 4) + BITSET(bitmap, 5) + \
  43.           BITSET(bitmap, 6) + BITSET(bitmap, 7) + 8 )
  44.  
  45.  
  46. /***************************************************
  47.   Loads the HFSFileHeader
  48. ****************************************************/
  49.  
  50. long LoadHeader(FILE *HelpFile) 
  51. {
  52.   HFSFILEHEADER fileHeader;
  53.   
  54.   fread(&fileHeader, sizeof(HFSFILEHEADER), 1, HelpFile);
  55.   
  56. #ifdef HLP_DEBUG
  57.   printf("DEBUG -> LoadHeader()\n");
  58.   printf("File plus Header: %ld\n", fileHeader.FilePlusHeader);
  59.   printf("File size: %ld\n", fileHeader.FileSize);
  60.   printf("File type: 0x%02x\n\n", fileHeader.FileType);
  61. #endif
  62.   
  63.   return fileHeader.FileSize;
  64. }
  65.  
  66.  
  67. /*************************************************
  68.   Decompresses the data using Microsoft's LZ77
  69. derivative (called Zeck Compression)
  70. **************************************************/ 
  71.  
  72. long Decompress(FILE *HelpFile, long CompSize, char *Buffer) {
  73.  
  74. long InBytes = 0;        /* How many bytes read in                    */
  75. long OutBytes = 0;       /* How many bytes written out                */
  76. BYTE BitMap, Set[16];    /* Bitmap and bytes associated with it       */
  77. long NumToRead;          /* Number of bytes to read for next group    */
  78. long counter, Index;     /* Going through next 8-16 codes or chars    */
  79. long Length, Distance;   /* Code length and distance back in 'window' */
  80. char *CurrPos;           /* Where we are at any given moment          */
  81. char *CodePtr;           /* Pointer to back-up in LZ77 'window'       */
  82.  
  83.  
  84.   CurrPos = Buffer;
  85.  
  86.   /* Go through until we're done */
  87.   while (InBytes < CompSize) {
  88.  
  89.     /* Get BitMap and data following it */
  90.     BitMap = (BYTE) fgetc(HelpFile);
  91.     NumToRead = BYTESTOREAD(BitMap);
  92.  
  93.     /* If we're trying to read more than we've 
  94.        got left, only read what we have left.  */
  95.     NumToRead = (CompSize - InBytes) < NumToRead ? 
  96.                                     CompSize-InBytes : NumToRead;
  97.  
  98.     fread(Set, 1, (int) NumToRead, HelpFile);    
  99.     InBytes += NumToRead + 1;
  100.  
  101.     /* Go through and decode data */
  102.     for (counter = 0, Index = 0; counter < 8; counter++) {
  103.  
  104.       /* It's a code, so decode it and copy the data */
  105.       if (BITSET(BitMap, counter)) {
  106.         Length = ((Set[Index+1] & 0xF0) >> 4) + 3;
  107.         Distance = ((Set[Index+1] & 0x0F) << 8) + Set[Index] + 1;
  108.         CodePtr = CurrPos - Distance;
  109.  
  110.         /* Copy data from 'window' */
  111.         while (Length) {
  112.           *CurrPos++ = *CodePtr++;
  113.           OutBytes++;
  114.           Length--;
  115.         } 
  116.         Index += 2;
  117.       } 
  118.       else {
  119.         *CurrPos++ = Set[Index++];
  120.         OutBytes++;
  121.       }
  122.     } /* for */
  123.   } /* while  */
  124.   return OutBytes;
  125.  
  126.  
  127. /***************************************************
  128.   Performs a Hex/ASCII dump of an HFS file.
  129. ****************************************************/
  130.  
  131. void HexDumpData(FILE *HelpFile, long fileSize) 
  132. {
  133.   char           Buffer[16];
  134.   long           counter;
  135.   long           BytesToPrint, Index;
  136.  
  137.   printf("Offset                   Hex Values                           Ascii\n");
  138.   printf("-------------------------------------------------------------------------\n");
  139.  
  140.   for (counter = 0; counter < fileSize; counter+=16) {
  141.  
  142.     printf("0x%08lX: ", counter);
  143.  
  144.     /* If this is the last line, how many bytes are in it? */
  145.     BytesToPrint = ((fileSize - counter) > 16) ? 16 : (fileSize - counter);
  146.     fread(Buffer, BytesToPrint, 1, HelpFile);
  147.  
  148.     /* Dump Hex */
  149.     for (Index=0; Index < BytesToPrint; Index++)
  150.       printf("%02X ", (BYTE) Buffer[Index]);
  151.  
  152.     /* If last line, fill in blanks */
  153.     for (Index=0; Index < 16-BytesToPrint; Index++) 
  154.       printf("   ");
  155.  
  156.     /* Dump Ascii */
  157.     for (Index=0; Index < BytesToPrint; Index++) 
  158.       putchar( isprint( Buffer[Index] ) ? Buffer[Index] : '.' );
  159.  
  160.     putchar('\n');
  161.   }
  162.  
  163.   free(Buffer);
  164. }
  165.  
  166.  
  167. /***************************************************
  168.   Performs a Hex/ASCII dump of an HFS file.
  169. ****************************************************/
  170.  
  171. void HexDumpFile(FILE *HelpFile, long FileStart) 
  172. {
  173.   long           fileSize;
  174.  
  175.   fseek(HelpFile, FileStart, SEEK_SET);
  176.  
  177.   fileSize = LoadHeader(HelpFile);  
  178.   
  179.   printf("File Size: 0x%08lX\n\n", fileSize);
  180.   
  181.   HexDumpData(HelpFile, fileSize);
  182. }
  183.  
  184. /***************************************************
  185.   Dumps the |SYSTEM info
  186. ****************************************************/
  187.  
  188. void SystemDump(FILE *HelpFile, long FileStart) 
  189. {
  190.  
  191.   char            HelpFileTitle[33];
  192.   SYSTEMREC       SystemRec;
  193.   long            CurrentLocation;
  194.   struct tm       *TimeRec;
  195.   SECWINDOW       *SWin;     /* Secondary Window record */
  196.   long            fileSize;
  197.   SYSTEMHEADER    SysHeader;
  198.  
  199.   fseek(HelpFile, FileStart, SEEK_SET);
  200.  
  201.   fileSize = LoadHeader(HelpFile);
  202.  
  203.   fread(&SysHeader, sizeof(SysHeader), 1, HelpFile);
  204.   printf("|SYSTEM Dump\n\n\n");
  205.  
  206.   /* Figure out Version and Revision */
  207.   if (SysHeader.Revision == 0x15) printf("HC.EXE  3.10 Help Compiler used\n");
  208.   else if (SysHeader.Revision == 0x21) printf("HCW.EXE. 4.00 or MVC.EXE\n");
  209.  
  210.   printf("\nVersion: %d\nRevision: %d\n", SysHeader.Version, SysHeader.Revision);
  211.   printf("Flag: 0x%04X  - ",SysHeader.Flags);
  212.  
  213.   /* Determine compression, if any. */
  214.   if (SysHeader.Flags == NO_COMPRESSION) printf("No compression\n");
  215.   else if (SysHeader.Flags & COMPRESSION_HIGH) printf("High Compression\n");
  216.   else printf("Unknown Compression: 0x%02x\n", SysHeader.Flags);
  217.  
  218.   TimeRec=localtime(&SysHeader.GenDate);
  219.   printf("Help File Generated: %s", asctime(TimeRec));
  220.  
  221.   /* If 3.0 get title */
  222.   CurrentLocation=12;
  223.   if (SysHeader.Revision == 0x0F) {
  224.      fgets(HelpFileTitle, 33, HelpFile);
  225.      printf("Help File Title: %s\n", HelpFileTitle);
  226.   }
  227.  
  228.   /* Else, get 3.1 System records */
  229.   else {
  230.     while (CurrentLocation < fileSize) {
  231.  
  232.       /* Read in system record and SystemRec data */
  233.       fread(&SystemRec, 4, 1, HelpFile);
  234.       SystemRec.RData = malloc(SystemRec.DataSize);
  235.       if (SystemRec.RData == NULL)
  236.       {
  237.         printf("Allocation of SystemRec.RData failed.");
  238.         return;
  239.       }
  240.       fread(SystemRec.RData, SystemRec.DataSize, 1, HelpFile);
  241.       CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  242.  
  243.       switch(SystemRec.RecordType) 
  244.       {
  245.         case 0x0001:  printf("Help File Title: %s\n", SystemRec.RData);
  246.           break;
  247.  
  248.         case 0x0002:  printf("Copyright Notice: %s\n", SystemRec.RData);
  249.           break;
  250.  
  251.         case 0x0003:  printf("Contents ID: 0x%04X\n", (long) *SystemRec.RData);
  252.           break;
  253.  
  254.         case 0x0004:  printf("Macro Data: %s\n",SystemRec.RData);
  255.           break;
  256.  
  257.         case 0x0005:  printf("Icon in System record\n");
  258.           break;
  259.  
  260.         case 0x0006:  printf("\nSecondary window:\n");
  261.                       SWin = (SECWINDOW *)SystemRec.RData;
  262.                       printf("Flag: %d\n", SWin->Flags);
  263.                       if (SWin->Flags & WSYSFLAG_TYPE) 
  264.                         printf("Type: %s\n", SWin->Type);
  265.                       if (SWin->Flags & WSYSFLAG_NAME) 
  266.                         printf("Name: %s\n", SWin->Name);
  267.                       if (SWin->Flags & WSYSFLAG_CAPTION) 
  268.                         printf("Caption: %s\n", SWin->Caption);
  269.                       if (SWin->Flags & WSYSFLAG_X) 
  270.                         printf("X: %d\n", SWin->X);
  271.                       if (SWin->Flags & WSYSFLAG_Y) 
  272.                         printf("Y: %d\n", SWin->Y);
  273.                       if (SWin->Flags & WSYSFLAG_WIDTH)
  274.                         printf("Width: %d\n", SWin->Width);
  275.                       if (SWin->Flags & WSYSFLAG_HEIGHT)
  276.                         printf("Height: %d\n", SWin->Height);
  277.                       if (SWin->Flags & WSYSFLAG_MAXIMIZE) 
  278.                         printf("Maximize Flag: %d\n", SWin->Maximize);
  279.                       if (SWin->Flags & WSYSFLAG_RGB) 
  280.                         printf("RGB Foreground Colors Set\n");
  281.                       if (SWin->Flags & WSYSFLAG_RGBNSR)
  282.                         printf("RGB For Non-Scrollable Region Set\n");
  283.                       if (SWin->Flags & WSYSFLAG_TOP) 
  284.                         printf("Secondary Window is always On Top\n");
  285.           break;
  286.  
  287.         case 0x0008:  printf("Citation: %s\n", SystemRec.RData);
  288.           break;
  289.  
  290.         case 0x000A:  printf("\nContents File: %s\n\n",SystemRec.RData);
  291.           break;                                                 
  292.           
  293.         default:      printf("\nUnknown record type: 0x%04X\n",SystemRec.RecordType);
  294.                       /* Back-up and hex-dump the data */
  295.                       fseek(HelpFile, 
  296.                             ftell(HelpFile) - SystemRec.DataSize, 
  297.                             SEEK_SET);
  298.                       HexDumpData(HelpFile, SystemRec.DataSize);
  299.       } /* switch */
  300.      
  301.       free(SystemRec.RData);
  302.  
  303.     } /* while */
  304.   } /* else */
  305. } /* SysDump */
  306.  
  307.  
  308. /***************************************************
  309.   Dumps the |FONT file
  310. ****************************************************/
  311.  
  312. void FontDump(FILE *HelpFile, long FileStart) 
  313. {
  314.   FONTHEADER      FontHdr;
  315.   FONTDESCRIPTOR  FontDesc;
  316.   char            AFont[32];
  317.   long            FontStart, CurrLoc;
  318.   long            fileSize;
  319.   long            counter;
  320.   long            NameLen;
  321.  
  322.   /* Go to the FONT file and get the headers */
  323.   fseek(HelpFile, FileStart, SEEK_SET);
  324.   fileSize = LoadHeader(HelpFile);
  325.  
  326.   fread(&FontHdr, sizeof(FontHdr), 1, HelpFile);
  327.  
  328.   printf("|FONTS\n\n Number Fonts: %d\n",FontHdr.NumFonts);
  329.   printf("Font #  -  Font Name\n");
  330.  
  331.  
  332.   /* Font names are 20 chars prior to Winhelp 4.0 */
  333.   /* In WinHelp 4.0, they are 32 characters.      */
  334.   if (SysInfo.Revision == 0x15)
  335.   {
  336.     NameLen = 20;
  337.   }
  338.   else
  339.   {
  340.     NameLen = 32;
  341.   }
  342.  
  343.   /* Keep track of start of fonts */
  344.   FontStart = ftell(HelpFile);
  345.   for (counter = 0; counter < (long) FontHdr.NumFonts; counter++) 
  346.   {
  347.     fread(AFont, NameLen, 1, HelpFile);
  348.     printf(" %3d    -  %s\n", counter, AFont);
  349.   }
  350.  
  351.   /* Go to Font Descriptors. Don't actually need this, because we're
  352.      there, but wanted to show how to get there using the offset.    */
  353.   fseek(HelpFile, FontStart + (long)(FontHdr.DescriptorsOffset) - sizeof(FontHdr), SEEK_SET); 
  354.   printf("\nNum Font Descriptors: %d\n", FontHdr.NumDescriptors);
  355.   printf("Default Descriptor: %d\n\n", FontHdr.DefDescriptor);
  356.   printf("Attributes: n=none  b=bold  i=ital  u=undr  s=strkout  d=dblundr  C=smallcaps\n\n");
  357.   printf("Font Name                        PointSize  Family   FG RGB      BG RGB   Attr\n");
  358.   printf("------------------------------------------------------------------------------\n");
  359.  
  360.   for (counter = 0; counter < (long) FontHdr.NumDescriptors; counter++) {
  361.     fread(&FontDesc, sizeof(FontDesc), 1, HelpFile);
  362.     CurrLoc = ftell(HelpFile);
  363.     fseek(HelpFile, FontStart + (NameLen * FontDesc.FontName), SEEK_SET);
  364.     fread(AFont, NameLen, 1, HelpFile);
  365.     fseek(HelpFile, CurrLoc, SEEK_SET);
  366.       
  367.     /* write out info on Font descriptor */
  368.     printf("%-32s    %4.1f    ", AFont, (float)(FontDesc.HalfPoints / 2));
  369.     switch (FontDesc.FontFamily) {
  370.       case FAM_MODERN: printf("Modern");
  371.                           break;
  372.  
  373.       case FAM_ROMAN:  printf("Roman ");
  374.                        break;
  375.  
  376.       case FAM_SWISS:  printf("Swiss ");
  377.                        break;
  378.  
  379.       case FAM_SCRIPT: printf("Script");
  380.                        break;
  381.  
  382.       case FAM_DECOR:  printf("Decor ");
  383.                        break;
  384.  
  385.       default:         printf("0X%02X ", FontDesc.FontFamily);
  386.                        break;
  387.     } /* Switch */
  388.     printf(" 0X%08lX  ",RGB(FontDesc.SRRGB.rgbRed,
  389.                             FontDesc.SRRGB.rgbGreen,
  390.                             FontDesc.SRRGB.rgbBlue));
  391.     printf("0X%08lX    ",RGB(FontDesc.NSRRGB.rgbRed,
  392.                              FontDesc.NSRRGB.rgbGreen,
  393.                              FontDesc.NSRRGB.rgbBlue));
  394.  
  395.     if (FontDesc.Attributes == 0) putchar('n');
  396.     if (FontDesc.Attributes & FONT_BOLD) putchar('b');
  397.     if (FontDesc.Attributes & FONT_ITAL) putchar('i');
  398.     if (FontDesc.Attributes & FONT_UNDR) putchar('u');
  399.     if (FontDesc.Attributes & FONT_STRK) putchar('s');
  400.     if (FontDesc.Attributes & FONT_DBUN) putchar('d');
  401.     if (FontDesc.Attributes & FONT_SMCP) putchar('C');
  402.     printf("\n");
  403.  
  404. #ifdef HLP_DEBUG
  405.     printf("Unknown = %d\n", FontDesc.Unknown);
  406. #endif
  407.   }
  408. }
  409.  
  410.  
  411. /***************************************************
  412.   Dumps the |CONTEXT file
  413. ****************************************************/
  414.  
  415. void ContextDump(FILE *HelpFile, long FileStart) 
  416. {
  417.   long            count;
  418.   long            CurrPage, FirstPageLoc;
  419.   long            TopicOffset, HashValue;
  420.   BTREEHEADER     BTreeHdr;
  421.   BTREELEAFHEADER CurrNode;
  422.  
  423.   /* Go to the TTLBTREE file and get the headers */
  424.   fseek(HelpFile, FileStart, SEEK_SET);
  425.   LoadHeader(HelpFile);
  426.   fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
  427.  
  428.   /* Save the current location */
  429.   FirstPageLoc = ftell(HelpFile);
  430.    
  431.   fseek(HelpFile, 
  432.         FirstPageLoc+(BTreeHdr.RootPage * BTreeHdr.PageSize), 
  433.         SEEK_SET);
  434.  
  435.   printf("# Context Hash Values in |CONTEXT %lu\n\n", BTreeHdr.TotalBtreeEntries);
  436.   CurrPage = BTreeHdr.FirstLeaf;
  437.  
  438.   do 
  439.   {
  440.     fseek(HelpFile, 
  441.           FirstPageLoc+(CurrPage * BTreeHdr.PageSize), 
  442.           SEEK_SET);
  443.     fread(&CurrNode, 8, 1, HelpFile);
  444.     for(count = 1; count <= CurrNode.NEntries; count++) 
  445.     {
  446.       fread(&HashValue, sizeof(HashValue), 1, HelpFile);
  447.       fread(&TopicOffset, sizeof(TopicOffset), 1, HelpFile);
  448.       printf("Topic Offset:0x%08lX  Hash Value: 0x%08lX\n", TopicOffset, HashValue);
  449.     }
  450.     CurrPage = CurrNode.NextPage;
  451.   } while(CurrPage != -1);
  452. }
  453.  
  454.  
  455. /***************************************************
  456.   Prints a Phrase from the Phrase table.
  457. ****************************************************/
  458. void PrintPhrase(long PhraseNum) 
  459. {
  460.   short *Offsets;
  461.   char  *p;
  462.  
  463.   p = Phrases+Offsets[PhraseNum];
  464.   while (p < Phrases + Offsets[PhraseNum + 1])
  465.     putchar(*p++);
  466. }
  467.  
  468.  
  469. /***************************************************
  470.   Dumps the Keyword B-Tree
  471. ****************************************************/
  472.  
  473. void KWBTreeDump(FILE *HelpFile, long FileStart) 
  474. {
  475.   char            Keyword[80], c;
  476.   long            count, Index;
  477.   long            CurrPage, FirstPageLoc;
  478.   long            KeywordOffset;
  479.   long            KeywordCount;
  480.   BTREEHEADER     BTreeHdr;
  481.   BTREELEAFHEADER CurrNode;
  482.  
  483.   /* Go to the KWBTREE file and get the headers */
  484.   fseek(HelpFile, FileStart, SEEK_SET);
  485.   LoadHeader(HelpFile);
  486.   fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
  487.  
  488.   /* Save the current location */
  489.   FirstPageLoc = ftell(HelpFile);
  490.    
  491.   fseek(HelpFile, 
  492.         FirstPageLoc+(BTreeHdr.RootPage * BTreeHdr.PageSize), 
  493.         SEEK_SET);
  494.  
  495.   printf("# Keywords - %lu\n\n", BTreeHdr.TotalBtreeEntries);
  496.   CurrPage = BTreeHdr.FirstLeaf;
  497.  
  498.   do 
  499.   {
  500.     fseek(HelpFile, 
  501.           FirstPageLoc+(CurrPage * BTreeHdr.PageSize), 
  502.           SEEK_SET);
  503.     fread(&CurrNode, 8, 1, HelpFile);
  504.     for(count = 1; count <= CurrNode.NEntries; count++) 
  505.     {
  506.       Index = 0;
  507.       while(c = (char) fgetc(HelpFile))
  508.         Keyword[Index++] = c;
  509.         
  510.       Keyword[Index] = 0;
  511.       fread(&KeywordCount, sizeof(KeywordCount), 1, HelpFile);
  512.       fread(&KeywordOffset, sizeof(KeywordOffset), 1, HelpFile);
  513.  
  514.       printf("Offset: 0x%08lX  Count: %d  Keyword: %s\n", 
  515.                           KeywordOffset, KeywordCount, Keyword);
  516.     }
  517.     CurrPage = CurrNode.NextPage;
  518.   } while(CurrPage != -1);
  519. }
  520.  
  521.  
  522. /***************************************************
  523.   Dumps the Keyword Data file
  524. ****************************************************/
  525.  
  526. void KWDataDump(FILE *HelpFile, long FileStart) 
  527. {
  528.   long fileSize;
  529.   long nIndex;
  530.   long Offset;
  531.   
  532.   /* Go to the KWDATA file and get the headers */
  533.   fseek(HelpFile, FileStart, SEEK_SET);
  534.   fileSize = LoadHeader(HelpFile);
  535.           
  536.   printf("Dumping Keyword Data File\n\n");
  537.   /* Go through all keyword offsets (fileSize / 4) */
  538.   for (nIndex = 0; nIndex < (fileSize / sizeof(Offset)); nIndex++)
  539.   {
  540.     fread(&Offset, sizeof(Offset), 1, HelpFile);
  541.     printf("Index: %d   Offset: 0x%08lX\n", nIndex, Offset);
  542.   }
  543. }
  544.  
  545.  
  546. /***************************************************
  547.   Dumps the Keyword Map file
  548. ****************************************************/
  549.  
  550. void KWMapDump(FILE *HelpFile, long FileStart) 
  551. {
  552.   long      fileSize;
  553.   long      nIndex;
  554.   WORD      nKWMaps;
  555.   KWMAPREC  kwMap;
  556.   
  557.   /* Go to the KWMAP file and get the headers */
  558.   fseek(HelpFile, FileStart, SEEK_SET);
  559.   fileSize = LoadHeader(HelpFile);
  560.           
  561.   printf("Dumping Keyword Map\n\n");
  562.   
  563.   fread(&nKWMaps, sizeof(nKWMaps), 1, HelpFile);
  564.   
  565.   /* Go through all keyword offsets (fileSize / 4) */
  566.   for (nIndex = 0; nIndex < (int) nKWMaps; nIndex++)
  567.   {
  568.     fread(&kwMap, sizeof(KWMAPREC), 1, HelpFile);
  569.     printf("Index: %d   First Keyword 0x%08lX    Leaf Page#: %05u\n", 
  570.                           nIndex, kwMap.FirstRec, kwMap.PageNum);
  571.   }
  572. }
  573.  
  574.  
  575. /***************************************************
  576.   Dumps the Topic Titles B-Tree
  577. ****************************************************/
  578.  
  579. void TTLDump(FILE *HelpFile, long FileStart) 
  580. {
  581.   char            Title[80], c;
  582.   long            count, Index;
  583.   long            CurrPage, FirstPageLoc, TopicOffset;
  584.   BTREEHEADER     BTreeHdr;
  585.   BTREELEAFHEADER CurrNode;
  586.  
  587.   /* Go to the TTLBTREE file and get the headers */
  588.   fseek(HelpFile, FileStart, SEEK_SET);
  589.   LoadHeader(HelpFile);
  590.   fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
  591.  
  592.   /* Save the current location */
  593.   FirstPageLoc = ftell(HelpFile);
  594.    
  595.   fseek(HelpFile, 
  596.         FirstPageLoc+(BTreeHdr.RootPage * BTreeHdr.PageSize), 
  597.         SEEK_SET);
  598.  
  599.   printf("# Titles in |TTLBTREE %lu\n\n", BTreeHdr.TotalBtreeEntries);
  600.   CurrPage = BTreeHdr.FirstLeaf;
  601.  
  602.   do 
  603.   {
  604.     fseek(HelpFile, 
  605.           FirstPageLoc+(CurrPage * BTreeHdr.PageSize), 
  606.           SEEK_SET);
  607.     fread(&CurrNode, 8, 1, HelpFile);
  608.     for(count = 1; count <= CurrNode.NEntries; count++) 
  609.     {
  610.       fread(&TopicOffset, sizeof(TopicOffset), 1, HelpFile);
  611.       Index = 0;
  612.       while(c = (char) fgetc(HelpFile))
  613.         Title[Index++] = c;
  614.         
  615.       Title[Index] = 0;
  616.  
  617.       printf("Topic Offset:0x%08lX  Title: %s\n", TopicOffset, Title);
  618.     }
  619.     CurrPage = CurrNode.NextPage;
  620.  
  621.   } while(CurrPage != -1);
  622. }
  623.  
  624.  
  625. /***************************************************
  626.   Dumps the |Phrases file
  627. ****************************************************/
  628.  
  629. void PhrasesDump(FILE *HelpFile, long FileStart) 
  630. {
  631.   long  nOuterIndex, nInnerIndex;
  632.   WORD  start, len;
  633.   
  634.   PhrasesLoad(HelpFile, FileStart);
  635.  
  636.   printf("Phrase#     Phrase\n");
  637.   for (nOuterIndex = 0; nOuterIndex < NumPhrases; nOuterIndex++)
  638.   {
  639.     start = (WORD) PhrOffsets[nOuterIndex] ;
  640.     len = PhrOffsets[nOuterIndex + 1] - start;
  641.     printf(" %5d     ", nOuterIndex + 1);
  642.     for (nInnerIndex = 0; nInnerIndex < (int) len; nInnerIndex++)
  643.     {
  644.       printf("%c", (Phrases[start + nInnerIndex]));
  645.     }
  646.     printf("\n");
  647.   }
  648. }
  649.  
  650.  
  651. /***************************************************
  652.   Loads the |SYSTEM info
  653. ****************************************************/
  654.  
  655. int SysLoad(FILE *HelpFile, long FileStart) 
  656. {
  657.   fseek(HelpFile, FileStart, SEEK_SET);
  658.  
  659.   LoadHeader(HelpFile);
  660.   
  661.   fread(&SysInfo, sizeof(SYSTEMHEADER), 1, HelpFile);
  662.   
  663. #ifdef HLP_DEBUG
  664.   printf("DEBUG -> SysLoad()\n");
  665.   printf("Magic: 0x%02x\n", SysInfo.Magic);
  666.   printf("Version: 0x%02x\n", SysInfo.Version);
  667.   printf("Revision: 0x%02x\n", SysInfo.Revision);
  668.   printf("Flags: 0x%04x\n\n", SysInfo.Flags);
  669. #endif
  670.  
  671.   if (SysInfo.Magic != SYS_MAGIC)
  672.   {
  673.     return 0;
  674.   }
  675.   
  676.   return 1;
  677. }
  678.  
  679.  
  680. /***************************************************
  681.   Loads the compression phrases
  682. ****************************************************/
  683.  
  684. void PhrasesLoad(FILE *HelpFile, long FileStart) 
  685. {
  686.   PHRASEHEADER  phraseHeader;
  687.   long          FileSize;
  688.   long          DeCompSize;
  689.   
  690.   fseek(HelpFile, FileStart, SEEK_SET);
  691.   FileSize = LoadHeader(HelpFile);
  692.  
  693.   fread(&phraseHeader, sizeof(phraseHeader), 1, HelpFile);
  694.   
  695.   if (SysInfo.Flags != NO_COMPRESSION)
  696.   {
  697.     if ((PhrOffsets = malloc(phraseHeader.PhrasesSize + 
  698.                             (phraseHeader.NumPhrases + 1) * 2 + 10)) == NULL)
  699.     {
  700.       printf("Unable to allocate space for Phrases.\n");
  701.       return;
  702.     }
  703.  
  704.     /* Assign Phrases to  where the comrpessed phrases are */   
  705.     Phrases = (char*) PhrOffsets + fread(PhrOffsets, 
  706.                                          2, 
  707.                                          phraseHeader.NumPhrases + 1, 
  708.                                          HelpFile);
  709.  
  710.     DeCompSize = Decompress(HelpFile,
  711.                             FileSize - (sizeof(phraseHeader) +
  712.                             2 * (phraseHeader.NumPhrases + 1)),
  713.                             Phrases);
  714.     if (DeCompSize != (long) phraseHeader.PhrasesSize)
  715.     {
  716.       printf("Warning, Phrases did not decompress to the proper size.\n");
  717.     }
  718.   }
  719.   else
  720.   {
  721.     if ((PhrOffsets = malloc(phraseHeader.PhrasesSize + 
  722.                             (phraseHeader.NumPhrases + 1) * 2 + 10)) == NULL)
  723.     {
  724.       printf("Unable to allocate space for Phrases.\n");
  725.       return;
  726.     }
  727.     /* Back up four bytes if phrases aren't compressed */
  728.     /* because PhrasesSize field doesn't exist.        */
  729.     fseek(HelpFile, -4, SEEK_CUR);
  730.     fread(PhrOffsets, FileSize - 4, 1, HelpFile);
  731.   }
  732.   /* Reset Phrases to be equal to PhrOffsets */
  733.   Phrases = (char *) PhrOffsets;  
  734.   NumPhrases = phraseHeader.NumPhrases;
  735. }
  736.  
  737.  
  738. /***************************************************
  739.   Finds an HFS File by traversing the HFS b-tree
  740.   Note: This is the only place I actually traverse
  741.   the b-tree instead of cycling through the leaf
  742.   pages. I put this in specifically to show how to
  743.   traverse the b-tree, since speed isn't a real
  744.   concern for HelpDump.
  745. ****************************************************/
  746.  
  747. char FindFile(FILE *HelpFile, char* filename, long* offset)
  748. {
  749.   BTREEHEADER       HFSHeader;
  750.   BTREEINDEXHEADER* HFSIndexHeader;
  751.   BTREELEAFHEADER*  HFSLeafHeader;
  752.   long              HFSStart;
  753.   short*            pNextPage;
  754.   char*             buffer;
  755.   char*             currPtr;
  756.   long              nKeys, nFiles;
  757.   char              found = 0;
  758.   long              currLevel = 1;
  759.  
  760.   /* Go to the HFS and read the header. */
  761.   fseek(HelpFile, HelpHeader.HFSLoc, SEEK_SET);
  762.   LoadHeader(HelpFile);
  763.   fread(&HFSHeader, sizeof(HFSHeader), 1, HelpFile);
  764.  
  765.   /* Allocate space for read buffer */
  766.   buffer = malloc(HFSHeader.PageSize);
  767.   if (buffer == NULL)
  768.   {
  769.     printf("Unable to allocate space for buffer.\n");
  770.     return found;
  771.   }
  772.   HFSIndexHeader = (BTREEINDEXHEADER*) buffer;
  773.  
  774.   HFSStart = ftell(HelpFile);
  775.   
  776.   /* Advance to root page */
  777.   fseek(HelpFile, 
  778.         (HFSHeader.RootPage * HFSHeader.PageSize) + HFSStart, 
  779.         SEEK_SET);
  780.       
  781.   /* If there's only one page, then it must be a leaf */
  782.   if (HFSHeader.TotalPages > 1)
  783.   {
  784.     /* Traverse b-tree looking for the key for the leaf page */  
  785.     while (!found)
  786.     {
  787.       /* Read in the page */
  788.       fread(buffer, HFSHeader.PageSize, 1, HelpFile);
  789.       currPtr = buffer + sizeof(BTREEINDEXHEADER);
  790.       pNextPage = (int *) currPtr;
  791.  
  792.       currPtr += sizeof(int);
  793.       
  794.       /* Go through all keys in the page */
  795.       for (nKeys = 0; nKeys < HFSIndexHeader->NEntries; nKeys++)
  796.       {
  797.         /* If filename is less than key, this is our page. */
  798.         if (strcmp(filename, currPtr) < 0)
  799.         {
  800.           break;
  801.         }
  802.         else
  803.         {
  804.           /* Advance to the next page# */
  805.           while (*currPtr)
  806.             currPtr++;
  807.           currPtr++;
  808.           pNextPage = (int *) currPtr;
  809.           currPtr += sizeof(int);
  810.         }
  811.       }
  812.       
  813.       /* Advance to next page */
  814.       fseek(HelpFile, 
  815.             (*pNextPage * HFSHeader.PageSize) + HFSStart, 
  816.             SEEK_SET);
  817.       
  818.       /* If this is the last index page */
  819.       /* then pNextPage points to a     */
  820.       /* leaf page                      */
  821.       if (currLevel == HFSHeader.nLevels - 1)
  822.       {
  823.         found = 1;
  824.       }
  825.       currLevel++;
  826.     }
  827.   }
  828.   
  829.   fread(buffer, HFSHeader.PageSize, 1, HelpFile);
  830.   HFSLeafHeader = (BTREELEAFHEADER*) buffer;
  831.   currPtr = buffer + sizeof(BTREELEAFHEADER);
  832.  
  833.  
  834.   found = 0;
  835.   
  836.   /* Loop through all files in this page */
  837.   for (nFiles = 0; nFiles < HFSLeafHeader->NEntries; nFiles++)
  838.   {
  839.     if (strcmp(filename, currPtr))
  840.     {  
  841.       /* Advance to the file offset */
  842.       while (*currPtr)
  843.         currPtr++;
  844.       
  845.       /* Move past the null and file offset */
  846.       /* to next file                       */
  847.       currPtr += 5;    
  848.     }
  849.     else
  850.     {
  851.       /* Save the offset of the file */
  852.       while (*currPtr)
  853.         currPtr++;
  854.       currPtr++;
  855.       
  856.       *offset = (long) *((long*) currPtr);
  857.       found = 1;
  858.       break;
  859.     }
  860.   }
  861.  
  862.   /* TRUE if file was found, FALSE if it wasn't */
  863.   return found;
  864. }
  865.  
  866.  
  867. /***************************************************
  868.   DumpFile
  869. ****************************************************/
  870.  
  871. void DumpFile(FILE *HelpFile)
  872. {
  873.   long fileOffset;
  874.   char fileName[255];
  875.   
  876.   /* For many files we need to know information about    */
  877.   /* The system, so we'll load the system data info here */
  878.   /* if it's available                                   */
  879.   SysLoaded = 0;
  880.   strcpy(fileName, "|SYSTEM");
  881.   if (FindFile(HelpFile, fileName, &fileOffset))
  882.   {
  883.      SysLoaded = (char) SysLoad(HelpFile, fileOffset);
  884.   }
  885.  
  886.   /* If it's not a version 3 help file */
  887.   /* then we can't handle it.          */
  888.   if (SysInfo.Version != 0x03)
  889.   {
  890.     printf("Warning: Not a version 3 help file. Version is %d.\n",
  891.            SysInfo.Version);
  892.   }
  893.   
  894.   /* If it's version 3, but not revision   */
  895.   /* 0x15 or 0x21, we also can't handle it */
  896.   if (SysInfo.Revision != 0x15 && SysInfo.Revision != 0x21)
  897.   {
  898.     printf("Revision not 0x15 or 0x21. Revision is 0x%02x.\n",
  899.            SysInfo.Revision);
  900.   }
  901.   
  902.   /* If we're reading the |TOPIC file, then we need to */
  903.   /* pre-load the phrases from the |Phrases file       */  
  904.   if (!strcmp(HFSFileToRead, "|TOPIC") ||
  905.       !strcmp(HFSFileToRead, "TOPIC"))
  906.   {
  907.     strcpy(fileName, "|Phrases");
  908.     if (FindFile(HelpFile, fileName, &fileOffset))
  909.     {
  910.       PhrasesLoad(HelpFile, fileOffset);
  911.     }
  912.   }
  913.  
  914.   strcpy(fileName, HFSFileToRead);
  915.   if (!FindFile(HelpFile, fileName, &fileOffset))
  916.   {
  917.     /* Append a "|" character to the beginning  */
  918.     /* of the filename and try to find the file */
  919.     strcpy(fileName, "|");
  920.     strcat(fileName, HFSFileToRead);
  921.     if (!FindFile(HelpFile, fileName, &fileOffset))
  922.     {
  923.       /* File not found */
  924.       printf("Error: Unable to find HFS file %s or %s\n",
  925.              HFSFileToRead, fileName);
  926.       return;
  927.     }
  928.   }
  929.  
  930.   if (!ForceHex)
  931.   {  
  932.     if (!strcmp(fileName, "|SYSTEM"))
  933.       SystemDump(HelpFile, fileOffset);
  934.     else if (!strcmp(fileName, "|TTLBTREE"))
  935.       TTLDump(HelpFile, fileOffset);
  936.     else if (!strcmp(fileName, "|CONTEXT"))
  937.       ContextDump(HelpFile, fileOffset);
  938.     else if (!strcmp(fileName, "|FONT"))
  939.       FontDump(HelpFile, fileOffset);
  940.     else if (!strcmp(fileName, "|KWBTREE"))
  941.       KWBTreeDump(HelpFile, fileOffset);
  942.     else if (!strcmp(fileName, "|AWBTREE"))
  943.       KWBTreeDump(HelpFile, fileOffset);
  944.     else if (!strcmp(fileName, "|KWDATA"))
  945.       KWDataDump(HelpFile, fileOffset);
  946.     else if (!strcmp(fileName, "|AWDATA"))
  947.       KWDataDump(HelpFile, fileOffset);
  948.     else if (!strcmp(fileName, "|KWMAP"))
  949.       KWMapDump(HelpFile, fileOffset);
  950.     else if (!strcmp(fileName, "|Phrases"))
  951.       PhrasesDump(HelpFile, fileOffset);
  952.     else 
  953.       HexDumpFile(HelpFile, fileOffset);
  954.   }
  955.   else
  956.   {
  957.     HexDumpFile(HelpFile, fileOffset);
  958.   }  
  959. }
  960.  
  961. /***************************************************
  962.   List out all the HFS files in a .HLP file
  963. ****************************************************/
  964.  
  965. void ListFiles(FILE *HelpFile)
  966. {
  967.   BTREEHEADER       HFSHeader;
  968.   BTREELEAFHEADER*  HFSLeafHeader;
  969.   long              HFSStart;
  970.   char*             buffer;
  971.   char*             currPtr;
  972.   long              nIndex, nFiles;
  973.   long*             offset;
  974.  
  975.   /* Go to the HFS and read the header. */
  976.   fseek(HelpFile, HelpHeader.HFSLoc, SEEK_SET);
  977.   LoadHeader(HelpFile);
  978.   fread(&HFSHeader, sizeof(HFSHeader), 1, HelpFile);
  979.  
  980.   CHECK_SIGNATURE(HFSHeader.Signature, 0x293B);
  981.  
  982. #ifdef HLP_DEBUG  
  983.   printf("DEBUG -> ListFiles()\n");
  984.   printf("B-Tree Page Size %d\n", HFSHeader.PageSize);
  985.   printf("B-Tree First Leaf %d\n", HFSHeader.FirstLeaf);
  986.   printf("B-Tree Num. Levels %d\n", HFSHeader.nLevels);
  987.   printf("B-Tree Total Pages %d\n", HFSHeader.TotalPages);
  988.   printf("B-Tree Total # Entries %ld\n", HFSHeader.TotalBtreeEntries);
  989. #endif 
  990.   /* Start of the HFS b-tree pages */
  991.   HFSStart = ftell(HelpFile);
  992.  
  993.   /* Go to the first leaf page of the HFS */  
  994.   fseek(HelpFile, HFSHeader.FirstLeaf * HFSHeader.PageSize, SEEK_CUR);
  995.  
  996.   /* Allocate space for read buffer and read first page */
  997.   buffer = malloc(HFSHeader.PageSize);
  998.   if (buffer == NULL)
  999.   {
  1000.     printf("Unable to allocate space for buffer.\n");
  1001.     return;
  1002.   }
  1003.   HFSLeafHeader = (BTREELEAFHEADER*) buffer;
  1004.   
  1005.   printf("\nHFS Filename                 Offset\n");
  1006.   printf("-----------------------------------\n");
  1007.   
  1008.   /* Loop through all HFS leaf pages */
  1009.   for (nIndex = 0; nIndex < HFSHeader.TotalPages; nIndex++)
  1010.   {
  1011.     fread(buffer, HFSHeader.PageSize, 1, HelpFile);
  1012.  
  1013.     currPtr = buffer + sizeof(BTREELEAFHEADER);
  1014.  
  1015.     /* Loop through all files in this page */
  1016.     for (nFiles = 0; nFiles < HFSLeafHeader->NEntries; nFiles++)
  1017.     {
  1018.  
  1019.       /* Print filename */
  1020.       printf("%-30s", currPtr);
  1021.     
  1022.       /* Advance to next filename */
  1023.       while (*currPtr)
  1024.         currPtr++;
  1025.       currPtr++;
  1026.       
  1027.       offset = (long*) currPtr;
  1028.       /* print offset to file */
  1029.       printf("0x%08lX\n", *offset);
  1030.  
  1031.       /* Move past the file offset to next file */
  1032.       currPtr += 4;    
  1033.     }
  1034.   }
  1035. }
  1036.  
  1037. /***************************************************
  1038.   Check to make sure it's a help file. Then
  1039.   either dump the HFS directory or dump an HFS
  1040.   file.
  1041. ****************************************************/
  1042.  
  1043. void HelpDump(FILE *HelpFile) 
  1044. {
  1045.   fread(&HelpHeader, sizeof(HelpHeader), 1, HelpFile);
  1046.   if (HelpHeader.MagicNumber != HF_MAGIC) 
  1047.   {
  1048.     printf("Fatal Error:\n");
  1049.     printf("  Not a valid WinHelp file!\n");
  1050.     return;
  1051.   }
  1052.  
  1053.   if (ReadHFSFile) 
  1054.      DumpFile(HelpFile);
  1055.   else
  1056.      ListFiles(HelpFile);
  1057.  
  1058. }
  1059.  
  1060.  
  1061. /***************************************************
  1062.   Show usage
  1063. ****************************************************/
  1064.  
  1065. void Usage()
  1066. {
  1067.   printf("HLPDUMP2  (version 2.0 of Help Dump)\n");
  1068.   printf("By Pete Davis and Mike Wallace               Copyright 1997\n\n");
  1069.   printf("Usage: HLPDUMP2 helpfile[.hlp] [hfsfilename] [/H]\n\n");
  1070.   printf("where:\n");
  1071.   printf("  helpfile    - name of .HLP/.GID/.ANN/.BMK file to open\n");
  1072.   printf("  hfsfilename - name of HFS file to read\n");
  1073.   printf("  /H          - force a hex dump\n\n");
  1074.   printf("note: Do not include the pipe '|' character in the hfsfilename.\n");
  1075. }
  1076.  
  1077.  
  1078. /***************************************************
  1079.  Entry point to HLPDUMP2
  1080. ****************************************************/
  1081.  
  1082. int main(int argc, char *argv[])
  1083. {
  1084.   char filename[_MAX_PATH];
  1085.   FILE *HelpFile;
  1086.  
  1087.   if (argc < 2) 
  1088.   {
  1089.     Usage();
  1090.     return EXIT_FAILURE;
  1091.   }
  1092.   ReadHFSFile = 0;
  1093.   if (argc >= 3) 
  1094.   {
  1095.     strcpy(HFSFileToRead, argv[2]);
  1096.     ReadHFSFile = 1;
  1097.   }
  1098.  
  1099.   /* Are we forcing a hex dump? */
  1100.   ForceHex = 0;
  1101.   if (argc == 4)
  1102.   {
  1103.  
  1104.   if (stricmp(argv[3], "/H"))
  1105.     {
  1106.       printf("Error: Argument 3 unrecognized\n");
  1107.       return EXIT_FAILURE;
  1108.     }
  1109.     ForceHex = 1;
  1110.  
  1111.   }
  1112.   strcpy(filename, argv[1]);
  1113.   strupr(filename);
  1114.   if (!strchr(filename, '.')) 
  1115.     strcat(filename, ".HLP");
  1116.  
  1117.   if ((HelpFile = fopen(filename, "rb")) == NULL) 
  1118.   {
  1119.     printf("%s does not exist!", filename);
  1120.     return EXIT_FAILURE;
  1121.   }
  1122.  
  1123.   printf("Dumping %s\n\n", filename);
  1124.   HelpDump(HelpFile);
  1125.   fclose(HelpFile);
  1126.  
  1127.   return EXIT_SUCCESS;
  1128.   
  1129. }
  1130.