home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2002 September
/
PCWorld_2002-09_cd.bin
/
Software
/
Vyzkuste
/
helpdeco
/
HELPDECO.C
< prev
next >
Wrap
Text File
|
1997-01-27
|
178KB
|
6,105 lines
/*
HELPDECO - Utility-Programm zum Zerlegen von Windows Hilfedateien
HELPDECO - utility program to dissect Windows help files
HELPDECO zerlegt HLP-Hilfedateien von Windows 3.0, 3.1, 3.11 und '95 und
viele MVB-Dateien des Multimedia-Viewers in alle für den jeweiligen
Hilfecompiler HC30, HC31, HCP, HCW, HCRTF, WMVC, MMVC oder MVC zum
erneuten Zusammenbau erforderlichen Dateien. Dazu gehören:
HPJ - die Projektdatei, als Parameter für den Hilfecompiler anzugeben
MVP - die Multimediaprojektdatei, als Parameter für den MM-Compiler
RTF - die Textdatei mit dem gesamten Hilfetext und allen Fußnoten
PH - die Phrasen-Datei (wie sie auch vom Hilfecompiler erzeugt wird)
ICO - ein eventuell der Hilfedatei zugeordnetes Icon
BMP/WMF/SHG/MRB - alle Bilder in Dateien mit passendem Format
Baggage - alle als Baggage in der Hilfedatei enthaltenen Dateien
HELPDECO dissects HLP help files of Windows 3.0, 3.1, 3.11, and '95 and
many MVB multi media viewer titles into all files required for a rebuild
using the appropriate help compiler HC30, HC31, HCP, HCW, HCRTF, WMVC,
MMVC or MVC:
HPJ - help project file, use as parameter when calling help compiler
MVP - multi media project file, parameter for multi media help compiler
RTF - text file containing whole content of help file and all footnotes
PH - phrases file (same as produced by help compiler)
ICO - icon of help file if embedded
BMP/WMF/SHG/MRB - embedded pictures in appropriate format
Baggage - all baggage files contained in help file
HELPDECO wird von der MS-DOS Kommandozeile aus mit dem Namen der zu
bearbeitenden Datei, eventuell dem Namen einer internen Datei und
eventuellen Optionen aufgerufen. HELPDECO läuft von der Kommandozeile
von Windows 95 oder Windows NT als 32-bit Applikation zur Bearbeitung
größerer Hilfedateien.
Call HELPDECO from MS-DOS command line. Supply name of help file to use,
optional name of internal file, and options if appropriate.
HELPDECO runs from Windows 95 or Windows NT command line as 32-bit
application to handle larger help files.
HELPDECO
Zeigt Benutzungshinweise
Displays usage
HELPDECO helpfilename
Zerlegt die Hilfedatei in alle zum erneuten Zusammenbau benötigten Dateien.
Diese Dateien werden im aktuellen (möglichst leeren) Verzeichnis abgelegt.
Existierende Dateien werden ohne Rückfrage überschrieben wenn die Option /y
angegeben wird.
Decompiles help file into all sources needed for a rebuild. All files are
created in current directory (should be empty). Existing files will be
overwritten without asking if option /y was specified.
Hinter dem Kommando können durch Leerzeichen abgesetzte Optionen angeben
werden:
Options may be appended to the command, separated using blanks, as follows:
/m
kann verwendet werden, um das Durchsuchen von macros nach Topicnamen zu
verhindern, wenn dabei Probleme auftreten. Hilfecompiler wird Warnung 4131
melden.
May be used to stop parsing macros for topic names. Help compiler will emit
Warning 4131.
/b
kann verwendet werden, um das Auflösen von Browse-Sequenzen zu verhindern,
wenn dabei Probleme auftreten. Hilfequelltextdatei enthält dann keine +
Fußnoten.
May be used to stop resolving browse sequences. Help source file than
contains no + footnotes.
/g
Schaltet das zeitintensive Raten von Kontextnamen aus. Anzuwenden wenn
HELPDECO 'no context ids found' meldet oder man nicht an lesbaren
Kontextnamen interessiert ist. Die Funktionsweise von HELPDECO wird dadurch
nicht beeinträchtigt.
Zum Raten versucht HELPDECO aus Kapitelüberschriften und Schlüsselwörten
die Kontextnamen zu rekonstruieren, was bei einigen Hilfedateien sehr gut,
bei anderen gar nicht funktioniert, je nach Hilfedateierstellungswerkzeug
oder Arbeitsmethode des Hilfeautors.
Turns off time consuming guessing of context names. Applicable when
HELPDECO reports 'no context ids found' or when you are not interested in
legible context ids. This doesn't affect functionality.
During guessing HELPDECO tries to reconstruct context ids from topic titles
and keywords. This may produce good results on some help files and no result
at all on others, depending on autoring tool used or working method of help
author.
/i
Wenn man sehen möchte, welche Kontextnamen HELPDECO errät...
If you want to see which context ids are guessed by HELPDECO...
/hprefix
HELPDECO versucht aus Kapitelüberschriften und Schlüsselwörtern Kontextnamen
zu erraten, wobei auch idh_ oder helpid_ vorangestellt werden.
Wenn die Hilfedatei mit anderen bekannten Prefixen erstellt wurde, kann
die Tabelle durch die Option /h prefix erweitert werden.
HELPDECO tries to guess context ids from topic titles and keywords, possibly
prefixed by idh_ or helpid_. If the help file was created using other
prefixes, they may be added to the table using the /h prefix option.
/a [annotationfilename.ANN]
Fügt zusätzlich alle Anmerkungen aus der angegebenen Anmerkungsdatei als
Anmerkungen des Benutzers ANN in die RTF-Datei ein. Fehlt der annotation-
filename, verwendet HELPDECO helpfilename.ANN dafür.
Adds all annotations from annotationfile as annotations of user ANN into
RTF file. Default annotationfilename is helpfilename.ANN.
/s n
Zerteilt die Ausgabe alle n Seiten in eine eigene RTF Datei, z.B. zerlegt
HELPDECO helpdemo /s 100
die Datei helpdemo.hlp und erzeugt RTF-Dateien mit den Namen helpdem1.rtf,
helpdem2.rtf,.. die jeweils höchstens 100 Seiten enthalten.
Splits output every n pages into a separate RTF file. For example
HELPDECO helpdemo /s 100
decompiles helpdeco.hlp and creates different RTF files helpdem1.rtf,
helpdem2.rtf,.. each containing no more than 100 topics.
HELPDECO helpfilename /r
Erzeugt aus der Hilfedatei eine RTF-Datei, die von WinWord geladen dasselbe
Aussehen hat wie die von WinHelp angezeigten Hilfeseiten. Damit kann eine
Hilfedatei komplett gedruckt oder weiterverarbeitet werden. Zusätzlich
Option /n angeben, wenn an Kapitelgrenzen kein Seitenwechsel stattfinden soll.
Converts help file into RTF file of same appearance if loaded into WinWord
as if displayed by WinHelp. To print or work with complete content. Specify
additional option /n, if no page breaks should separate topics.
HELPDECO helpfilename /c
Erzeugt aus der Hilfedatei eine *.CNT-Datei für WinHlp32, die alle Kapitel
mit Überschriften in der Reihenfolge enthält, in der sie in der Hilfedatei
auftreten. Die Datei muß dann mit HCW 4.00 oder einem Texteditor in eine
hierarchische Struktur überarbeitet werden.
Generates a *.CNT file used by WinHlp32, containing all chapters that have
titles assigned in the order they appear in the helpfile. This file should
then be edited using HCW 4.00 or any text editor into a hierarchical order.
HELPDECO helpfilename /e
Zeigt alle Referenzen auf externe Hilfedateien.
Lists all references to external help files.
HELPDECO helpfilename /e /f
Zeigt alle Referenzen auf externe Hilfedateien und die Titel der Topics in
denen sie auftraten.
Lists all references to external help files and titles of topics that
contained these references.
HELPDECO helpfilename /p
Prüft Referenzen auf externe Hilfedateien. Die referenzierten Hilfedateien
müssen für HELPDECO zugreifbar sein. Dieser Aufruf erzeugt keine neuen
Dateien und modifiziert keine existierenden. Fehler werden auf stdout (den
Bildschirm) geschrieben.
Checks references to external help files. Referenced help file need to be
available to HELPDECO. This call doesn't produce any new files and doesn't
modify existing files. Errors are reported to stdout (screen).
HELPDECO helpfilename /d
Zeigt das interne Inhaltsverzeichnis der Hilfedatei. Es kann auch eine
*.MVB,*.M??,*.ANN,*.CAC,*.AUX Datei anstelle der *.HLP-Datei angegeben
werden.
Displays internal directory of help file. You may supply a *.MVB,*.M??,
*.ANN,*.CAC,*.AUX file instead of a *.HLP file.
HELPDECO helpfilename /x
Zeigt das interne Inhaltsverzeichnis als HexDump
Displays hex dump of internal directory
HELPDECO helpfilename "internalfilename"
Zeigt die genannte interne Datei in einem passenden Format an, soweit die
interne Datei anzeigbar ist, sonst als HexDump. Sie können die Ausgabe in
eine Datei umleiten durch anhängen von >outfile.
Displays internal file in appropriate format if known, else hex dump. You
may redirect output into a file using >outfile.
HELPDECO helpfilename "internalfilename" /x
Zeigt die genannte interne Datei als HexDump
Displays hex dump of internal file
HELPDECO helpfilename "internalfilename" /x /t offset
Zeigt die genannte interne Datei als HexDump beginnend bei Position
offset, der als Dezimalzahl oder mit vorangestelltem 0x als Hexadezimal-
zahl akzeptiert wird.
Displays hex dump of internal file starting at offset, which may be
specified in decimal or preceeded with 0x in hex.
HELPDECO helpfilename "internalfilename" filename
Exportiert die genannte interne Datei in filename
Exports internal file into filename
*.ANN, *.CAC, *.AUX
Diese Dateien sind auch wie Hilfedateien formatiert, HELPDECO kann aber nur
verwendet werden, um ihr Inhaltsverzeichnis anzuzeigen oder um einzelne
Dateien anzuzeigen und zu exportieren.
These files are formatted like helpfiles, but HELPDECO can only be used to
display their internal directory or display or export internal files.
HELPDECO wurde erstellt von / was written by
Manfred Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany
CIS 100326,2776
Wenn Sie weitere Fragen, Probleme (oder Antworten!) haben, können Sie
durch eine EMail an 100326.2776@compuserve.com mit mir in Kontakt treten.
If you have more questions, problems (or answers!), please feel free to
send me an EMail to 100326.2776@compuserve.com
HELPDECO basiert auf HELPDUMP von Pete Davis veröffentlicht in:
HELPDECO is based upon HELPDUMP from Pete Davis published in:
The Windows Help File Format, Dr. Dobbs Journal, Sep/Oct 1993
Thanks to Holger Haase, who did a lot of work on picture file formats.
And thanks to Jürgen Müller for pointing out 32-bit differences.
Thanks to Bent Lynggaard for the information on help file free lists
and his contribution on context id guessing.
HELPFILE.TXT enthält eine Beschreibung des Windows Hilfedateiformats
wie HELPDECO es versteht und weiterer Dateiformate wie MRB/SHG.
See HELPFILE.TXT for a description of the Windows help file format
as parsed by HELPDECO and related file formats like MRB/SHG.
Die neueste Version von HELPDECO befindet sich stets in:
The newest public version of HELPDECO is always available at:
CompuServe: Dr. Dobbs Journal DDJFOR Undocumented Corner HELPDCxx.ZIP
InterNet: ftp://gmutant.wrlc.org/pub/winhelp
HELPDECO ist Freeware. Der Einsatz erfolgt auf eigene Gefahr. Kein
Programmteil darf kommerziell verwendet werden. Für das Kopieren dürfen
keine Gebühren verlangt werden (Sharewarehandel Finger weg).
HELPDECO is freeware. Use at your own risk. No part of the program may be
used commercially. No fees may be charged on distributing the program
(shareware distributors keep off).
Die Verwendung von Inhalten zerlegter Hilfedateien kann eine Verletzung
des Urheberrechtes bedeuten.
The use of parts of decompiled help files may constitute a violation of
copyright law.
Version 2.1:
macros may contain rtf meta characters
Win95 topic names may contain (nearly) all characters
0x8000 in SWin->Maximize allowed
ForeHelp creates PhrIndex/PhrImage _and_ _empty_ Phrases file
HCRTF complained about > footnotes before # footnotes
macro parsing changed again
Version 2.0:
Guess context ids from titles and keywords based on idea of Bent Lynggaard
Recompiled 16 bit EXE without register calling convention. BC++ 3.1 bug.
Doesn't print last (stray) topic of HC30 help files
Can list entry points into this help file (option /l)
Version 1.9: faster & better than ever...
changed TopicPos, TopicOffset, Keyword maintenance
changed unhash to 40 bit integer arithmetic
fixed keyword footnotes [Bent Lynggaard]
no [ALIAS] in MVP files
no hidden text in option /r RTF files
some changes in font and stylesheet handling
corrects rounding error of HC31 on negative values
handles non-underlined topic jumps
Version 1.8: used some spare days to clean up the to-do list...
better tracking of TopicOffset during decompilation
lists and checks references to external files, shows referencing topics
can add annotations from .ANN file to decompiled .RTF file
fixed bug in handling of pictures containing JumpId-macro hotspots
changed parsing of macros (3rd attempt to guess what Microsoft did)
fixed bug in popup/jump to external file / secondary window
fixed bug in > footnote / |VIOLA internal file handling
fixed bug in keyword assignment
now removes LZ77 compression from exported SHGs/MRBs
recreates Win 95 (HCW 4.00) [MACROS] section from internal |Rose file
32 bit version available
handles LANGUAGE, [CHARTAB] and [GROUP] section of media view files
Version 1.7
removed unneccessary output statement
Version 1.6 can now check references to external help files plus:
duplicate macro names preceeding picture hotspot info skipped
does not write Win95 commands to multi-media help project files
changed unhash to circumvent Microsoft-C++ float rounding error
handles keywords defined inside topic text
Version 1.5
fixed static on buffer of TopicName function (affected HC30 files)
Version 1.4 fixes some bugs reported by different users:
buffer overflow in expanding LZ77&RunLen (byPacked 3) images fixed
embedded images {bmxwd} larger than 32k supported
extract topic names from jump into external file if no file specified
handles more phrases on HCRTF generated (Win95) help files
Windows 3.1 (HC31) |Phrases always Zeck compressed
LinkData2 buffer enlarged 1 byte to store trailing NUL character
Version 1.3
parses examples of {bmc} etc. statements contained in help text correctly
can now generate a *.CNT content file for Windows 95 / WinHlp32
Microsoft C: ctype macros (isalnum/isprint) don't work with signed char
Version 1.2 fixes some severe bugs introduced in version 1.1 and:
tells you which help compiler to use
collects multiple keyword footnotes into single lines
handles \r\n in COPYRIGHT
converts SPC-macro (but only in [CONFIG] section)
does not generate duplicate MAP-statements if possible
{button} and {mci,mci_left,mci_right} commands supported
[BITMAP]-section in HCRTF help files irritated transparent bitmaps
Version 1.1 now supports more features of Win95/HCRTF 4.00/WinHlp32:
Supports LCID, CHARSET, AUTO-SIZE HEIGHT, CNT, INDEX_SEPARATORS
Additional Win95 Macros (to extract original topic names)
[CONFIG:n] of Win95 supported (internal file |CFn)
Secondary windows with > footnote supported (internal file |VIOLA)
Transparent bitmaps supported (bmct,bmlt,bmrt)
Expanded internal limits as HCRTF allows larger items
Now does RunLen compressed device dependend bitmaps
Bugs in handling of metafiles removed
Bug in placement of pack(1) removed
Parsing of macros changed (is it really better now ?)
*/
#include "helpdeco.h"
/* neccessary compiler options for 16 bit version using Borland C/C++:
// bcc -ml -K -Os -p helpdeco.c helpdec1.c
// Don't compile using Register Calling Convention in BC3.1: compiler bug.
// To compile 32 bit version using Microsoft VC4.0 create a new workspace
// for a WIN32 console application, insert HELPDECO.C and HELPDEC1.C into
// it and compile. Use /KNOWEAS 16 bit version as stub to create dual-mode
// application.
// Byte align ! Portable to little endian machines only.
*/
FILEREF *external;
char HelpFileName[81];
char name[_MAX_FNAME];
char ext[_MAX_EXT];
FILE *AnnoFile;
HASHREC *hashrec;
int hashrecs=0;
BROWSE *browse;
int browses;
int browsenums;
long scaling;
int rounderr;
START *start;
int starts;
BOOL lzcompressed,Hall;
BOOL before31,after31;
BOOL win95;
BOOL mvp,multi;
BOOL warnings,missing;
long *Topic;
int Topics; /* 16 bit: max. 16348 Topics */
GROUP *group;
int groups;
CONTEXTREC *ContextRec;
int ContextRecs; /* 16 bit: max. 8191 Context Records */
ALTERNATIVE *alternative;
int alternatives;
BOOL overwrite=FALSE;
BOOL exportLZ77=FALSE;
BOOL extractmacros=TRUE;
BOOL guessing=TRUE;
long guessed=0L;
BOOL listtopic=FALSE;
BOOL nopagebreak=FALSE;
BOOL resolvebrowse=TRUE;
BOOL reportderived=FALSE;
BOOL checkexternal=FALSE;
BOOL exportplain=FALSE;
#define MAXKEYWORDS (64<<(sizeof(int)*2)) /* 16 bit: 1024, 32 bit: 16348 */
int NextKeywordRec,KeywordRecs;
KEYWORDREC *KeywordRec;
TOPICOFFSET NextKeywordOffset;
char helpcomp[13];
char HelpFileTitle[81];
char TopicTitle[256];
char *Phrases;
unsigned int *PhraseOffsets;
unsigned int PhraseCount;
long TopicFileLength;
int TopicBlockSize; /* 2k or 4k */
int DecompressSize; /* 4k or 16k */
char buffer[4096];
char keyword[512];
char index_separators[40]=",;";
char *extension;
int extensions=0;
/* index into bmpext: bit 0=multiresolution bit 1=bitmap, bit 2=metafile, bit 3=hotspot data, bit 4=embedded, bit 5=transparent */
char *bmpext[]={"???","MRB","BMP","MRB","WMF","MRB","MRB","MRB","SHG","MRB","SHG","MRB","SHG","MRB","SHG","MRB"};
char **stopwordfilename;
int stopwordfiles;
char **fontname;
int fontnames;
unsigned char DefFont;
FONTDESCRIPTOR *font;
int fonts;
unsigned char lookup[]={0,3,1,2,4,5}; /* to translate font styles */
struct { unsigned char r,g,b; } color[128];
int colors;
char **windowname;
int windownames;
BOOL NotInAnyTopic;
int TopicsPerRTF;
BOOL lists['z'-'0'+1];
BOOL keyindex['z'-'0'+1];
static signed char table[256]=
{
'\x00', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7', '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF',
'\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF',
'\xF0', '\x0B', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\x0C', '\xFF',
'\x0A', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F',
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F',
'\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0D',
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F',
'\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2A', '\x2B', '\x2C', '\x2D', '\x2E', '\x2F',
'\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', '\x58', '\x59', '\x5A', '\x5B', '\x5C', '\x5D', '\x5E', '\x5F',
'\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6A', '\x6B', '\x6C', '\x6D', '\x6E', '\x6F',
'\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', '\x78', '\x79', '\x7A', '\x7B', '\x7C', '\x7D', '\x7E', '\x7F',
'\x80', '\x81', '\x82', '\x83', '\x0B', '\x85', '\x86', '\x87', '\x88', '\x89', '\x8A', '\x8B', '\x8C', '\x8D', '\x8E', '\x8F',
'\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x98', '\x99', '\x9A', '\x9B', '\x9C', '\x9D', '\x9E', '\x9F',
'\xA0', '\xA1', '\xA2', '\xA3', '\xA4', '\xA5', '\xA6', '\xA7', '\xA8', '\xA9', '\xAA', '\xAB', '\xAC', '\xAD', '\xAE', '\xAF',
'\xB0', '\xB1', '\xB2', '\xB3', '\xB4', '\xB5', '\xB6', '\xB7', '\xB8', '\xB9', '\xBA', '\xBB', '\xBC', '\xBD', '\xBE', '\xBF',
'\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF'
};
char oldtable[256];
unsigned char untable[]={0,'1','2','3','4','5','6','7','8','9','0',0,'.','_',0,0,0,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
char *prefix[]={"","idh_","helpid_",NULL,NULL,NULL,NULL,NULL};
long prefixhash[sizeof(prefix)/sizeof(prefix[0])];
FONTDESCRIPTOR CurrentFont;
long hash(char *name) /* convert 3.1/'95 topic name to hash value */
{
long hash;
unsigned char *ptr;
if(*name=='\0') return 1L;
for(hash=0L,ptr=(unsigned char *)name;*ptr;ptr++)
{
hash=hash*43L+table[*ptr];
}
return hash;
}
char *unhash(unsigned long hash) /* deliver 3.1 context id that fits hash value */
{
static char buffer[15];
int i,j,k;
unsigned long hashlo,divlo,result,mask;
unsigned char hashhi,divhi;
char ch;
i=0;
j=hashrecs;
while(i<j)
{
k=(i+j)/2;
if(hashrec[k].hash<(long)hash)
{
i=k+1;
}
else if(hashrec[k].hash>(long)hash)
{
j=k;
}
else return hashrec[k].name;
}
for(i=0;i<43;i++)
{
buffer[j=14]='\0';
hashlo=hash;
hashhi=i;
while(1)
{
divhi=21;
divlo=0x80000000UL;
result=0UL;
for(mask=0x80000000UL;mask;mask>>=1)
{
if(hashhi>divhi||hashhi==divhi&&hashlo>=divlo)
{
result|=mask;
hashhi-=divhi;
if(divlo>hashlo) hashhi--;
hashlo-=divlo;
}
divlo>>=1;
if(divhi&1) divlo|=0x80000000UL;
divhi>>=1;
}
ch=untable[(int)hashlo];
if(!ch) break;
buffer[--j]=ch;
if(result==0) return buffer+j;
hashlo=result;
}
}
/* should never happen */
error("Can not find a matching string for hash value %08lx",hash);
sprintf(buffer,"HASH%08lx",hash);
return buffer;
}
char *ContextId(unsigned long hash) /* unhash and verify for legal entry point */
{
char *ptr;
int i;
for(i=0;i<ContextRecs;i++)
{
if(ContextRec[i].HashValue==hash)
{
return unhash(hash);
}
}
ptr=unhash(hash);
printf("Help Compiler will issue Warning 4113: Unresolved jump or popup '%s'\n",ptr);
return ptr;
}
void AddTopic(char *TopicName,BOOL derived) /* adds a known topic name to hash decode list */
{
long x;
int i,j,k;
x=hash(TopicName);
i=0;
j=hashrecs;
while(i<j)
{
k=(i+j)/2;
if(hashrec[k].hash<x)
{
i=k+1;
}
else if(hashrec[k].hash>x)
{
j=k;
}
else
{
if(stricmp(TopicName,hashrec[k].name)!=0)
{
if(!hashrec[k].derived)
{
if(!derived) fprintf(stderr,"ContextId %s already defined as %s\n",TopicName,hashrec[k].name);
return;
}
if(derived) return;
free(hashrec[k].name);
hashrec[k].name=my_strdup(TopicName);
}
if(!derived&&hashrec[k].derived)
{
guessed--;
hashrec[k].derived=FALSE;
}
return;
}
}
/* %100 to decrease memory fragmentation */
if(hashrecs%100==0) hashrec=my_realloc(hashrec,(hashrecs+100)*sizeof(HASHREC));
if(i<hashrecs) memmove(hashrec+i+1,hashrec+i,sizeof(HASHREC)*(hashrecs-i));
hashrec[i].name=my_strdup(TopicName);
hashrec[i].derived=derived;
hashrec[i].hash=x;
if(derived) guessed++;
hashrecs++;
}
/* ContextRec is sorted by TopicOffset. Binary search for an entry for
// TopicOffset topic. If more than one entry with identical TopicOffset
// is stored, return index of the first one, if not found return -1 */
int FindContext(long topic)
{
int i,lwb,upb;
lwb=0;
upb=ContextRecs;
while(lwb<upb)
{
i=(lwb+upb)/2;
if(ContextRec[i].TopicOffset>topic)
{
upb=i;
}
else if(ContextRec[i].TopicOffset<topic)
{
lwb=i+1;
}
else
{
while(i>0&&ContextRec[i-1].TopicOffset==topic) i--;
return i;
}
}
return -1;
}
/* Look if buf contains str (caseinsensitive) and update case of str so it is
// matching case appearance in buf and return TRUE, FALSE if not found */
int FindString(unsigned char *buf,int buflen,unsigned char *str,int len)
{
int i,j;
unsigned char ch;
buflen-=len;
for(i=0;i<=buflen;i++)
{
for(j=0;j<len;j++)
{
if(table[ch=buf[j]]!=table[str[j]]) break;
str[j]=ch;
}
if(j==len)
{
return TRUE;
}
buf++;
}
return FALSE;
}
/* Many people and authoring systems create context ids from topic titles,
// either by replacing every illegal char with _ or . or leaving it out,
// sometimes using only part of the topic title or prefixing it with idh_
// or helpid_. This function tries all variants and may find a context id
// matching the hash value.
// And if it doesn't derive a context id, don't bother, unhash delivers
// one matching the hash code. This code is just to please the user.
// Win95 allows for nearly every char to be used as a context ident. */
BOOL Derive(unsigned char *str,unsigned long desiredhash,char *buffer)
{
int i,j,k,l,m,n,o,p,s;
unsigned long hash,h,x,y;
char *ptr;
unsigned char ch;
l=2;
s=strlen(str);
for(i=!win95;i<l;i++) /* three variants what to do with illegal characters */
{ /* but only if an illegal character found (see below) */
for(j=0;prefix[j];j++)
{
k=0;
while(str[k])
{
hash=prefixhash[j];
strcpy(buffer,prefix[j]);
ptr=strchr(buffer,'\0');
for(m=k;str[m];m++)
{
ch=str[m];
if(i)
{
n=oldtable[ch];
if(n==0)
{
if(i==2)
{
n=oldtable[ch='_'];
}
else
{
continue;
}
}
}
else /* Win95 */
{
n=table[ch];
if(n==0) continue;
}
*ptr++=ch;
hash=43L*hash+n;
for(x=1L,o=0;o<6;o++,x*=43L)
{
h=desiredhash-x*hash;
if(h<x)
{
y=x;
p=0;
while(h)
{
y/=43L;
ch=untable[(unsigned char)(h/y)];
if(!ch) break;
h%=y;
ptr[p++]=ch;
}
if(h==0&&(FindString(str,s,ptr,p)||p<3))
{
ptr[p]='\0';
return TRUE;
}
}
}
}
if(i)
{
while(oldtable[ch=str[k]]) k++;
if(ch) l=3;
while((ch=str[k])!='\0'&&!oldtable[ch]) k++;
}
else /* win95 */
{
while(table[ch=str[k]]) k++;
if(ch) l=3;
while((ch=str[k])!='\0'&&!table[ch]) k++;
}
}
}
}
return FALSE;
}
void Guess(char *str,TOPICOFFSET topic)
{
int i,j,k,m;
long hash;
i=FindContext(topic);
if(i!=-1) /* iff there is a # footnote assigned to that topic offset */
{
do
{
hash=ContextRec[i].HashValue;
j=0;
k=hashrecs;
while(j<k)
{
m=(j+k)/2;
if(hashrec[m].hash<hash)
{
j=m+1;
}
else if(hashrec[m].hash>hash)
{
k=m;
}
else break;
}
if(j>=k) if(Derive(str,hash,buffer))
{
if(reportderived) printf("Derived %s\n",buffer);
AddTopic(buffer,TRUE);
}
}
while(++i<ContextRecs&&ContextRec[i].TopicOffset==topic);
}
}
void SysLoad(FILE *HelpFile) /* gets global values from SYSTEM file */
{
SYSTEMRECORD *SysRec;
SYSTEMHEADER SysHdr;
SECWINDOW *SWin;
int i;
char kwdata[10];
char kwbtree[10];
if(!SearchFile(HelpFile,"|SYSTEM",NULL))
{
fputs("Internal |SYSTEM file not found. Can't continue.\n",stderr);
exit(1);
}
my_fread(&SysHdr,sizeof(SysHdr),HelpFile);
before31=SysHdr.Minor<16;
after31=SysHdr.Minor>21;
multi=SysHdr.Minor==27;
lzcompressed=!before31&&(SysHdr.Flags==4||SysHdr.Flags==8);
win95=SysHdr.Minor==33&&!mvp;
windownames=0;
windowname=NULL;
if(before31)
{
DecompressSize=TopicBlockSize=2048;
}
else
{
DecompressSize=0x4000;
if(SysHdr.Flags==8)
{
TopicBlockSize=2048;
}
else
{
TopicBlockSize=4096;
}
}
if(before31)
{
my_gets(HelpFileTitle,33,HelpFile);
}
else
{
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
switch(SysRec->RecordType)
{
case 0x0001:
strlcpy(HelpFileTitle,SysRec->Data,sizeof(HelpFileTitle));
break;
case 0x0006:
SWin=(SECWINDOW *)SysRec->Data;
windowname=my_realloc(windowname,(windownames+1)*sizeof(char *));
windowname[windownames]=NULL;
if(SWin->Flags&WSYSFLAG_NAME)
{
windowname[windownames]=my_strdup(SWin->Name);
}
windownames++;
break;
case 0x000E:
keyindex[SysRec->Data[1]-'0']=TRUE;
break;
}
}
}
for(i='A';i<='z';i++)
{
sprintf(kwdata,"|%cWDATA",i);
sprintf(kwbtree,"|%cWBTREE",i);
if(SearchFile(HelpFile,kwdata,NULL)&&SearchFile(HelpFile,kwbtree,NULL))
{
lists[i-'0']=TRUE;
}
}
}
/*********************************************************************************
GRAPHICS STUFF
==============
ExtractBitmap(..) extracts the graphics stored as |bmXXX in the Windows
HelpFile to separate files. In this version multiple resolution pictures
(with and without hotspots) are saved as MRB's, pictures with hotspots are
converted to the SHG-format, single resolution Bitmaps without hotspots
to standard BMP-format and all Metafiles without hotspots to the Aldus
Placeable Metafile WMF format.
GENERAL NOTES ABOUT |bmXXX
--------------------------
|bmXXX contains one MRB-File. This can be directly recompiled. If hotspots
in the graphics contained in the MRB-File shall be added or changed with
SHED, the MRB-File has to be split into one SHG file for each graphic. This
is very easy as the graphics-data will only be slightly rewrapped. If the
graphics themselves shall be edited with a standard drawing program, the
SHG files have to be converted into bitmaps or metafiles (as flagged in
the SHG). The hotspot data is lost by this step.
MRB-FILES
---------
MRBC takes the input-file(s), converts them to "lp"-Files if needed, adds
a resolution specification if none is given in the file and finally outputs
a "lp"-File with the ending ".MRB". Depending on the given display type
MRBC sets the following resolutions:
| | | x-resolution | y-resolution
display-type | extension | std-extension | (ppi) | (ppi)
-------------+-----------+---------------+--------------+-------------
CGA | ".C*" | ".CGA" | 96 | 48
EGA | ".E*" | ".EGA" | 96 | 72
VGA | ".V*" | ".VGA" | 96 | 96
8514 | ".8*" | ".854" | 120 | 120
SHG-Files
---------
Structure of SHG-Data:
Offset | Size | Ref | Description
----------+------------+-----+-----------------------------------------------
0 | 1 Byte | | always 0x01, maybe a version-number
1 | 1 Word | N | the number of hotspots
3 | 1 DWord | M | size of macro-data (in bytes)
7 | 15xN Bytes | HSD | Hotspot-Data (see below)
7 + 15*N | M Bytes | | Macro-Data (ASCIIZ-Strings)
7+15*N+M | 2*N*ASCIIZ | | Hotspot-Id and Context- or Macro-Name as
| | | ASCIIZ-String
Structure of Hotspot-Data:
Offset | Size | Ref | Description
----------+------------+-----+-------------------------------------------------
0 | 3 Bytes | | Hotspot-Type: 0xE3 0x00 0x00 = jump, visible
| | | 0xE7 0x04 0x00 = jump, invisible
| | | 0xE2 0x00 0x00 = pop-up, visible
| | | 0xE6 0x04 0x00 = pop-up, invisible
| | | 0xC8 0x00 0x00 = macro, visible
| | | 0xCC 0x04 0x00 = macro, invisible
3 | 1 Word | | Left border of hotspot
5 | 1 Word | | Top border of hotspot
7 | 1 Word | | Right border - Left border
9 | 1 Word | | Bottom border - Top border
11 | 1 DWord | | 0x00000000 => nothing
| | | 0x00000001 => this is a macro-hotspot
| | | others => hash-value of the context-string
| | | according to the WinHelp standard
03-06-1995 Holger Haase
**********************************************************************************/
/* compares filename a[.HLP] and filename b[.HLP], returning 0 if match */
int filenamecmp(const char *a,const char *b)
{
char aname[_MAX_FNAME],bname[_MAX_FNAME];
char aext[_MAX_EXT],bext[_MAX_EXT];
int i;
_splitpath(a,NULL,NULL,aname,aext);
_splitpath(b,NULL,NULL,bname,bext);
if(aext[0]=='\0') strcpy(aext,".HLP");
if(bext[0]=='\0') strcpy(bext,".HLP");
i=strcmpi(aname,bname);
if(i) return i;
return strcmpi(aext,bext);
}
/* store external reference in list, checked later */
void StoreReference(char *filename,int type,char *id,long hash)
{
CHECKREC *ptr;
FILEREF *ref;
PLACEREC *place;
for(ref=external;ref;ref=ref->next)
{
if(filenamecmp(filename,ref->filename)==0) break;
}
if(!ref)
{
ref=my_malloc(sizeof(FILEREF)+strlen(filename));
strcpy(ref->filename,filename);
ref->check=NULL;
ref->next=external;
external=ref;
}
for(ptr=ref->check;ptr;ptr=ptr->next)
{
if(ptr->type==type&&ptr->hash==hash) break;
}
if(!ptr)
{
ptr=my_malloc(sizeof(CHECKREC));
ptr->type=type;
ptr->hash=hash;
ptr->id=my_strdup(id);
ptr->here=NULL;
ptr->next=ref->check;
ref->check=ptr;
}
if(listtopic&&TopicTitle[0])
{
place=my_malloc(sizeof(PLACEREC)+strlen(TopicTitle));
strcpy(place->topicname,TopicTitle);
place->next=ptr->here;
ptr->here=place;
}
}
/* validate each entry in list of external references */
void CheckReferences(void)
{
FILEREF *ref;
CHECKREC *ptr;
FILE *f;
BOOL found;
long offset;
int i,n;
CTXOMAPREC CTXORec;
BTREEHEADER BTreeHdr;
BTREENODEHEADER CurrNode;
CONTEXTREC ContextRec;
for(ref=external;ref;ref=ref->next)
{
f=fopen(ref->filename,"rb");
if(!f)
{
printf("%s not found\n",ref->filename);
}
else
{
if(SearchFile(f,NULL,NULL))
{
for(ptr=ref->check;ptr;ptr=ptr->next)
{
found=FALSE;
if(ptr->type==CONTEXT)
{
if(SearchFile(f,"|CTXOMAP",NULL))
{
n=my_getw(f);
for(i=0;i<n;i++)
{
my_fread(&CTXORec,sizeof(CTXORec),f);
if(CTXORec.MapID==ptr->hash) /* hash is context id */
{
found=TRUE;
break;
}
}
}
}
else
{
if(SearchFile(f,"|CONTEXT",NULL))
{
my_fread(&BTreeHdr,sizeof(BTreeHdr),f);
offset=ftell(f);
CurrNode.PreviousPage=BTreeHdr.RootPage;
for(n=1;n<BTreeHdr.NLevels;n++)
{
fseek(f,offset+CurrNode.PreviousPage*(long)BTreeHdr.PageSize,SEEK_SET);
my_fread(&CurrNode,sizeof(BTREEINDEXHEADER),f);
for(i=0;i<CurrNode.NEntries;i++)
{
ContextRec.HashValue=getdw(f);
if(ContextRec.HashValue>ptr->hash) break;
CurrNode.PreviousPage=my_getw(f); /* Page */
}
}
fseek(f,offset+CurrNode.PreviousPage*(long)BTreeHdr.PageSize,SEEK_SET);
my_fread(&CurrNode,sizeof(BTREENODEHEADER),f);
for(i=0;i<CurrNode.NEntries;i++)
{
my_fread(&ContextRec,sizeof(ContextRec),f);
if(ContextRec.HashValue==ptr->hash) found=TRUE;
if(ContextRec.HashValue>=ptr->hash) break;
}
}
}
if(!found)
{
if(ptr->id)
{
printf("%s@%s not found\n",ptr->id,ref->filename);
}
else
{
printf("0x%08lx@%s not found\n",ptr->hash,ref->filename);
}
while(ptr->here)
{
printf(" %s\n",ptr->here->topicname);
ptr->here=ptr->here->next;
}
}
}
}
else
{
printf("%s isn't a valid WinHelp file !\n",ref->filename);
}
fclose(f);
}
}
}
/* list each entry in list of external references */
void ListReferences(void)
{
FILEREF *ref;
CHECKREC *ptr;
hashrecs=0;
for(ref=external;ref;ref=ref->next)
{
for(ptr=ref->check;ptr;ptr=ptr->next)
{
printf("%s ",ref->filename);
switch(ptr->type)
{
case TOPIC:
printf("topic id ");
if(ptr->id)
{
printf("'%s'",ptr->id);
}
else
{
printf("0x%08lx='%s'",ptr->hash,unhash(ptr->hash));
}
break;
case CONTEXT:
printf("[MAP] id ");
if(ptr->id)
{
printf("'%s'",ptr->id);
}
else
{
printf("0x%08lx=(%ld)",ptr->hash,ptr->hash);
}
break;
}
if(ptr->here)
{
printf(" referenced from:");
while(ptr->here)
{
printf("\n %s",ptr->here->topicname);
ptr->here=ptr->here->next;
}
}
putchar('\n');
}
}
}
void unquote(char *ptr)
{
char *dest;
dest=ptr;
do
{
if(*ptr=='\\'&&ptr[1]!='\0')
{
ptr++;
}
}
while((*dest++=*ptr++)!='\0');
}
char *findMatchingChar(char *p) /* searches for matching '`', '"', or '('. */
{
char match = '\'';
if(*p=='"') match = '"';
else if(*p=='(') match = ')';
else if(*p!='`') return p; /* no match for this string */
for(p++;*p!='\0'&&*p!=match;p++)
{
if(*p=='\\'&&p[1]!='\0')
{
p++;
}
else if(*p=='`'||*p=='"'||*p=='(')
{
if(!*(p=findMatchingChar(p))) break; /* error: end of string */
}
}
return p;
}
/* scan macro string for topic names and external references */
BOOL CheckMacroX(char *ptr)
{
static char *macro[]=
{
"AddAccelerator(ssm","AA(ssm",
"AppendItem(sssm","AI(sssm",
"AL(ssts",
"ChangeButtonBinding(sm","CBB(sm",
"CB(ssm",
"CE(sm",
"ChangeItemBinding(sm","CIB(sm",
"ExtInsertItem(sssmss",
"IfThen(mm","IT(mm",
"IfThenElse(mmm","IE(mmm",
"ExecFile(sssm","EF(sssm",
"InsertItem(sssms","II(sssms",
"JumpContext(x","JC(x","JumpContext(fx","JC(fx",
"JumpHash(h","JH(h","JumpHash(fh","JH(fh",
"JumpId(c","JI(c","JumpId(fc","JI(fc",
"KL(ssts",
"MPrintID(c",
"Not(m",
"PopupId(c","PI(c","PopupId(fc","PI(fc",
"PopupContext(x","PC(x","PopupContext(fx","PC(fx",
"PopupHash(h","PopupHash(fh",
"SetContents(fx",
"SE(sssssc",
"SH(sssst",
"UpdateWindow(fc","UW(fc"
};
char *name;
int namelen,parms,i,n,l;
char *parm[20];
while(1)
{
while(*ptr==' ') ptr++;
if(*ptr=='\0') return TRUE;
if(!isalpha(*ptr)) return FALSE;
for(name=ptr,namelen=0;isalnum(*ptr)||*ptr=='_';namelen++) ptr++;
while(*ptr==' ') ptr++;
if(*ptr!='(') return FALSE;
ptr++;
parms=0;
while(1)
{
while(*ptr==' ') ptr++;
parm[parms]=ptr; /* save after skip spaces */
if(*ptr=='`'||*ptr=='"')
{
parm[parms++]=ptr+1; /* advance past quote */
ptr=findMatchingChar(ptr);
if(*ptr=='\0') return FALSE; /* unexpected end of string */
*ptr++='\0'; /* zap terminating quote */
while(*ptr==' '||*ptr=='\t') ptr++;
}
else if(*ptr!=')'&&*ptr!='\0')
{
parm[parms++]=ptr;
while(*ptr!=','&&*ptr!=')'&&*ptr!='\0')
{
if(*ptr=='('||*ptr=='`'||*ptr=='"') /* `, " probably not needed */
{
ptr=findMatchingChar(ptr);
if(*ptr=='\0') return FALSE;
}
ptr++;
}
}
if(*ptr==')') break;
if(*ptr!=',') return FALSE;
*ptr++='\0';
}
*ptr++='\0';
for(i=0;i<sizeof(macro)/sizeof(macro[0]);i++)
{
if(strcspn(macro[i],"(")==namelen&&memicmp(macro[i],name,namelen)==0&&strlen(macro[i]+namelen+1)>=parms) break;
}
if(i<sizeof(macro)/sizeof(macro[0])) /* macro of interest */
{
char *at;
for(n=0;n<parms;n++) unquote(parm[n]);
for(n=0;n<parms;n++)
{
if(macro[i][namelen+1+n]=='m')
{
CheckMacroX(parm[n]); /* recursive */
}
else if(macro[i][namelen+1+n]=='c')
{
if(extractmacros)
{
while(parm[n][0]==' ') parm[n]++;
for(l=strlen(parm[n]);l>0&&parm[n][l-1]==' ';l--) ;
parm[n][l]='\0';
AddTopic(parm[n],FALSE);
}
}
else if(macro[i][namelen+1+n]=='f')
{
at=strchr(parm[n],'>');
if(at)
{
if(filenamecmp(parm[n],name)!=0)
{
if(macro[i][namelen+2+n]=='c')
{
StoreReference(parm[n],TOPIC,parm[n+1],hash(parm[n+1]));
n++;
}
else if(macro[i][namelen+2+n]=='x')
{
StoreReference(parm[n],CONTEXT,parm[n+1],strtoul(parm[n+1],NULL,0));
n++;
}
else if(macro[i][namelen+2+n]=='h')
{
StoreReference(parm[n],TOPIC,parm[n+1],strtoul(parm[n+1],NULL,0));
n++;
}
}
}
}
else if(macro[i][namelen+1+n]=='t')
{
at=strchr(parm[n],'@');
if(at)
{
if(filenamecmp(at+1,name)!=0)
{
*at='\0';
StoreReference(at+1,TOPIC,parm[n],hash(parm[n]));
}
else
{
AddTopic(parm[n],FALSE);
}
}
else
{
AddTopic(parm[n],FALSE);
}
}
}
}
while(*ptr==' ') ptr++;
if(*ptr!=':'&&*ptr!=';') break;
ptr++;
}
return TRUE;
}
void CheckMacro(char *ptr)
{
char *temp;
if(!multi)
{
temp=my_strdup(ptr);
if(!CheckMacroX(temp)) fprintf(stderr,"Bad macro: %s\n",ptr);
free(temp);
}
}
/* check hotspot info of bitmap for topic names and external references and
// extract (write to file) if checkexternal not set, write out first bitmap
// or metafile only (no SHG/MRB) if exportplain (create lookalike rtf) set */
int ExtractBitmap(char *szFilename,MFILE *f)
{
FILE *fTarget;
char *filename;
int type;
unsigned int i,n,j;
unsigned char byType,byPacked;
long l,pos,offset,nextpict,FileStart;
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
APMFILEHEADER afh;
unsigned short *wp;
unsigned short wMagic,mapmode,colors;
unsigned long dwRawSize,dwDataSize,dwHotspotOffset,dwOffsBitmap,dwHotspotSize,dwPictureOffset,xPels,yPels;
FileStart=f->tell(f);
wMagic=GetWord(f);
if(wMagic!=0x506C /* SHG */ &&wMagic!=0x706C /* MRB */)
{
error("Unknown magic 0x%04X (%c%c)",wMagic,wMagic&0x00FF,wMagic>>8);
return 0;
}
fTarget=NULL;
n=GetWord(f);
type=!exportplain&&n>1; /* contains multiple resolutions */
/* do not depend on wMagic, because it is sometimes incorrect */
nextpict=4+4*n;
for(j=0;j<n;j++)
{
f->seek(f,FileStart+4+4*j);
dwOffsBitmap=GetDWord(f);
f->seek(f,FileStart+dwOffsBitmap);
byType=f->get(f); /* type of picture: 5=DDB, 6=DIB, 8=METAFILE */
byPacked=f->get(f); /* packing method: 0=unpacked, 1=RunLen, 2=LZ77, 3=both */
if(byType==6&&byPacked<4||byType==5&&byPacked<2)
{
type|=2; /* contains bitmap */
memset(&bmfh,0,sizeof(bmfh));
memset(&bmih,0,sizeof(bmih));
bmfh.bfType=0x4D42; /* bitmap magic ("BM") */
bmih.biSize=sizeof(bmih);
xPels=GetCDWord(f);
/* HC30 doesn't like certain PelsPerMeter */
if(!before31) bmih.biXPelsPerMeter=(xPels*79L+1)/2;
yPels=GetCDWord(f);
if(!before31) bmih.biYPelsPerMeter=(yPels*79L+1)/2;
bmih.biPlanes=GetCWord(f);
bmih.biBitCount=GetCWord(f);
bmih.biWidth=GetCDWord(f);
bmih.biHeight=GetCDWord(f);
colors=(int)(bmih.biClrUsed=GetCDWord(f));
if(!colors) colors=(unsigned short)1<<bmih.biBitCount;
bmih.biClrImportant=GetCDWord(f);
if(after31&&bmih.biClrImportant==1) type|=0x20; /* contains transparent bitmap */
dwDataSize=GetCDWord(f);
dwHotspotSize=GetCDWord(f);
dwPictureOffset=GetDWord(f);
dwHotspotOffset=GetDWord(f);
if(exportplain||n==1&&(dwHotspotOffset==0L||dwHotspotSize==0L))
{
if(checkexternal) break;
strcat(szFilename,".BMP");
fTarget=my_fopen(szFilename,"wb");
if(fTarget)
{
fwrite(&bmfh,1,sizeof(bmfh),fTarget);
fwrite(&bmih,1,sizeof(bmih),fTarget);
if(byType==6)
{
CopyBytes(f,colors*4L,fTarget);
}
else
{
putdw(0x000000L,fTarget);
putdw(0xFFFFFFL,fTarget);
}
bmfh.bfOffBits=sizeof(bmfh)+sizeof(bmih)+colors*4L;
bmih.biSizeImage=(((bmih.biWidth*bmih.biBitCount+31)/32)*4)*bmih.biHeight;
if(byType==5) /* convert DDB to DIB */
{
long width,length;
unsigned char count,value;
int pad;
width=((bmih.biWidth*bmih.biBitCount+15)/16)*2;
pad=(int)(((width+3)/4)*4-width);
count=value=0;
for(l=0;l<bmih.biHeight;l++)
{
if(byPacked==1)
{
for(length=0;length<width;length++)
{
if((count&0x7F)==0)
{
count=f->get(f);
value=f->get(f);
}
else if(count&0x80)
{
value=f->get(f);
}
putc(value,fTarget);
count--;
}
}
else
{
CopyBytes(f,width,fTarget);
}
if(pad) fwrite(buffer,pad,1,fTarget);
}
}
else
{
DecompressIntoFile(byPacked,f,dwDataSize,fTarget);
}
/* update bitmap headers */
bmfh.bfSize=ftell(fTarget);
fseek(fTarget,0L,SEEK_SET);
fwrite(&bmfh,1,sizeof(bmfh),fTarget);
fwrite(&bmih,1,sizeof(bmih),fTarget);
}
break;
}
}
else if(byType==8&&byPacked<4) /* Windows MetaFile */
{
type|=4; /* contains metafile */
memset(&afh,0,sizeof(afh));
mapmode=GetCWord(f); /* mapping mode */
afh.rcBBox.right=GetWord(f); /* width of metafile-picture */
afh.rcBBox.bottom=GetWord(f); /* height of metafile-picture */
dwRawSize=GetCDWord(f);
dwDataSize=GetCDWord(f);
dwHotspotSize=GetCDWord(f);
dwPictureOffset=GetDWord(f);
dwHotspotOffset=GetDWord(f);
if(exportplain||n==1&&(dwHotspotOffset==0L||dwHotspotSize==0L))
{
if(checkexternal) break;
afh.dwKey=0x9AC6CDD7L;
afh.wInch=2540;
wp=(unsigned short *)&afh;
for(i=0;i<10;i++) afh.wChecksum^=*wp++;
strcat(szFilename,".WMF");
fTarget=my_fopen(szFilename,"wb");
if(fTarget)
{
fwrite(&afh,1,sizeof(afh),fTarget);
DecompressIntoFile(byPacked,f,dwDataSize,fTarget);
}
break;
}
}
else
{
error("Unknown format (%d) or packing method (%d)",byType,byPacked);
break;
}
type|=8; /* contains hotspot info (set before accessing bmpext) */
if(!checkexternal)
{
if(!fTarget)
{
strcat(szFilename,".");
strcat(szFilename,bmpext[type&0x0F]);
fTarget=my_fopen(szFilename,"wb");
if(!fTarget) break;
my_putw(wMagic,fTarget);
my_putw(n,fTarget);
}
fseek(fTarget,4+4*j,SEEK_SET);
putdw(nextpict,fTarget);
fseek(fTarget,nextpict,SEEK_SET);
putc(byType,fTarget);
if(exportLZ77)
{
putc(byPacked,fTarget);
}
else
{
putc(byPacked&1,fTarget);
}
if(byType==8)
{
putcw(mapmode,fTarget); /* mapping mode */
my_putw(afh.rcBBox.right,fTarget); /* width of metafile-picture */
my_putw(afh.rcBBox.bottom,fTarget); /* height of metafile-picture */
putcdw(dwRawSize,fTarget);
}
else
{
putcdw(xPels,fTarget);
putcdw(yPels,fTarget);
putcw(bmih.biPlanes,fTarget);
putcw(bmih.biBitCount,fTarget);
putcdw(bmih.biWidth,fTarget);
putcdw(bmih.biHeight,fTarget);
putcdw(bmih.biClrUsed,fTarget);
putcdw(bmih.biClrImportant,fTarget);
}
pos=ftell(fTarget);
putdw(0,fTarget); /* changed later ! */
putdw(0,fTarget); /* changed later ! */
putdw(0,fTarget); /* changed later ! */
putdw(0,fTarget); /* changed later ! */
if(byType==6) CopyBytes(f,colors*4L,fTarget);
offset=ftell(fTarget);
f->seek(f,FileStart+dwOffsBitmap+dwPictureOffset);
if(exportLZ77)
{
dwDataSize=CopyBytes(f,dwDataSize,fTarget);
}
else
{
dwDataSize=DecompressIntoFile(byPacked&2,f,dwDataSize,fTarget);
}
}
if(dwHotspotSize)
{
f->seek(f,FileStart+dwOffsBitmap+dwHotspotOffset);
if(f->get(f)!=1)
{
fputs("No hotspots\n",stderr);
dwHotspotSize=0L;
}
else
{
unsigned int hotspots,n,j,l;
unsigned long MacroDataSize;
char *ptr;
HOTSPOT *hotspot;
hotspots=GetWord(f);
MacroDataSize=GetDWord(f);
hotspot=my_malloc(hotspots*sizeof(HOTSPOT));
f->read(f,hotspot,sizeof(HOTSPOT)*hotspots);
if(checkexternal)
{
while(MacroDataSize-->0) f->get(f);
}
else
{
putc(1,fTarget);
my_putw(hotspots,fTarget);
putdw(MacroDataSize,fTarget);
fwrite(hotspot,sizeof(HOTSPOT),hotspots,fTarget);
if(MacroDataSize) CopyBytes(f,MacroDataSize,fTarget);
}
for(n=0;n<hotspots;n++)
{
j=StringRead(buffer,sizeof(buffer),f)+1;
l=j+StringRead(buffer+j,sizeof(buffer)-j,f)+1;
if(fTarget) fwrite(buffer,l,1,fTarget);
if(extractmacros) switch(hotspot[n].id0)
{
case 0xC8: /* macro (never seen) */
case 0xCC: /* macro without font change */
CheckMacro(buffer+j);
break;
case 0xE0: /* popup jump HC30 */
case 0xE1: /* topic jump HC30 */
case 0xE2: /* popup jump HC31 */
case 0xE3: /* topic jump HC31 */
case 0xE6: /* popup jump without font change */
case 0xE7: /* topic jump without font change */
if(hash(buffer+j)!=hotspot[n].hash)
{
fprintf(stderr,"Wrong hash %08lx instead %08lx for '%s'\n",hotspot[n].hash,hash(buffer+j),buffer+j);
}
AddTopic(buffer+j,FALSE);
break;
case 0xEA: /* popup jump into external file */
case 0xEB: /* topic jump into external file / secondary window */
case 0xEE: /* popup jump into external file without font change */
case 0xEF: /* topic jump into external file / secondary window without font change */
if(hotspot[n].id1!=0&&hotspot[n].id1!=1&&hotspot[n].id1!=4&&hotspot[n].id1!=6||hotspot[n].id2!=0)
{
}
else
{
filename=strchr(buffer+j,'@');
if(filename) *filename++='\0';
ptr=strchr(buffer+j,'>');
if(ptr) *ptr='\0';
if(filename)
{
StoreReference(filename,TOPIC,buffer+j,hash(buffer+j));
}
else
{
AddTopic(buffer+j,FALSE);
}
break;
}
default:
error("Unknown hotspot %02x %02x %02x X=%u Y=%u W=%u H=%u %08lx,%s,%s",hotspot[n].id0,hotspot[n].id1,hotspot[n].id2,hotspot[n].x,hotspot[n].y,hotspot[n].w,hotspot[n].h,hotspot[n].hash,buffer,buffer+j);
}
}
free(hotspot);
}
}
if(!checkexternal)
{
dwPictureOffset=offset-nextpict;
nextpict=ftell(fTarget);
/* fix up some locations */
fseek(fTarget,pos,SEEK_SET);
putdw((dwDataSize<<1)+1,fTarget);
putdw((dwHotspotSize<<1)+1,fTarget);
putdw(dwPictureOffset,fTarget);
if(dwHotspotSize) putdw(dwPictureOffset+dwDataSize,fTarget);
}
}
if(fTarget) my_fclose(fTarget);
return type;
}
/****************************************************************************
// END OF GRAPHICS STUFF
//**************************************************************************/
char *getbitmapname(unsigned int n) /* retrieve extension of exported bitmap n */
{
static char name[12];
if(n<extensions&&extension[n])
{
sprintf(name,"bm%u.%s",n,bmpext[extension[n]&0x0F]);
}
else if(n==65535U)
{
missing=TRUE;
fputs("There was a picture file rejected on creation of helpfile.\n",stderr);
strcpy(name,"missing.bmp");
}
else /* should never happen */
{
warnings=TRUE;
fprintf(stderr,"Bitmap bm%u not exported\n",n);
sprintf(name,"bm%u.bmp",n);
}
return name;
}
void ListBitmaps(FILE *hpj) /* writes out [BITMAPS] section */
{
int i;
if(hpj&&extensions)
{
fputs("[BITMAPS]\n",hpj);
for(i=0;i<extensions;i++) if(extension[i])
{
fprintf(hpj,"bm%u.%s\n",i,bmpext[extension[i]&0x0F]);
}
putc('\n',hpj);
}
}
void ExportBitmaps(FILE *HelpFile) /* export all bitmaps */
{
BUFFER buf;
MFILE *mf;
char *leader;
char FileName[20];
long FileLength;
int i,num,n,type;
long savepos;
leader="|bm"+before31;
SearchFile(HelpFile,NULL,NULL);
for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
{
for(i=0;i<n;i++)
{
my_gets(FileName,sizeof(FileName),HelpFile);
getdw(HelpFile);
if(memcmp(FileName,leader,strlen(leader))==0)
{
savepos=ftell(HelpFile);
if(SearchFile(HelpFile,FileName,&FileLength))
{
mf=CreateVirtual(HelpFile);
type=ExtractBitmap(FileName+(FileName[0]=='|'),mf);
CloseMap(mf);
if(type)
{
num=atoi(FileName+(FileName[0]=='|')+2);
if(num>=extensions)
{
extension=my_realloc(extension,(num+1)*sizeof(char));
while(extensions<=num) extension[extensions++]=0;
}
extension[num]=type;
}
}
fseek(HelpFile,savepos,SEEK_SET);
}
}
}
}
char *TopicName(long topic)
{
static char name[20];
int i;
if(before31)
{
if(topic==0L) topic=Topic[0];
for(i=16;i<Topics;i++) if(Topic[i]==topic)
{
sprintf(name,"TOPIC%d",i);
return name;
}
}
else
{
if(topic==-1L)
{
NotInAnyTopic=TRUE;
return "21KSYK4"; /* evaluates to -1 without generating help compiler warning */
}
i=FindContext(topic);
if(i!=-1)
{
return unhash(ContextRec[i].HashValue);
}
}
if(topic) fprintf(stderr,"Can not find topic offset %08lx\n",topic);
return NULL;
}
char *GetWindowName(long n) /* secondary window name from window number */
{
if(windowname==NULL||n<0||n>=windownames||windowname[n]==NULL) return "main";
return windowname[n];
}
/* create HPJ file from contents of |SYSTEM internal file */
void SysList(FILE *HelpFile,FILE *hpj,char *IconFileName)
{
long FileLength;
SYSTEMHEADER SysHdr;
SYSTEMRECORD *SysRec;
STOPHEADER StopHdr;
char name[51];
char *ptr;
long color;
FILE *f;
int fbreak,macro,windows,i,keywords,dllmaps,n;
if(hpj&&SearchFile(HelpFile,"|SYSTEM",NULL))
{
my_fread(&SysHdr,sizeof(SysHdr),HelpFile);
if(SysHdr.Minor==15)
{
strcpy(helpcomp,"HC30");
}
else if(SysHdr.Minor==21)
{
strcpy(helpcomp,"HC31/HCP");
}
else if(SysHdr.Minor==27)
{
strcpy(helpcomp,"WMVC/MVCC");
}
else if(SysHdr.Minor==33)
{
if(mvp)
{
strcpy(helpcomp,"MVC");
}
else
{
strcpy(helpcomp,"HCRTF");
}
}
fputs("[OPTIONS]\n",hpj);
if(before31) /* If 3.0 get title */
{
my_gets(HelpFileTitle,33,HelpFile);
if(HelpFileTitle[0]!='\0'&&HelpFileTitle[0]!='\n')
{
fprintf(hpj,"TITLE=%s\n",HelpFileTitle);
}
fprintf(hpj,"INDEX=%s\n",TopicName(0L));
if(PhraseCount)
{
fputs("COMPRESS=TRUE\n",hpj);
}
else
{
fputs("COMPRESS=FALSE\n",hpj);
}
for(i='A';i<='z';i++) if(lists[i-'0']&&i!='K')
{
fprintf(hpj,"MULTIKEY=%c\n",i);
}
putc('\n',hpj);
}
else /* else get 3.1 System records */
{
macro=0;
fbreak=0;
windows=0;
keywords=0;
dllmaps=0;
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
switch(SysRec->RecordType)
{
case 0x0001:
if(SysRec->Data[0]) fprintf(hpj,"TITLE=%s\n",SysRec->Data);
break;
case 0x0002:
ptr=strchr(SysRec->Data,'\r');
if(ptr) strcpy(ptr,"%date");
if(SysRec->Data[0]) fprintf(hpj,"COPYRIGHT=%s\n",SysRec->Data);
break;
case 0x0003:
ptr=TopicName(*(long *)SysRec->Data);
if(ptr) fprintf(hpj,"CONTENTS=%s\n",ptr);
break;
case 0x0004:
macro=1;
break;
case 0x0005:
fprintf(hpj,"ICON=%s\n",IconFileName);
f=my_fopen(IconFileName,"wb");
if(f)
{
fwrite(SysRec->Data,SysRec->DataSize,1,f);
my_fclose(f);
}
break;
case 0x0006:
windows++;
break;
case 0x0008:
if(SysRec->Data[0]) fprintf(hpj,"CITATION=%s\n",SysRec->Data);
break;
case 0x0009:
if(!mvp) fprintf(hpj,"LCID=0x%X 0x%X 0x%X\n",*(short *)(SysRec->Data+8),*(short *)SysRec->Data,*(short *)(SysRec->Data+2));
break;
case 0x000A:
if(!mvp&&SysRec->Data[0]) fprintf(hpj,"CNT=%s\n",SysRec->Data);
break;
case 0x000B:
// if(!mvp) fprintf(hpj,"CHARSET=%d\n",*(unsigned char *)(SysRec->Data+1));
break;
case 0x000C:
if(mvp)
{
fbreak=1;
}
else
{
fprintf(hpj,"DEFFONT=%s,%d,%d\n",SysRec->Data+2,*(unsigned char *)SysRec->Data,*(unsigned char *)(SysRec->Data+1));
}
break;
case 0x000D:
if(mvp) groups++;
break;
case 0x000E:
if(mvp)
{
keywords=1;
}
else
{
fprintf(hpj,"INDEX_SEPARATORS=\"%s\"\n",SysRec->Data);
strlcpy(index_separators,SysRec->Data,sizeof(index_separators));
}
break;
case 0x0012:
if(SysRec->Data[0]) fprintf(hpj,"LANGUAGE=%s\n",SysRec->Data);
break;
case 0x0013:
dllmaps=1;
break;
}
}
if(win95)
{
i=0;
if(lzcompressed) i|=8;
if(Hall) i|=4; else if(PhraseCount) i|=2;
fprintf(hpj,"COMPRESS=%d\n",i);
}
else if(!lzcompressed)
{
fputs("COMPRESS=OFF\n",hpj);
}
else if(PhraseCount)
{
fputs("COMPRESS=HIGH\n",hpj);
}
else
{
fputs("COMPRESS=MEDIUM\n",hpj);
}
if(SysHdr.Flags==8) fputs("CDROMOPT=TRUE\n",hpj);
for(i='A';i<='z';i++) if(lists[i-'0']&&i!='K'&&(i!='A'||!win95))
{
fprintf(hpj,"MULTIKEY=%c\n",i);
}
putc('\n',hpj);
if(windows)
{
fputs("[WINDOWS]\n",hpj);
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
if(SysRec->RecordType==0x0006)
{
if(SysRec->DataSize==sizeof(SECWINDOW))
{
PrintWindow(hpj,(SECWINDOW *)SysRec->Data);
}
else
{
PrintMVBWindow(hpj,(MVBWINDOW *)SysRec->Data);
}
}
}
putc('\n',hpj);
}
if(macro)
{
fputs("[CONFIG]\n",hpj);
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
if(SysRec->RecordType==0x0004)
{
if(sscanf(SysRec->Data,"SPC(%ld)%n",&color,&n)>0)
{
fprintf(hpj,"SPC(%u,%u,%u)%s\n",(unsigned char)(color),(unsigned char)(color>>8),(unsigned char)(color>>16),SysRec->Data+n);
}
else
{
fprintf(hpj,"%s\n",SysRec->Data);
}
}
}
putc('\n',hpj);
}
if(fbreak)
{
fputs("[FTINDEX]\n",hpj);
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
if(SysRec->RecordType==0x000C)
{
ptr=strtok(SysRec->Data," ");
if(ptr)
{
fprintf(hpj,"dtype%s",ptr);
ptr=strtok(NULL," ");
if(ptr)
{
fprintf(hpj,"=%s",ptr);
ptr=strtok(NULL," ");
if(ptr)
{
fprintf(hpj,"!%s",ptr);
ptr=strtok(NULL," ");
if(ptr)
{
fprintf(hpj,",%s",ptr+1);
if(SearchFile(HelpFile,ptr,NULL))
{
for(n=0;n<stopwordfiles;n++)
{
if(strcmp(stopwordfilename[n],ptr)==0) break;
}
if(n==stopwordfiles)
{
stopwordfilename=my_realloc(stopwordfilename,(stopwordfiles+1)*sizeof(char *));
stopwordfilename[stopwordfiles++]=my_strdup(ptr);
f=my_fopen(ptr+1,"wt");
if(f)
{
my_fread(&StopHdr,sizeof(StopHdr),HelpFile);
for(n=0;n<StopHdr.BytesUsed;n+=1+strlen(buffer))
{
i=getc(HelpFile);
my_fread(buffer,i,HelpFile);
buffer[i]='\0';
fprintf(f,"%s\n",buffer);
}
my_fclose(f);
}
}
}
ptr=strtok(NULL," ");
if(ptr) fprintf(hpj,",%s",ptr);
}
}
}
putc('\n',hpj);
}
}
}
putc('\n',hpj);
}
if(groups||multi&&browsenums>1)
{
group=my_malloc(groups*sizeof(GROUP));
fputs("[GROUPS]\n",hpj);
i=0;
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
if(SysRec->RecordType==0x000D)
{
ptr=strchr(SysRec->Data,' ');
if(ptr) *ptr++='\0';
groups=SearchFile(HelpFile,SysRec->Data,NULL);
n=strcspn(SysRec->Data,".");
SysRec->Data[n]='\0';
if(ptr&&strcmp(ptr,"\"\" ")==0)
{
fprintf(hpj,"group=%s\n",SysRec->Data);
}
else
{
fprintf(hpj,"group=%s,%s\n",SysRec->Data,ptr);
}
group[i].Name=my_strdup(SysRec->Data);
if(groups)
{
my_fread(&group[i].GroupHeader,sizeof(group[i].GroupHeader),HelpFile);
if(group[i].GroupHeader.GroupType==2)
{
group[i].Bitmap=my_malloc(group[i].GroupHeader.BitmapSize);
my_fread(group[i].Bitmap,group[i].GroupHeader.BitmapSize,HelpFile);
}
}
else
{
group[i].GroupHeader.GroupType=0;
}
i++;
}
}
if(multi) for(i=1;i<browsenums;i++) fprintf(hpj,"group=BROWSE%04x\n",i);
if(SearchFile(HelpFile,"|GMACROS",&FileLength))
{
long len;
long pos;
long off;
getdw(HelpFile); /* possible count or group number */
for(pos=4L;pos<FileLength;pos+=len)
{
len=getdw(HelpFile); /* length of record containing two longs and two strings */
off=getdw(HelpFile); /* offset of second string in record (first is at pos 8) */
if(off<=0L) off=len;
if(len<8) break;
if(len<off) break;
if(off>8)
{
my_fread(buffer,off-8,HelpFile);
buffer[off-8]='\0';
fprintf(hpj,"entry=%s\n",buffer);
}
if(len>off)
{
my_fread(buffer,len-off,HelpFile);
buffer[len-off]='\0';
fprintf(hpj,"exit=%s\n",buffer);
}
}
}
putc('\n',hpj);
}
if(dllmaps)
{
fputs("[DLLMAPS]\n",hpj);
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
if(SysRec->RecordType==0x0013)
{
if(strcmp(SysRec->Data,"MVMCI")!=0&&strcmp(SysRec->Data,"MVIMAGE")!=0&&strcmp(SysRec->Data,"MVBRKR")!=0)
{
ptr=SysRec->Data+strlen(SysRec->Data)+1;
fprintf(hpj,"%s=%s,",SysRec->Data,ptr);
ptr=ptr+strlen(ptr)+1;
fprintf(hpj,"%s,",ptr);
ptr=ptr+strlen(ptr)+1;
fprintf(hpj,"%s,",ptr);
ptr=ptr+strlen(ptr)+1;
fprintf(hpj,"%s\n",ptr);
}
}
}
putc('\n',hpj);
}
if(keywords)
{
fputs("[KEYINDEX]\n",hpj);
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
if(SysRec->RecordType==0x000E)
{
fprintf(hpj,"keyword=%c,\"%s\"\n",SysRec->Data[1],SysRec->Data+30);
}
}
putc('\n',hpj);
}
for(i=0;i<windows;i++)
{
sprintf(name,"|CF%d",i);
if(SearchFile(HelpFile,name,&FileLength))
{
fprintf(hpj,"[CONFIG:%d]\n",i);
/* may use [CONFIG-GetWindowName] instead, but WindowName need not be defined */
for(n=0;n<FileLength;n+=strlen(buffer)+1)
{
my_gets(buffer,sizeof(buffer),HelpFile);
fprintf(hpj,"%s\n",buffer);
}
putc('\n',hpj);
}
}
}
}
}
/* load phrases for decompression from old Phrases file or new PhrIndex,
// PhrImage files of HCRTF */
BOOL PhraseLoad(FILE *HelpFile)
{
long FileLength;
char junk[30];
BOOL newphrases;
PHRINDEXHDR PhrIndexHdr;
unsigned int n;
long l,offset;
long SavePos;
if(SearchFile(HelpFile,"|PhrIndex",NULL))
{
my_fread(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile);
SavePos=ftell(HelpFile);
if(SearchFile(HelpFile,"|PhrImage",&FileLength))
{
if(FileLength!=PhrIndexHdr.phrimagecompressedsize)
{
fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileLength);
}
PhraseCount=(unsigned int)PhrIndexHdr.entries;
PhraseOffsets=my_malloc(sizeof(unsigned int)*(PhraseCount+1));
Phrases=my_malloc(PhrIndexHdr.phrimagesize);
if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize)
{
my_fread(Phrases,PhrIndexHdr.phrimagesize,HelpFile);
}
else
{
DecompressIntoBuffer(2,HelpFile,FileLength,Phrases,PhrIndexHdr.phrimagesize);
}
fseek(HelpFile,SavePos,SEEK_SET);
GetBit(NULL);
offset=0;
PhraseOffsets[0]=offset;
for(l=0;l<PhrIndexHdr.entries;l++)
{
for(n=1;GetBit(HelpFile);n+=1<<PhrIndexHdr.bits) ;
if(GetBit(HelpFile)) n+=1;
if(PhrIndexHdr.bits>1) if(GetBit(HelpFile)) n+=2;
if(PhrIndexHdr.bits>2) if(GetBit(HelpFile)) n+=4;
if(PhrIndexHdr.bits>3) if(GetBit(HelpFile)) n+=8;
if(PhrIndexHdr.bits>4) if(GetBit(HelpFile)) n+=16;
offset+=n;
PhraseOffsets[(int)l+1]=offset;
}
}
Hall=TRUE;
fprintf(stderr,"%u phrases loaded\n",PhraseCount);
}
else if(SearchFile(HelpFile,"|Phrases",&FileLength))
{
PhraseCount=my_getw(HelpFile);
newphrases=PhraseCount==0x0800; /* VC4.0: MSDEV\HELP\MSDEV40.MVB */
if(newphrases) PhraseCount=my_getw(HelpFile);
if(my_getw(HelpFile)!=0x0100)
{
error("Unknown |Phrases file structure");
return FALSE;
}
if(PhraseCount)
{
if(before31)
{
offset=(PhraseCount+1)*sizeof(short);
FileLength-=(PhraseCount+1)*sizeof(short)+4;
l=FileLength;
}
else
{
l=getdw(HelpFile);
if(newphrases)
{
my_fread(&junk,sizeof(junk),HelpFile);
offset=(PhraseCount+1)*sizeof(short);
FileLength-=(PhraseCount+1)*sizeof(short)+sizeof(junk)+10;
}
else
{
offset=(PhraseCount+1)*sizeof(short);
FileLength-=(PhraseCount+1)*sizeof(short)+8;
}
}
PhraseOffsets=my_malloc(sizeof(unsigned int)*(PhraseCount+1));
for(n=0;n<=PhraseCount;n++) PhraseOffsets[n]=my_getw(HelpFile)-offset;
Phrases=my_malloc(l);
DecompressIntoBuffer((before31?0:2),HelpFile,FileLength,Phrases,l);
fprintf(stderr,"%u phrases loaded\n",PhraseCount);
}
Hall=FALSE;
}
return TRUE;
}
/* write phrase PhraseNum to out and returns advanced out
// or to f it out = NULL or uses PrintString if f = NULL, returns NULL then */
char *PrintPhrase(unsigned int PhraseNum,char *out,FILE *f)
{
char *ptr;
size_t len;
if(PhraseNum>=PhraseCount)
{
error("Phrase %u does not exist",PhraseNum);
return out;
}
ptr=Phrases+PhraseOffsets[PhraseNum];
len=PhraseOffsets[PhraseNum+1]-PhraseOffsets[PhraseNum];
if(out)
{
memcpy(out,ptr,len);
return out+len;
}
if(f)
{
fwrite(ptr,len,1,f);
}
else
{
PrintString(ptr,len);
}
return NULL;
}
/* writeout .PH file from already loaded phrases */
void PhraseList(char *FileName)
{
FILE *f;
unsigned int n;
if(PhraseCount)
{
f=my_fopen(FileName,"wt");
if(f)
{
for(n=0;n<PhraseCount;n++)
{
PrintPhrase(n,NULL,f);
putc('\n',f);
}
my_fclose(f);
}
}
}
char *FontFamily(unsigned int i)
{
static char *familyname[]={"nil","roman","swiss","modern","script","decor","tech"};
if(i>0&&i<7) return familyname[i];
return familyname[0];
}
/* collect another color into color table */
unsigned char AddColor(unsigned char r,unsigned char g,unsigned char b)
{
int n;
for(n=0;n<colors;n++)
{
if(r==color[n].r&&g==color[n].g&&b==color[n].b) break;
}
if(n==colors)
{
color[colors].r=r;
color[colors].g=g;
color[colors].b=b;
colors++;
}
return n;
}
/* load fonts from help file, filling up internal font structure,
// writing fonttbl, colortbl, and styletbl to rtf file */
void FontLoad(FILE *HelpFile,FILE *rtf,FILE *hpj)
{
static char *BestFonts[]={"Arial","Times New Roman","MS Sans Serif","MS Serif","Helv","TmsRmn","MS Sans Serif","Helvetica","Times Roman","Times"};
CHARMAPHEADER CharmapHeader;
FONTHEADER FontHdr;
FILE *f;
char FontName[33];
char CharMap[33];
char *ptr;
char *p;
long FontStart;
int i,j,k,l,len;
unsigned char *family;
BOOL charmap;
OLDFONT oldfont;
NEWFONT newfont;
MVBFONT mvbfont;
MVBSTYLE *mvbstyle;
NEWSTYLE *newstyle;
FONTDESCRIPTOR *fd;
if(SearchFile(HelpFile,"|FONT",NULL))
{
FontStart=ftell(HelpFile);
my_fread(&FontHdr,sizeof(FontHdr),HelpFile);
fontnames=FontHdr.NumFacenames;
len=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/fontnames;
fontname=my_malloc(fontnames*sizeof(char *));
family=my_malloc(fontnames*sizeof(unsigned char));
memset(family,0,fontnames*sizeof(unsigned char));
charmap=FALSE;
mvbstyle=NULL;
newstyle=NULL;
for(i=0;i<fontnames;i++)
{
fseek(HelpFile,FontStart+FontHdr.FacenamesOffset+len*i,SEEK_SET);
my_fread(FontName,len,HelpFile);
FontName[len]='\0';
ptr=strchr(FontName,',');
if(ptr&&FontHdr.FacenamesOffset>=16)
{
*ptr++='\0';
fseek(HelpFile,FontStart+FontHdr.CharmapsOffset,SEEK_SET);
for(j=0;hpj&&j<FontHdr.NumCharmaps;j++)
{
my_fread(CharMap,32,HelpFile);
CharMap[32]='\0';
p=strchr(CharMap,',');
if(p&&strcmp(p+1,ptr)==0&&strcmp(CharMap,"|MVCHARTAB,0")!=0)
{
if(!charmap)
{
fputs("[CHARMAP]\n",hpj);
charmap=TRUE;
}
*p++='\0';
if(strcmp(p,"0")==0)
{
fprintf(hpj,"DEFAULT=%s\n",CharMap);
}
else
{
fprintf(hpj,"%s=%s\n",FontName,CharMap);
}
break;
}
}
}
fontname[i]=my_strdup(FontName);
}
if(charmap) putc('\n',hpj);
if(hpj&&FontHdr.FacenamesOffset>=16) for(j=0;j<FontHdr.NumCharmaps;j++)
{
fseek(HelpFile,FontStart+FontHdr.CharmapsOffset+j*32,SEEK_SET);
my_fread(CharMap,32,HelpFile);
CharMap[32]='\0';
p=strchr(CharMap,',');
if(p&&strcmp(CharMap,"|MVCHARTAB,0")!=0)
{
*p++='\0';
if(SearchFile(HelpFile,CharMap,NULL))
{
my_fread(&CharmapHeader,sizeof(CHARMAPHEADER),HelpFile);
f=my_fopen(CharMap,"wt");
if(f)
{
fprintf(f,"%d,\n",CharmapHeader.Entries);
for(k=0;k<CharmapHeader.Entries;k++)
{
fprintf(f,"%5u,",my_getw(HelpFile));
fprintf(f,"%5u,",my_getw(HelpFile));
fprintf(f,"%3u,",getc(HelpFile));
fprintf(f,"%3u,",getc(HelpFile));
fprintf(f,"%3u,",getc(HelpFile));
fprintf(f,"%3u,\n",getc(HelpFile));
my_getw(HelpFile);
}
fprintf(f,"%d,\n",CharmapHeader.Ligatures);
for(k=0;k<CharmapHeader.Ligatures;k++)
{
for(l=0;l<CharmapHeader.LigLen;l++)
{
fprintf(f,"%3u,",getc(HelpFile));
}
putc('\n',f);
}
my_fclose(f);
}
}
}
}
fseek(HelpFile,FontStart+FontHdr.DescriptorsOffset,SEEK_SET);
colors=1; /* auto */
color[0].r=1;
color[0].g=1;
color[0].b=0;
fonts=FontHdr.NumDescriptors;
if(font) free(font);
font=my_malloc(fonts*sizeof(FONTDESCRIPTOR));
memset(font,0,fonts*sizeof(FONTDESCRIPTOR));
if(FontHdr.FacenamesOffset>=16)
{
scaling=1L;
rounderr=0;
for(i=0;i<FontHdr.NumDescriptors;i++)
{
my_fread(&mvbfont,sizeof(mvbfont),HelpFile);
fd=font+i;
fd->FontName=mvbfont.FontName;
fd->HalfPoints=-2L*mvbfont.Height;
fd->Bold=mvbfont.Weight>500;
fd->Italic=mvbfont.Italic!=0;
fd->Underline=mvbfont.Underline!=0;
fd->StrikeOut=mvbfont.StrikeOut!=0;
fd->DoubleUnderline=mvbfont.DoubleUnderline!=0;
fd->SmallCaps=mvbfont.SmallCaps!=0;
fd->textcolor=AddColor(mvbfont.FGRGB[0],mvbfont.FGRGB[1],mvbfont.FGRGB[2]);
fd->backcolor=AddColor(mvbfont.BGRGB[0],mvbfont.BGRGB[1],mvbfont.BGRGB[2]);
fd->FontFamily=mvbfont.PitchAndFamily>>4;
fd->style=mvbfont.style;
fd->up=mvbfont.up;
fd->expndtw=mvbfont.expndtw;
}
fseek(HelpFile,FontStart+FontHdr.FormatsOffset,SEEK_SET);
mvbstyle=my_malloc(FontHdr.NumFormats*sizeof(MVBSTYLE));
my_fread(mvbstyle,FontHdr.NumFormats*sizeof(MVBSTYLE),HelpFile);
for(i=0;i<FontHdr.NumFormats;i++)
{
MVBSTYLE *m;
m=mvbstyle+i;
m->font.FGRGB[0]=AddColor(m->font.FGRGB[0],m->font.FGRGB[1],m->font.FGRGB[2]);
m->font.BGRGB[0]=AddColor(m->font.BGRGB[0],m->font.BGRGB[1],m->font.BGRGB[2]);
}
}
else if(FontHdr.FacenamesOffset>=12)
{
scaling=1L;
rounderr=0;
for(i=0;i<FontHdr.NumDescriptors;i++)
{
my_fread(&newfont,sizeof(newfont),HelpFile);
fd=font+i;
fd->Bold=newfont.Weight>500;
fd->Italic=newfont.Italic!=0;
fd->Underline=newfont.Underline!=0;
fd->StrikeOut=newfont.StrikeOut!=0;
fd->DoubleUnderline=newfont.DoubleUnderline!=0;
fd->SmallCaps=newfont.SmallCaps!=0;
fd->FontName=newfont.FontName;
fd->HalfPoints=-2L*newfont.Height;
fd->textcolor=AddColor(newfont.FGRGB[0],newfont.FGRGB[1],newfont.FGRGB[2]);
fd->backcolor=AddColor(newfont.BGRGB[0],newfont.BGRGB[1],newfont.BGRGB[2]);
fd->FontFamily=newfont.PitchAndFamily>>4;
}
fseek(HelpFile,FontStart+FontHdr.FormatsOffset,SEEK_SET);
newstyle=my_malloc(FontHdr.NumFormats*sizeof(NEWSTYLE));
my_fread(newstyle,FontHdr.NumFormats*sizeof(NEWSTYLE),HelpFile);
for(i=0;i<FontHdr.NumFormats;i++)
{
NEWSTYLE *m;
m=newstyle+i;
m->font.FGRGB[0]=AddColor(m->font.FGRGB[0],m->font.FGRGB[1],m->font.FGRGB[2]);
m->font.BGRGB[0]=AddColor(m->font.BGRGB[0],m->font.BGRGB[1],m->font.BGRGB[2]);
}
}
else
{
scaling=10L;
rounderr=5;
for(i=0;i<FontHdr.NumDescriptors;i++)
{
my_fread(&oldfont,sizeof(oldfont),HelpFile);
fd=font+i;
fd->Bold=(oldfont.Attributes&FONT_BOLD)!=0;
fd->Italic=(oldfont.Attributes&FONT_ITAL)!=0;
fd->Underline=(oldfont.Attributes&FONT_UNDR)!=0;
fd->StrikeOut=(oldfont.Attributes&FONT_STRK)!=0;
fd->DoubleUnderline=(oldfont.Attributes&FONT_DBUN)!=0;
fd->SmallCaps=(oldfont.Attributes&FONT_SMCP)!=0;
fd->FontName=oldfont.FontName;
fd->HalfPoints=oldfont.HalfPoints;
fd->textcolor=AddColor(oldfont.FGRGB[0],oldfont.FGRGB[1],oldfont.FGRGB[2]);
fd->backcolor=AddColor(oldfont.BGRGB[0],oldfont.BGRGB[1],oldfont.BGRGB[2]);
if(oldfont.FontFamily<6)
{
fd->FontFamily=lookup[oldfont.FontFamily];
}
else
{
fd->FontFamily=oldfont.FontFamily;
}
}
}
for(i=0;i<FontHdr.NumDescriptors;i++)
{
if(font[i].FontName<fontnames)
{
family[font[i].FontName]=font[i].FontFamily;
}
}
DefFont=0;
l=sizeof(BestFonts)/sizeof(BestFonts[0]);
if(fontname)
{
for(i=0;i<fontnames;i++) if(family[i])
{
for(j=0;j<l;j++)
{
if(stricmp(fontname[i],BestFonts[j])==0)
{
DefFont=i;
l=j;
break;
}
}
}
}
fprintf(rtf,"{\\rtf1\\ansi\\deff%d\n{\\fonttbl",DefFont);
for(i=0;i<fontnames;i++)
{
fprintf(rtf,"{\\f%d\\f%s %s;}",i,FontFamily(family[i]),fontname[i]);
free(fontname[i]);
}
free(fontname);
fputs("}\n",rtf);
if(colors>1)
{
fputs("{\\colortbl;",rtf);
for(i=1;i<colors;i++) fprintf(rtf,"\\red%d\\green%d\\blue%d;",color[i].r,color[i].g,color[i].b);
fputs("}\n",rtf);
}
fprintf(rtf,"{\\stylesheet{\\fs%d \\snext0 Normal;}\n",font[0].HalfPoints);
if(mvbstyle)
{
for(i=0;i<FontHdr.NumFormats;i++)
{
MVBSTYLE *m,*n;
m=mvbstyle+i;
fprintf(rtf,"{\\*\\cs%u \\additive",m->StyleNum+9);
if(m->BasedOn)
{
n=mvbstyle+(m->BasedOn-1);
if(m->font.FontName!=n->font.FontName) fprintf(rtf,"\\f%d",m->font.FontName);
if(m->font.expndtw!=n->font.expndtw) fprintf(rtf,"\\expndtw%d",m->font.expndtw);
if(m->font.FGRGB[0]!=n->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
if(m->font.BGRGB[0]!=n->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
if(m->font.Height!=n->font.Height) fprintf(rtf,"\\fs%d",-2L*m->font.Height);
if((m->font.Weight>500)!=(n->font.Weight>500)) fprintf(rtf,"\\b%d",m->font.Weight>500);
if(m->font.Italic!=n->font.Italic) fprintf(rtf,"\\i%d",m->font.Italic);
if(m->font.Underline!=n->font.Underline) fprintf(rtf,"\\ul%d",m->font.Underline);
if(m->font.StrikeOut!=n->font.StrikeOut) fprintf(rtf,"\\strike%d",m->font.StrikeOut);
if(m->font.DoubleUnderline!=n->font.DoubleUnderline) fprintf(rtf,"\\uldb%d",m->font.DoubleUnderline);
if(m->font.SmallCaps!=n->font.SmallCaps) fprintf(rtf,"\\scaps%d",m->font.SmallCaps);
if(m->font.up!=n->font.up) if(m->font.up>0) fprintf(rtf,"\\up%d",m->font.up); else fprintf(rtf,"\\dn%d",-m->font.up);
fprintf(rtf," \\sbasedon%u",m->BasedOn+9);
}
else
{
fprintf(rtf,"\\f%d",m->font.FontName);
if(m->font.Italic) fputs("\\i",rtf);
if(m->font.Weight>500) fputs("\\b",rtf);
if(m->font.Underline) fputs("\\ul",rtf);
if(m->font.StrikeOut) fputs("\\strike",rtf);
if(m->font.DoubleUnderline) fputs("\\uldb",rtf);
if(m->font.SmallCaps) fputs("\\scaps",rtf);
if(m->font.expndtw) fprintf(rtf,"\\expndtw%d",m->font.expndtw);
if(m->font.up>0) fprintf(rtf,"\\up%d",m->font.up);
else if(m->font.up<0) fprintf(rtf,"\\dn%d",-m->font.up);
fprintf(rtf,"\\fs%d",-2*m->font.Height);
if(m->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
if(m->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
}
fprintf(rtf," %s;}\n",m->StyleName);
}
free(mvbstyle);
}
else if(newstyle)
{
for(i=0;i<FontHdr.NumFormats;i++)
{
NEWSTYLE *m,*n;
m=newstyle+i;
fprintf(rtf,"{\\*\\cs%u \\additive",m->StyleNum+9);
if(m->BasedOn)
{
n=newstyle+(m->BasedOn-1);
if(m->font.FontName!=n->font.FontName) fprintf(rtf,"\\f%d",m->font.FontName);
if(m->font.FGRGB[0]!=n->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
if(m->font.BGRGB[0]!=n->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
if(m->font.Height!=n->font.Height) fprintf(rtf,"\\fs%d",-2L*m->font.Height);
if((m->font.Weight>500)!=(n->font.Weight>500)) fprintf(rtf,"\\b%d",m->font.Weight>500);
if(m->font.Italic!=n->font.Italic) fprintf(rtf,"\\i%d",m->font.Italic);
if(m->font.Underline!=n->font.Underline) fprintf(rtf,"\\ul%d",m->font.Underline);
if(m->font.StrikeOut!=n->font.StrikeOut) fprintf(rtf,"\\strike%d",m->font.StrikeOut);
if(m->font.DoubleUnderline!=n->font.DoubleUnderline) fprintf(rtf,"\\uldb%d",m->font.DoubleUnderline);
if(m->font.SmallCaps!=n->font.SmallCaps) fprintf(rtf,"\\scaps%d",m->font.SmallCaps);
fprintf(rtf," \\sbasedon%u",m->BasedOn+9);
}
else
{
fprintf(rtf,"\\f%d",m->font.FontName);
if(m->font.Italic) fputs("\\i",rtf);
if(m->font.Weight>500) fputs("\\b",rtf);
if(m->font.Underline) fputs("\\ul",rtf);
if(m->font.StrikeOut) fputs("\\strike",rtf);
if(m->font.DoubleUnderline) fputs("\\uldb",rtf);
if(m->font.SmallCaps) fputs("\\scaps",rtf);
fprintf(rtf,"\\fs%d",-2*m->font.Height);
if(m->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
if(m->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
}
fprintf(rtf," %s;}\n",m->StyleName);
}
free(newstyle);
}
if(family) free(family);
fputs("}\\pard\\plain\n",rtf);
memset(&CurrentFont,0,sizeof(CurrentFont));
CurrentFont.FontName=DefFont;
if(hpj)
{
fprintf(stderr,"%u font names, %u font descriptors",fontnames,FontHdr.NumDescriptors);
if(FontHdr.FacenamesOffset>=12) printf(", %u font styles",FontHdr.NumFormats);
fputs(" loaded\n",stderr);
}
}
}
/* read NumBytes from |TOPIC starting at TopicPos (or if TopicPos is 0
// where last left off) into dest, returning number of bytes read.
// TopicRead handles LZ77 decompression and the crossing of topic blocks */
long TopicRead(FILE *HelpFile,long TopicPos,void *dest,long NumBytes)
{
static TOPICBLOCKHEADER TopicBlockHeader;
static unsigned char TopicBuffer[0x4000];
static long TopicFileStart;
static long TopicBlockNum;
unsigned int TopicBlockOffset;
static unsigned int DecompSize;
static long LastTopicPos;
unsigned int n;
if(!TopicFileStart) /* first call: HelpFile is at start of |TOPIC */
{
TopicFileStart=ftell(HelpFile);
TopicBlockNum=-1L;
}
if(!TopicPos) TopicPos=LastTopicPos; /* continue where left off */
if((TopicPos-sizeof(TOPICBLOCKHEADER))/DecompressSize!=TopicBlockNum) /* other topic block */
{
TopicBlockNum=(TopicPos-sizeof(TOPICBLOCKHEADER))/DecompressSize;
if(TopicBlockNum*TopicBlockSize>=TopicFileLength) return 0;
fseek(HelpFile,TopicFileStart+TopicBlockNum*TopicBlockSize,SEEK_SET);
n=TopicBlockSize;
if(n+TopicBlockNum*TopicBlockSize>TopicFileLength)
{
n=(unsigned int)(TopicFileLength-TopicBlockNum*TopicBlockSize);
}
my_fread(&TopicBlockHeader,sizeof(TOPICBLOCKHEADER),HelpFile);
n-=sizeof(TOPICBLOCKHEADER);
if(lzcompressed)
{
DecompSize=DecompressIntoBuffer(2,HelpFile,n,TopicBuffer,sizeof(TopicBuffer));
}
else
{
DecompSize=my_fread(TopicBuffer,n,HelpFile);
}
}
TopicBlockOffset=(TopicPos-sizeof(TOPICBLOCKHEADER))%DecompressSize;
if(TopicBlockOffset+NumBytes>DecompSize) /* more than available in this block */
{
n=DecompSize-TopicBlockOffset;
if(n) memcpy(dest,TopicBuffer+TopicBlockOffset,n);
return n+TopicRead(HelpFile,(TopicBlockNum+1)*DecompressSize+sizeof(TOPICBLOCKHEADER),(char *)dest+n,NumBytes-n);
}
if(NumBytes) memcpy(dest,TopicBuffer+TopicBlockOffset,NumBytes);
LastTopicPos=TopicPos+NumBytes;
return NumBytes;
}
/* Hall or oldstyle Phrase replacement of str into out */
char *PhraseReplace(unsigned char *str,long len,char *out)
{
int CurChar;
if(Hall)
{
while(len)
{
CurChar=*str++;
len--;
if((CurChar&1)==0) /* phrases 0..127 */
{
out=PrintPhrase(CurChar/2,out,NULL);
}
else if((CurChar&3)==1) /* phrases 128..16511 */
{
CurChar=128+(CurChar/4)*256+*str++;
len--;
out=PrintPhrase(CurChar,out,NULL);
}
else if((CurChar&7)==3) /* copy next n characters */
{
while(CurChar>0)
{
*out++=*str++;
len--;
CurChar-=8;
}
}
else if((CurChar&0x0F)==0x07)
{
while(CurChar>0)
{
*out++=' ';
CurChar-=16;
}
}
else /* if((CurChar&0x0F)==0x0F) */
{
while(CurChar>0)
{
*out++='\0';
CurChar-=16;
}
}
}
}
else
{
while(len)
{
CurChar=*str++;
len--;
if(CurChar>0&&CurChar<16) /* phrase 0..1919 */
{
CurChar=256*(CurChar-1)+*str++;
len--;
out=PrintPhrase(CurChar/2,out,NULL);
if(CurChar&1) *out++=' ';
}
else
{
*out++=CurChar;
}
}
}
return out;
}
/* reads next chunk from |TOPIC like TopicRead, but does phrase decompression
// if Length > NumBytes, suitable to read LinkData2. If phrase decompression
// doesn't expands to Length bytes, buffer is padded using 0. TopicPhraseRead
// always NUL-terminates at dest[Length] just to be save */
long TopicPhraseRead(FILE *HelpFile,long TopicPos,char *dest,long NumBytes,long Length)
{
char *buffer;
long BytesRead;
if(Length<=NumBytes) /* no phrase compression in this case */
{
BytesRead=TopicRead(HelpFile,TopicPos,dest,Length);
if(BytesRead==Length&&Length<NumBytes) /* some trailing bytes are not used (bug in HCRTF ?) */
{
buffer=my_malloc(NumBytes-Length);
BytesRead+=TopicRead(HelpFile,0L,buffer,NumBytes-Length);
free(buffer);
}
}
else
{
buffer=my_malloc(NumBytes);
BytesRead=TopicRead(HelpFile,TopicPos,buffer,NumBytes);
NumBytes=PhraseReplace(buffer,NumBytes,dest)-dest;
free(buffer);
if(NumBytes>Length)
{
error("Phrase replacement delivers %ld bytes instead of %ld",NumBytes,Length);
exit(1);
}
}
while(NumBytes<=Length) dest[NumBytes++]='\0';
return BytesRead;
}
void Annotate(long pos,FILE *rtf)
{
long FileLength;
char FileName[19];
int i;
long l;
sprintf(FileName,"%ld!0",pos);
if(SearchFile(AnnoFile,FileName,&FileLength))
{
fputs("{\\v {\\*\\atnid ANN}\\chatn {\\*\\annotation \\pard\\plain {\\chatn }",rtf);
for(l=0;l<FileLength&&(i=getc(AnnoFile))!=-1;l++)
{
if(i==0x0D)
{
fputs("\\par\n",rtf);
}
else if(i!='{'&&i!='}'&&i!='\\'&&isprint(i))
{
putc(i,rtf);
}
else if(i=='{')
{
fputs("\\{\\-",rtf);
}
else if(i!='\0'&&i!=0x0A)
{
fprintf(rtf,"\\'%02x",i);
}
}
fputs("}}",rtf);
}
}
/* collect all keywords assigned to positions starting at NextKeywordOffset
// from all keyword lists, saving the first MAXKEYWORDS in KeywordRec
// (which is allocated to MAXKEYWORDS if NULL) for use in ListKeywords.
// updates NextKeywordOffset, clears NextKeywordRec, sets KeywordRecs. */
void CollectKeywords(FILE *HelpFile)
{
unsigned short j,m;
int i,n,k,l,map;
long FileLength,savepos,KWDataOffset,from;
long *keytopic;
BUFFER buf;
char kwdata[10];
char kwbtree[10];
fputs("Collecting keywords...",stderr);
savepos=ftell(HelpFile);
if(KeywordRec) /* free old keywords */
{
for(i=0;i<KeywordRecs;i++)
{
if(KeywordRec[i].Keyword) free(KeywordRec[i].Keyword);
}
}
else
{
KeywordRec=my_malloc(MAXKEYWORDS*sizeof(KEYWORDREC));
}
NextKeywordRec=KeywordRecs=0;
from=NextKeywordOffset;
NextKeywordOffset=0x7FFFFFFFL;
for(k=0;k<2;k++) for(map='0';map<='z';map++)
{
if(k)
{
if(!keyindex[map-'0']) continue;
sprintf(kwdata,"|%cKWDATA",map);
sprintf(kwbtree,"|%cKWBTREE",map);
}
else
{
if(!lists[map-'0']) continue;
sprintf(kwdata,"|%cWDATA",map);
sprintf(kwbtree,"|%cWBTREE",map);
}
if(SearchFile(HelpFile,kwdata,&FileLength))
{
keytopic=my_malloc(FileLength);
my_fread(keytopic,FileLength,HelpFile);
if(SearchFile(HelpFile,kwbtree,NULL))
{
for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
{
for(i=0;i<n;i++)
{
my_gets(keyword,sizeof(keyword),HelpFile);
m=my_getw(HelpFile);
KWDataOffset=getdw(HelpFile);
for(j=0;j<m;j++)
{
if(keytopic[KWDataOffset/4+j]>=from)
{
if(KeywordRecs>=MAXKEYWORDS)
{
NextKeywordOffset=KeywordRec[KeywordRecs-1].TopicOffset;
while(KeywordRec[KeywordRecs-1].TopicOffset==NextKeywordOffset)
{
KeywordRecs--;
if(KeywordRec[KeywordRecs].Keyword) free(KeywordRec[KeywordRecs].Keyword);
}
}
l=KeywordRecs;
while(l>0&&KeywordRec[l-1].TopicOffset>keytopic[KWDataOffset/4+j])
{
KeywordRec[l].KeyIndex=KeywordRec[l-1].KeyIndex;
KeywordRec[l].Footnote=KeywordRec[l-1].Footnote;
KeywordRec[l].Keyword=KeywordRec[l-1].Keyword;
KeywordRec[l].TopicOffset=KeywordRec[l-1].TopicOffset;
l--;
}
KeywordRec[l].KeyIndex=k>0;
KeywordRec[l].Footnote=map;
KeywordRec[l].Keyword=my_strdup(keyword);
KeywordRec[l].TopicOffset=keytopic[KWDataOffset/4+j];
KeywordRecs++;
}
}
}
}
free(keytopic);
}
}
}
fseek(HelpFile,savepos,SEEK_SET);
for(i=0;i<22;i++) fputs("\b \b",stderr);
}
/* writes out all keywords appearing up to position TopicOffset and eats
// them up so they are not written out again. Merges keywords if possible */
void ListKeywords(FILE *HelpFile,FILE *rtf,long TopicOffset)
{
int len,footnote,keyindex;
if(NextKeywordRec>=KeywordRecs)
{
if(NextKeywordOffset==0x7FFFFFFFL) return;
CollectKeywords(HelpFile);
}
footnote=keyindex=len=0;
while(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset<=TopicOffset)
{
if(len>0&&(KeywordRec[NextKeywordRec].Footnote!=footnote||KeywordRec[NextKeywordRec].KeyIndex!=keyindex||len+strlen(KeywordRec[NextKeywordRec].Keyword)>(after31?1023:254)))
{
fputs("}\n",rtf);
len=0;
}
if(len>0)
{
putc(';',rtf);
}
else if(KeywordRec[NextKeywordRec].KeyIndex)
{
fprintf(rtf,"{\\up K}{\\footnote\\pard\\plain{\\up K} %c:",KeywordRec[NextKeywordRec].Footnote);
}
else
{
fprintf(rtf,"{\\up %c}{\\footnote\\pard\\plain{\\up %c} ",KeywordRec[NextKeywordRec].Footnote,KeywordRec[NextKeywordRec].Footnote);
}
len+=strlen(KeywordRec[NextKeywordRec].Keyword)+1;
putrtf(rtf,KeywordRec[NextKeywordRec].Keyword);
footnote=KeywordRec[NextKeywordRec].Footnote;
keyindex=KeywordRec[NextKeywordRec].KeyIndex;
NextKeywordRec++;
}
if(len) fputs("}\n",rtf);
}
/* create > footnote if topic at TopicOffset has a window assigned to
// using the |VIOLA internal file. Read VIOLA sequentially, reloading
// next page only if neccessary, because it is properly ordered. */
int ListWindows(FILE *HelpFile,long TopicOffset)
{
long savepos;
static int n,i;
static BUFFER buf;
static int VIOLAfound=-1;
static VIOLAREC *Viola;
int result;
if(VIOLAfound==0) return -1;
savepos=ftell(HelpFile);
if(VIOLAfound==-1)
{
VIOLAfound=0;
if(SearchFile(HelpFile,"|VIOLA",NULL))
{
n=GetFirstPage(HelpFile,&buf,NULL);
if(n)
{
Viola=my_malloc(n*sizeof(VIOLAREC));
my_fread(Viola,n*sizeof(VIOLAREC),HelpFile);
i=0;
VIOLAfound=1;
}
}
}
result=-1;
if(VIOLAfound==1)
{
while(i>=n||TopicOffset>Viola[i].TopicOffset)
{
if(i>=n)
{
free(Viola);
n=GetNextPage(HelpFile,&buf);
if(n==0)
{
VIOLAfound=0;
break;
}
Viola=my_malloc(n*sizeof(VIOLAREC));
my_fread(Viola,n*sizeof(VIOLAREC),HelpFile);
i=0;
}
else
{
i++;
}
}
if(i<n&&Viola[i].TopicOffset==TopicOffset)
{
result=Viola[i].WindowNumber;
}
}
fseek(HelpFile,savepos,SEEK_SET);
return result;
}
/* Browse sequence handling support functions. As an efficient means to
// resolve browse sequences, FirstPass numbers and saves all browse
// sequence start positions (that are topics where BrowseNext and
// BrowsePrev both point to some topic behind) and links them with the
// following topics. Whenever a topic is merged to the beginning of a
// browse sequence, it's start topics browse subnumber is incremented.
// The interesting part is a topic where BrowsePrev and BrowseNext both
// point to earlier topics. Two different browse sequences need to be
// merged at this point, that is will get the same browse start number
// and one start topics subnumber needs to accommodate the other browse
// sequence. Using the start records, TopicDump does know which browse
// sequence starts at which topic and knows the browse sequence number
// and subnumber assigned. */
void AddStart(long StartTopic,int BrowseNum,int Count)
{
start=my_realloc(start,(starts+1)*sizeof(START));
start[starts].StartTopic=StartTopic;
start[starts].BrowseNum=BrowseNum;
start[starts].Start=Count;
starts++;
}
void FixStart(int BrowseNum,int NewBrowseNum,int AddCount)
{
int i;
for(i=0;i<starts;i++) if(start[i].BrowseNum==BrowseNum)
{
start[i].BrowseNum=NewBrowseNum;
start[i].Start+=AddCount;
}
}
void AddBrowse(long StartTopic,long NextTopic,long PrevTopic)
{
int i;
for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break; /* empty space in array ? */
if(i==browses) /* no empty space, add to array */
{
browse=my_realloc(browse,++browses*sizeof(BROWSE));
}
browse[i].StartTopic=StartTopic;
browse[i].NextTopic=NextTopic;
browse[i].PrevTopic=PrevTopic;
browse[i].BrowseNum=browsenums++;
browse[i].Start=1;
browse[i].Count=1;
}
void MergeBrowse(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
{
int i,j;
for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
{
if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
}
for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L)
{
if(browse[j].PrevTopic==TopicOffset||browse[j].PrevTopic==OtherTopicOffset) break;
}
if(i<browses&&j<browses)
{
browse[i].Count++;
browse[i].NextTopic=browse[j].NextTopic;
FixStart(browse[j].BrowseNum,browse[i].BrowseNum,browse[i].Count);
browse[j].Start+=browse[i].Count;
AddStart(browse[j].StartTopic,browse[i].BrowseNum,browse[j].Start);
browse[i].Count+=browse[j].Count;
browse[j].StartTopic=-1L;
if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
{
AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
browse[i].StartTopic=-1L;
}
}
else
{
warnings=TRUE;
fprintf(stderr,"Can not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
}
}
void LinkBrowse(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
{
int i;
for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
{
if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
}
if(i<browses)
{
browse[i].NextTopic=NextTopic;
browse[i].Count++;
if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
{
AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
browse[i].StartTopic=-1L;
}
}
else
{
warnings=TRUE;
fprintf(stderr,"Can not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
{
fprintf(stderr,"Open browse %08lx %08lx\n",browse[i].PrevTopic,browse[i].NextTopic);
}
}
}
void BackLinkBrowse(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
{
int i;
for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
{
if(browse[i].PrevTopic==TopicOffset||browse[i].PrevTopic==OtherTopicOffset) break;
}
if(i<browses)
{
browse[i].PrevTopic=PrevTopic;
browse[i].Count++;
browse[i].Start++;
FixStart(browse[i].BrowseNum,browse[i].BrowseNum,1);
if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
{
AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
browse[i].StartTopic=-1L;
}
}
else
{
warnings=TRUE;
fprintf(stderr,"Can not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
}
}
unsigned long AddLink(long StartTopic,long NextTopic,long PrevTopic)
{
int i,j;
unsigned long result;
result=0L;
for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break;
if(i==browses) browse=my_realloc(browse,++browses*sizeof(BROWSE));
for(j=0;j<starts;j++) if(start[j].StartTopic==StartTopic) break;
if(j<starts)
{
browse[i].StartTopic=start[j].StartTopic;
browse[i].BrowseNum=start[j].BrowseNum;
browse[i].Start=start[j].Start;
browse[i].Count=start[j].Start;
browse[i].NextTopic=NextTopic;
browse[i].PrevTopic=PrevTopic;
result=browse[i].BrowseNum+((long)browse[i].Start<<16);
}
else
{
warnings=TRUE;
fprintf(stderr,"Browse start %08lx not found\n",StartTopic);
}
return result;
}
unsigned long MergeLink(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
{
int i,j;
unsigned long result;
result=0L;
for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
{
if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
}
for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L)
{
if(browse[j].PrevTopic==TopicOffset||browse[j].PrevTopic==OtherTopicOffset) break;
}
if(i<browses&&j<browses)
{
browse[i].Count++;
browse[j].Start--;
if(browse[i].Count!=browse[j].Start)
{
warnings=TRUE;
fprintf(stderr,"Prev browse end %d doen't match next browse start %d\n",browse[i].Count,browse[j].Start);
}
result=browse[i].BrowseNum+((long)browse[i].Count<<16);
browse[i].NextTopic=browse[j].NextTopic;
browse[i].Count=browse[j].Count;
browse[j].StartTopic=-1L;
}
else
{
warnings=TRUE;
fprintf(stderr,"Can not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
}
return result;
}
unsigned long LinkLink(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
{
int i;
unsigned long result;
result=0L;
for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
{
if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
}
if(i<browses)
{
browse[i].NextTopic=NextTopic;
browse[i].Count++;
result=browse[i].BrowseNum+((long)browse[i].Count<<16);
if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
{
browse[i].StartTopic=-1L;
}
}
else
{
warnings=TRUE;
fprintf(stderr,"Can not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
}
return result;
}
unsigned long BackLinkLink(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
{
int i;
unsigned long result;
result=0L;
for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
{
if(browse[i].PrevTopic==TopicOffset||browse[i].PrevTopic==OtherTopicOffset) break;
}
if(i<browses)
{
browse[i].PrevTopic=PrevTopic;
browse[i].Start--;
result=browse[i].BrowseNum+((long)browse[i].Start<<16);
if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
{
browse[i].StartTopic=-1L;
}
}
else
{
warnings=TRUE;
fprintf(stderr,"Can not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
}
return result;
}
/* create numbered rtf file names, no numbering if i=0 */
void BuildName(char *buffer,int i)
{
char num[7];
strcpy(buffer,name);
if(i)
{
itoa(i,num,10);
if(strlen(buffer)+strlen(num)>8)
{
buffer[8-strlen(num)]='\0';
}
strcat(buffer,num);
}
strcat(buffer,".RTF");
}
/* emit rtf commands to change to font i.
// ul forces underline on, uldb forces doubleunderline on */
void ChangeFont(FILE *rtf,unsigned int i,BOOL ul,BOOL uldb)
{
FONTDESCRIPTOR *f;
long pos;
if(i<fonts)
{
pos=ftell(rtf);
f=font+i;
if(f->style)
{
fprintf(rtf,"\\plain\\cs%d",f->style+9);
if(uldb) fputs("\\uldb",rtf); else if(ul) fputs("\\ul",rtf);
}
else
{
/* HC30 can't reset, so reset using \plain */
if(CurrentFont.Bold&&!f->Bold
|| CurrentFont.Italic&&!f->Italic
|| CurrentFont.Underline&&!(!uldb&&(ul||f->Underline))
|| CurrentFont.StrikeOut&&!f->StrikeOut
|| CurrentFont.DoubleUnderline&&!(uldb||f->DoubleUnderline)
|| CurrentFont.SmallCaps&&!f->SmallCaps
|| CurrentFont.FontName&&!f->FontName
|| CurrentFont.textcolor&&!f->textcolor
|| CurrentFont.backcolor&&!f->backcolor
|| CurrentFont.up&&!f->up
|| CurrentFont.style&&!f->style)
{
fputs("\\plain",rtf);
memset(&CurrentFont,0,sizeof(CurrentFont));
CurrentFont.FontName=DefFont;
}
if(f->FontName!=CurrentFont.FontName) fprintf(rtf,"\\f%d",f->FontName);
if(f->Italic&&!CurrentFont.Italic) fputs("\\i",rtf);
if(f->Bold&&!CurrentFont.Bold) fputs("\\b",rtf);
if(!uldb&&(ul||f->Underline)&&!CurrentFont.Bold) fputs("\\ul",rtf);
if(f->StrikeOut&&!CurrentFont.StrikeOut) fputs("\\strike",rtf);
if((uldb||f->DoubleUnderline)&&!CurrentFont.DoubleUnderline) fputs("\\uldb",rtf);
if(f->SmallCaps&&!CurrentFont.SmallCaps) fputs("\\scaps",rtf);
if(f->expndtw!=CurrentFont.expndtw) fprintf(rtf,"\\expndtw%d",f->expndtw);
if(f->up!=CurrentFont.up)
{
if(f->up>0) fprintf(rtf,"\\up%d",f->up);
else if(f->up<0) fprintf(rtf,"\\dn%d",-f->up);
}
if(f->HalfPoints!=CurrentFont.HalfPoints) fprintf(rtf,"\\fs%d",f->HalfPoints);
if(f->textcolor!=CurrentFont.textcolor) fprintf(rtf,"\\cf%d",f->textcolor);
if(f->backcolor!=CurrentFont.backcolor) fprintf(rtf,"\\cb%d",f->backcolor);
}
memcpy(&CurrentFont,f,sizeof(CurrentFont));
if(ul) CurrentFont.Underline=1;
if(uldb)
{
CurrentFont.Underline=0;
CurrentFont.DoubleUnderline=1;
}
if(ftell(rtf)!=pos) putc(' ',rtf);
}
}
/* list all groups the topic TopicNum is assigned to and/or emit footnote
// for browse sequence of this topic as + footnote into rtf file */
void ListGroups(FILE *rtf,long TopicNum,unsigned long BrowseNum)
{
int i;
BOOL grouplisted;
grouplisted=FALSE;
for(i=0;i<groups;i++) if(group[i].GroupHeader.GroupType==1||group[i].GroupHeader.GroupType==2)
{
if(TopicNum>=group[i].GroupHeader.FirstTopic&&TopicNum<=group[i].GroupHeader.LastTopic&&(group[i].GroupHeader.GroupType==1||group[i].GroupHeader.GroupType==2&&(group[i].Bitmap[TopicNum>>3]&(1<<(TopicNum&7)))))
{
if(!grouplisted)
{
fputs("{\\up +}{\\footnote\\pard\\plain{\\up +} ",rtf);
if(BrowseNum) fprintf(rtf,"BROWSE%04x:%04x",(unsigned short)BrowseNum,(unsigned short)(BrowseNum>>16));
grouplisted=TRUE;
}
fprintf(rtf,";%s",group[i].Name);
}
}
if(grouplisted)
{
fputs("}\n",rtf);
}
else if(BrowseNum)
{
fprintf(rtf,"{\\up +}{\\footnote\\pard\\plain{\\up +} BROWSE%04x:%04x}\n",(unsigned short)BrowseNum,(unsigned short)(BrowseNum>>16));
}
}
/* advances TopicOffset to next block in |TOPIC if setting of TopicPos to
// NextBlock crosses TOPICBLOCKHEADER */
TOPICOFFSET NextTopicOffset(TOPICOFFSET TopicOffset,TOPICPOS NextBlock,TOPICPOS TopicPos)
{
/* it should never be neccessary to subtract sizeof(TOPICBLOCKHEADER), as no
// TOPICLINK may start in the last (12..21) bytes, but just to make shure... */
if((NextBlock-sizeof(TOPICBLOCKHEADER))/DecompressSize!=(TopicPos-sizeof(TOPICBLOCKHEADER))/DecompressSize)
{
return ((NextBlock-sizeof(TOPICBLOCKHEADER))/DecompressSize)*0x8000L;
}
return TopicOffset;
}
/* TopicDump: converts the internal |TOPIC file to RTF format suitable for
// recompilation inserting footnotes with information from other internal
// files as required */
FILE *TopicDump(FILE *HelpFile,FILE *rtf,FILE *hpj,BOOL makertf)
{
TOPICLINK TopicLink;
char *LinkData1; /* Data associated with this link */
long nonscroll=-1L;
char *LinkData2; /* Second set of data */
int fontset,i;
int NextContextRec;
unsigned long BrowseNum;
char *hotspot;
char *arg;
BOOL firsttopic=TRUE;
BOOL ul,uldb;
int nextbitmap,TopicInRTF,NumberOfRTF;
long TopicNum,TopicOffset,TopicPos;
int col,cols,lastcol;
short *iptr;
unsigned short x1,x2,x3;
short y1;
long l1;
char *ptr;
char *cmd;
char *str;
long ActualTopicOffset,MaxTopicOffset;
TOPICHEADER30 *TopicHdr30;
TOPICHEADER *TopicHdr;
long BogusTopicOffset;
if(SearchFile(HelpFile,"|TOPIC",&TopicFileLength))
{
fontset=-1;
nextbitmap=1;
if(browse) free(browse);
browse=NULL;
browses=0;
NextContextRec=0;
ul=uldb=FALSE;
hotspot=NULL;
TopicOffset=0L;
TopicPos=12L;
TopicNum=16;
TopicInRTF=0;
NumberOfRTF=1;
while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
{
if(before31)
{
if(TopicPos+TopicLink.NextBlock>=TopicFileLength) break;
}
else
{
if(TopicLink.NextBlock<=0) break;
}
if(TopicLink.DataLen1>sizeof(TOPICLINK))
{
LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
}
else LinkData1=NULL;
if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
{
LinkData2=my_malloc(TopicLink.DataLen2+1);
if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
}
else LinkData2=NULL;
if(LinkData1&&TopicLink.RecordType==TL_TOPICHDR) /* display a Topic Header record */
{
if(TopicsPerRTF&&++TopicInRTF>=TopicsPerRTF)
{
putc('}',rtf);
my_fclose(rtf);
BuildName(buffer,++NumberOfRTF);
if(hpj) fprintf(hpj,"%s\n",buffer);
rtf=my_fopen(buffer,"wt");
FontLoad(HelpFile,rtf,NULL);
TopicInRTF=0;
}
else if(!firsttopic)
{
if(makertf&&nopagebreak)
{
fputs("\\par\n",rtf);
}
else
{
fputs("\\page\n",rtf);
}
}
firsttopic=FALSE;
fprintf(stderr,"\rTopic %ld...",TopicNum-15);
if(!makertf)
{
BrowseNum=0L;
if(before31)
{
TopicHdr30=(TOPICHEADER30 *)LinkData1;
fprintf(rtf,"{\\up #}{\\footnote\\pard\\plain{\\up #} TOPIC%ld}\n",TopicNum);
if(resolvebrowse)
{
if(TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum>TopicNum
|| TopicHdr30->NextTopicNum==-1&&TopicHdr30->PrevTopicNum>TopicNum
|| TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum==-1)
{
BrowseNum=AddLink(TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum)
{
BrowseNum=MergeLink(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&(TopicHdr30->PrevTopicNum==-1||TopicHdr30->PrevTopicNum>TopicNum))
{
BrowseNum=BackLinkLink(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
else if(TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum&&(TopicHdr30->NextTopicNum==-1||TopicHdr30->NextTopicNum>TopicNum))
{
BrowseNum=LinkLink(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
}
ListKeywords(HelpFile,rtf,TopicPos);
}
else
{
BogusTopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
TopicHdr=(TOPICHEADER *)LinkData1;
if(TopicHdr->Scroll!=-1L)
{
nonscroll=TopicHdr->Scroll;
}
else
{
nonscroll=TopicHdr->NextTopic;
}
if(resolvebrowse)
{
if(TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck>TopicOffset
|| TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>TopicOffset
|| TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck==-1L)
{
BrowseNum=AddLink(TopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset)
{
BrowseNum=MergeLink(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>TopicOffset))
{
BrowseNum=BackLinkLink(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>TopicOffset))
{
BrowseNum=LinkLink(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
}
}
ListGroups(rtf,TopicNum-16,BrowseNum);
if(LinkData2&&TopicLink.DataLen2>0)
{
if(*LinkData2)
{
fputs("{\\up $}{\\footnote\\pard\\plain{\\up $} ",rtf);
putrtf(rtf,LinkData2);
fputs("}\n",rtf);
}
for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=strlen(LinkData2+i)+1)
{
fputs("{\\up !}{\\footnote\\pard\\plain{\\up !} ",rtf);
if(!after31&&strlen(LinkData2+i)>254)
{
printf("Help compiler will issue Warning 3511: Macro '%s' exceeds limit of 254 characters\n",LinkData2+i);
}
putrtf(rtf,LinkData2+i);
fputs("}\n",rtf);
}
}
while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<=TopicOffset)
{
fputs("{\\up #}{\\footnote\\pard\\plain{\\up #} ",rtf);
putrtf(rtf,unhash(ContextRec[NextContextRec].HashValue));
fputs("}\n",rtf);
if(!mvp) while(NextContextRec+1<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
{
NextContextRec++;
}
NextContextRec++;
}
i=ListWindows(HelpFile,TopicOffset);
if(i!=-1) fprintf(rtf,"{\\up >}{\\footnote\\pard\\plain{\\up >} %s}\n",GetWindowName(i));
}
TopicNum++;
}
else if(LinkData1&&LinkData2&&TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
{
if(AnnoFile) Annotate(TopicPos,rtf);
ptr=LinkData1;
scanlong(&ptr);
if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
{
x1=scanword(&ptr);
ActualTopicOffset=TopicOffset;
MaxTopicOffset=ActualTopicOffset+x1;
TopicOffset+=x1;
}
if(TopicLink.RecordType==TL_TABLE)
{
fputs("\\trowd",rtf);
cols=(unsigned char)*ptr++;
x1=(unsigned char)*ptr++;
switch(x1)
{
case 0:
case 2:
l1=*(short *)ptr; /* min table width */
ptr+=2;
fputs("\\trqc",rtf);
break;
case 1:
case 3:
l1=32767L;
break;
}
iptr=(short *)ptr;
if(cols>1)
{
x1=iptr[0]+iptr[1]+iptr[3]/2;
fprintf(rtf,"\\trgaph%ld\\trleft%ld \\cellx%ld\\cellx%ld",((iptr[3]*scaling-rounderr)*l1)/32767,(((iptr[1]-iptr[3])*scaling-rounderr)*l1-32767)/32767,((x1*scaling-rounderr)*l1)/32767,(((x1+iptr[2]+iptr[3])*scaling-rounderr)*l1)/32767);
x1+=iptr[2]+iptr[3];
for(col=2;col<cols;col++)
{
x1+=iptr[2*col]+iptr[2*col+1];
fprintf(rtf,"\\cellx%ld",((x1*scaling-rounderr)*l1)/32767);
}
}
else
{
fprintf(rtf,"\\trleft%ld \\cellx%ld ",((iptr[1]*scaling-rounderr)*l1-32767)/32767,((iptr[0]*scaling-rounderr)*l1)/32767);
}
ptr=(char *)(iptr+2*cols);
}
lastcol=-1;
str=LinkData2;
for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
{
fputs("\\pard",rtf);
if(TopicPos<nonscroll) fputs("\\keepn",rtf);
if(TopicLink.RecordType==TL_TABLE)
{
fputs("\\intbl",rtf);
lastcol=*(short *)ptr;
ptr+=5;
}
ptr+=4;
x2=*(unsigned short *)ptr;
ptr+=2;
if(x2&0x1000) fputs("\\keep",rtf);
if(x2&0x0400) fputs("\\qr",rtf);
if(x2&0x0800) fputs("\\qc",rtf);
if(x2&0x0001) scanlong(&ptr);
if(x2&0x0002) fprintf(rtf,"\\sb%ld",scanint(&ptr)*scaling-rounderr);
if(x2&0x0004) fprintf(rtf,"\\sa%ld",scanint(&ptr)*scaling-rounderr);
if(x2&0x0008) fprintf(rtf,"\\sl%ld",scanint(&ptr)*scaling-rounderr);
if(x2&0x0010) fprintf(rtf,"\\li%ld",scanint(&ptr)*scaling-rounderr);
if(x2&0x0020) fprintf(rtf,"\\ri%ld",scanint(&ptr)*scaling-rounderr);
if(x2&0x0040) fprintf(rtf,"\\fi%ld",scanint(&ptr)*scaling-rounderr);
if(x2&0x0100)
{
x1=(unsigned char)*ptr++;
if(x1&1) fputs("\\box",rtf);
if(x1&2) fputs("\\brdrt",rtf);
if(x1&4) fputs("\\brdrl",rtf);
if(x1&8) fputs("\\brdrb",rtf);
if(x1&0x10) fputs("\\brdrr",rtf);
if(x1&0x20) fputs("\\brdrth",rtf); else fputs("\\brdrs",rtf);
if(x1&0x40) fputs("\\brdrdb",rtf);
ptr+=2;
}
if(x2&0x0200)
{
y1=scanint(&ptr);
while(y1-->0)
{
x1=scanword(&ptr);
if(x1&0x4000)
{
switch(scanword(&ptr))
{
case 1:
fputs("\\tqr",rtf);
break;
case 2:
fputs("\\tqc",rtf);
break;
}
}
fprintf(rtf,"\\tx%d",(x1&0x3FFF)*scaling-rounderr);
}
}
putc(' ',rtf);
while(1) /* ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK)&&str<end) */
{
if(*str&&fontset>=0&&fontset<fonts&&font&&font[fontset].SmallCaps) strlwr(str);
do
{
if(!makertf)
{
while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<=ActualTopicOffset&&ContextRec[NextContextRec].TopicOffset<MaxTopicOffset)
{
fputs("{\\up #}{\\footnote\\pard\\plain{\\up #} ",rtf);
putrtf(rtf,unhash(ContextRec[NextContextRec].HashValue));
fputs("}\n",rtf);
if(!mvp) while(NextContextRec+1<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
{
NextContextRec++;
}
NextContextRec++;
}
if(!before31) ListKeywords(HelpFile,rtf,ActualTopicOffset<MaxTopicOffset?ActualTopicOffset:MaxTopicOffset-1);
}
if(*str)
{
if(*str!='{'&&*str!='}'&&*str!='\\'&&isprint((unsigned char)*str))
{
putc(*str,rtf);
}
else if(!makertf&&*str=='{')
{
fputs("\\{\\-",rtf); /* emit invisible dash after { brace */
/* because bmc or another legal command may follow, but this */
/* command was not parsed the help file was build, so it was */
/* used just as an example. The dash will be eaten up by the */
/* help compiler on recompile. */
}
else
{
fprintf(rtf,"\\'%02x",(unsigned char)*str);
}
}
if(ActualTopicOffset<MaxTopicOffset) ActualTopicOffset++;
}
while(*str++);
if((unsigned char)ptr[0]==0xFF)
{
ptr++;
break;
}
else switch((unsigned char)ptr[0])
{
case 0x20: /* vfld MVB */
if(*(long *)(ptr+1))
{
fprintf(rtf,"\\{vfld%ld\\}",*(long *)(ptr+1));
}
else
{
fputs("\\{vfld\\}",rtf);
}
ptr+=5;
break;
case 0x21: /* dtype MVB */
if(*(short *)(ptr+1))
{
fprintf(rtf,"\\{dtype%d\\}",*(short *)(ptr+1));
}
else
{
fputs("\\{dtype\\}",rtf);
}
ptr+=3;
break;
case 0x80: /* font change */
ChangeFont(rtf,fontset=*(short *)(ptr+1),ul,uldb);
ptr+=3;
break;
case 0x81:
fputs("\\line\n",rtf);
ptr++;
break;
case 0x82:
if(TopicLink.RecordType==TL_TABLE)
{
if((unsigned char)ptr[1]!=0xFF)
{
fputs("\n\\par\\intbl ",rtf);
}
else if(*(short *)(ptr+2)==-1)
{
fputs("\\cell\\intbl\\row\n",rtf);
}
else if(*(short *)(ptr+2)==lastcol)
{
fputs("\\par\\pard ",rtf);
}
else
{
fputs("\\cell\\pard ",rtf);
}
}
else
{
fputs("\n\\par ",rtf);
}
ptr++;
break;
case 0x83:
fputs("\\tab ",rtf);
ptr++;
break;
case 0x86:
x3=(unsigned char)*ptr++;
x1=*ptr++;
if(x1==0x05) cmd="ewc"; else cmd="bmc";
goto picture;
case 0x87:
x3=(unsigned char)*ptr++;
x1=*ptr++;
if(x1==0x05) cmd="ewl"; else cmd="bml";
goto picture;
case 0x88:
x3=(unsigned char)*ptr++;
x1=*ptr++;
if(x1==0x05) cmd="ewr"; else cmd="bmr";
goto picture;
picture:
l1=scanlong(&ptr);
switch(x1)
{
case 0x22: /* HC31 */
ActualTopicOffset+=scanword(&ptr); /* number of hotspots in picture */
if(ActualTopicOffset>MaxTopicOffset) ActualTopicOffset=MaxTopicOffset;
/* fall thru */
case 0x03: /* HC30 */
x1=((unsigned short *)ptr)[0];
switch(x1)
{
case 1:
while(nextbitmap<extensions&&extension[nextbitmap]<0x10) nextbitmap++;
if(nextbitmap>=extensions)
{
error("Bitmap never saved");
break;
}
x2=nextbitmap++;
goto other;
case 0:
x2=((unsigned short *)ptr)[1];
other:
if(makertf)
{
switch(x3)
{
case 0x86:
fprintf(rtf,"{\\field {\\*\\fldinst import %s}}",getbitmapname(x2));
break;
case 0x87:
fprintf(rtf,"{\\pvpara {\\field {\\*\\fldinst import %s}}\\par}\n",getbitmapname(x2));
break;
case 0x88:
fprintf(rtf,"{\\pvpara\\posxr{\\field {\\*\\fldinst import %s}}\\par}\n",getbitmapname(x2));
break;
}
}
else
{
if(x2<extensions&&(extension[x2]&0x20))
{
if(strcmp(cmd,"bmc")==0) cmd="bmct";
else if(strcmp(cmd,"bml")==0) cmd="bmlt";
else if(strcmp(cmd,"bmr")==0) cmd="bmrt";
}
fprintf(rtf,"\\{%s %s\\}",cmd,getbitmapname(x2));
}
break;
}
break;
case 0x05: /* ewc,ewl,ewr */
if(ptr[6]=='!')
{
fprintf(rtf,"\\{button %s\\}",ptr+7);
}
else if(ptr[6]=='*')
{
char *plus;
int n,c1,c2;
sscanf(ptr+7,"%d,%d,%n",&c1,&c2,&n);
plus=strchr(ptr+7+n,'+');
if((c1&0xFFF5)!=0x8400) fprintf(stderr,"mci c1=%04x\n",c1);
fputs("\\{mci",rtf);
if(cmd[2]=='r') fputs("_right",rtf);
if(cmd[2]=='l') fputs("_left",rtf);
if(c2==1) fputs(" REPEAT",rtf);
if(c2==2) fputs(" PLAY",rtf);
if(!plus) fputs(" EXTERNAL",rtf);
if(c1&8) fputs(" NOMENU",rtf);
if(c1&2) fputs(" NOPLAYBAR",rtf);
fprintf(rtf,",%s\\}\n",plus?plus+1:ptr+7+n);
}
else
{
fprintf(rtf,"\\{%s %s\\}",cmd,ptr+6);
}
break;
}
ptr+=l1;
break;
case 0x89: /* end of hotspot */
if(!makertf)
{
if(hotspot[0]=='%'&&fontset>=0&&fontset<fonts&&font[fontset].Underline)
{
hotspot[0]='*';
}
}
ChangeFont(rtf,fontset,ul=FALSE,uldb=FALSE);
if(!makertf)
{
if(!after31&&strlen(hotspot)>255)
{
puts("Help compiler will issue Warning 4072: Context string exceeds limit of 255 characters");
}
fputs("{\\v ",rtf);
putrtf(rtf,multi&&(hotspot[0]=='%'||hotspot[0]=='*')?hotspot+1:hotspot);
fputc('}',rtf);
}
ptr++;
break;
case 0xC8: /* macro */
ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
if(!makertf)
{
hotspot=my_realloc(hotspot,strlen(ptr+3)+2);
sprintf(hotspot,"!%s",ptr+3);
}
ptr+=*(short *)(ptr+1)+3;
break;
case 0xCC: /* macro without font change */
ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
if(!makertf)
{
hotspot=my_realloc(hotspot,strlen(ptr+3)+3);
sprintf(hotspot,"%%!%s",ptr+3);
}
ptr+=*(short *)(ptr+1)+3;
break;
case 0xE0: /* popup jump HC30 */
ChangeFont(rtf,fontset,ul=TRUE,FALSE);
goto label0;
case 0xE1: /* topic jump HC30 */
ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
label0:
if(!makertf)
{
hotspot=my_realloc(hotspot,128);
sprintf(hotspot,"TOPIC%ld",*(long *)(ptr+1));
}
ptr+=5;
break;
case 0xE2: /* popup jump HC31 */
ChangeFont(rtf,fontset,ul=TRUE,FALSE);
goto label1;
case 0xE3: /* topic jump HC31 */
ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
label1:
if(!makertf)
{
arg=ContextId(*(long *)(ptr+1));
hotspot=my_realloc(hotspot,strlen(arg)+1);
sprintf(hotspot,"%s",arg);
}
ptr+=5;
break;
case 0xE6: /* popup jump without font change */
ChangeFont(rtf,fontset,ul=TRUE,FALSE);
goto label2;
case 0xE7: /* topic jump without font change */
ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
label2:
if(!makertf)
{
arg=ContextId(*(long *)(ptr+1));
hotspot=my_realloc(hotspot,strlen(arg)+2);
sprintf(hotspot,"%%%s",arg);
}
ptr+=5;
break;
case 0xEA: /* popup jump into external file */
case 0xEE:
ChangeFont(rtf,fontset,ul=TRUE,FALSE);
goto label3;
case 0xEB: /* topic jump into external file / secondary window */
case 0xEF:
ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
label3:
if(!makertf)
{
if((unsigned char)ptr[0]==0xEE||(unsigned char)ptr[0]==0xEF)
{
cmd="%";
}
else
{
cmd="";
}
arg=unhash(*(long *)(ptr+4)); // no ContextId, it may jump into external file
switch((unsigned char)ptr[3])
{
case 0:
hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1);
sprintf(hotspot,"%s%s",cmd,arg);
break;
case 1:
hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(GetWindowName(ptr[8]))+1);
sprintf(hotspot,"%s%s>%s",cmd,arg,GetWindowName(ptr[8]));
break;
case 4:
hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1);
sprintf(hotspot,"%s%s@%s",cmd,arg,ptr+8);
break;
case 6:
hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1+strlen(strchr(ptr+8,'\0')+1)+1);
sprintf(hotspot,"%s%s>%s@%s",cmd,arg,ptr+8,strchr(ptr+8,'\0')+1);
break;
}
}
ptr+=*(short *)(ptr+1)+3;
break;
case 0x8B:
fputs("\\~",rtf);
ptr++;
break;
case 0x8C:
fputs("\\-",rtf);
ptr++;
break;
default:
ptr++;
}
}
}
}
if(LinkData1) free(LinkData1);
if(LinkData2) free(LinkData2);
if(before31)
{
TopicPos+=TopicLink.NextBlock;
}
else
{
TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
TopicPos=TopicLink.NextBlock;
}
}
}
return rtf;
}
int _cdecl ContextRecCmp(const void *a,const void *b)
{
if(((const CONTEXTREC *)a)->TopicOffset<((const CONTEXTREC *)b)->TopicOffset) return -1;
if(((const CONTEXTREC *)a)->TopicOffset>((const CONTEXTREC *)b)->TopicOffset) return 1;
return 0;
}
void ContextLoad(FILE *HelpFile)
{
BUFFER buf;
int n;
long entries;
if(SearchFile(HelpFile,"|CONTEXT",NULL))
{
n=GetFirstPage(HelpFile,&buf,&entries);
if(entries)
{
ContextRec=my_malloc(entries*sizeof(CONTEXTREC));
ContextRecs=0;
while(n)
{
my_fread(ContextRec+ContextRecs,n*sizeof(CONTEXTREC),HelpFile);
ContextRecs+=n;
n=GetNextPage(HelpFile,&buf);
}
fprintf(stderr,"%d topic offsets and hash values loaded\n",ContextRecs);
qsort(ContextRec,ContextRecs,sizeof(CONTEXTREC),ContextRecCmp);
}
}
else if(SearchFile(HelpFile,"|TOMAP",&entries))
{
Topic=my_malloc(entries);
my_fread(Topic,entries,HelpFile);
Topics=(int)(entries/sizeof(long));
}
}
void GenerateContent(FILE *HelpFile,FILE *ContentFile) /* create a simple Win95 contents file */
{
VIOLAREC *WindowRec;
long FileLength,offset;
int n,i,j,WindowRecs;
BUFFER buf;
char *ptr;
fprintf(ContentFile,":Base %s%s>main\n",name,ext);
if(HelpFileTitle[0]) fprintf(ContentFile,":Title %s\n",HelpFileTitle);
WindowRecs=0;
if(SearchFile(HelpFile,"|VIOLA",NULL))
{
n=GetFirstPage(HelpFile,&buf,&FileLength);
if(FileLength)
{
WindowRec=my_malloc(FileLength*sizeof(VIOLAREC));
while(n)
{
my_fread(WindowRec+WindowRecs,n*sizeof(VIOLAREC),HelpFile);
WindowRecs+=n;
n=GetNextPage(HelpFile,&buf);
}
}
}
if(SearchFile(HelpFile,"|TTLBTREE",NULL))
{
for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
{
for(i=0;i<n;i++)
{
offset=getdw(HelpFile);
if(my_gets(buffer,sizeof(buffer),HelpFile))
{
ptr=TopicName(offset);
if(ptr)
{
fprintf(ContentFile,"1 %s=%s",buffer,ptr);
for(j=0;j<WindowRecs;j++)
{
if(WindowRec[j].TopicOffset==offset)
{
fprintf(ContentFile,">%s",GetWindowName(WindowRec[j].WindowNumber));
break;
}
}
putc('\n',ContentFile);
}
}
}
}
}
}
void ListRose(FILE *HelpFile,FILE *hpj)
{
long FileLength,offset,hash,h,pos,savepos;
unsigned char *ptr;
long *keytopic;
int n,i,l,e;
unsigned short j,count;
BUFFER buf,buf2;
if(SearchFile(HelpFile,"|Rose",NULL))
{
savepos=ftell(HelpFile);
if(SearchFile(HelpFile,"|KWDATA",&FileLength))
{
keytopic=my_malloc(FileLength);
my_fread(keytopic,FileLength,HelpFile);
if(SearchFile(HelpFile,"|KWBTREE",NULL))
{
fputs("[MACROS]\n",hpj);
for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
{
for(i=0;i<n;i++)
{
my_gets(keyword,sizeof(keyword),HelpFile);
for(hash=0L,ptr=(unsigned char *)keyword;*ptr;ptr++)
{
hash=hash*43L+table[*ptr];
}
count=my_getw(HelpFile);
offset=getdw(HelpFile);
for(j=0;j<count;j++)
{
if(keytopic[offset/4+j]==-1L)
{
pos=ftell(HelpFile);
fseek(HelpFile,savepos,SEEK_SET);
for(l=GetFirstPage(HelpFile,&buf2,NULL);l;l=GetNextPage(HelpFile,&buf2))
{
for(e=0;e<l;e++)
{
h=getdw(HelpFile);
my_gets(buffer,sizeof(buffer),HelpFile);
if(h==hash)
{
fprintf(hpj,"%s\n%s\n",keyword,buffer);
my_gets(buffer,sizeof(buffer),HelpFile);
fprintf(hpj,"%s\n",buffer);
}
else
{
my_gets(buffer,sizeof(buffer),HelpFile);
}
}
}
fseek(HelpFile,pos,SEEK_SET);
break;
}
}
}
}
putc('\n',hpj);
}
free(keytopic);
}
}
}
/* dump section: all the dump-routines are used to display internal files
// of the help file with known format of contents for debugging reasons */
void PrintNewFont(int i,NEWFONT *newfont)
{
printf("%3d: %-32.32s %6ld %-6s %02X%02X%02X %02X%02X%02X ",i,fontname[newfont->FontName],newfont->Height,FontFamily(newfont->PitchAndFamily>>4),newfont->FGRGB[2],newfont->FGRGB[1],newfont->FGRGB[0],newfont->BGRGB[2],newfont->BGRGB[1],newfont->BGRGB[0]);
if(newfont->Weight>500) putchar('b');
if(newfont->Italic) putchar('i');
if(newfont->Underline) putchar('u');
if(newfont->StrikeOut) putchar('s');
if(newfont->DoubleUnderline) putchar('d');
if(newfont->SmallCaps) putchar('c');
putchar('\n');
}
void PrintMvbFont(int i,MVBFONT *mvbfont)
{
printf("%3d: %-32.32s %6ld %-6s %02X%02X%02X %02X%02X%02X ",i,fontname[mvbfont->FontName],mvbfont->Height,FontFamily(mvbfont->PitchAndFamily>>4),mvbfont->FGRGB[2],mvbfont->FGRGB[1],mvbfont->FGRGB[0],mvbfont->BGRGB[2],mvbfont->BGRGB[1],mvbfont->BGRGB[0]);
if(mvbfont->Weight>500) putchar('b');
if(mvbfont->Italic) putchar('i');
if(mvbfont->Underline) putchar('u');
if(mvbfont->StrikeOut) putchar('s');
if(mvbfont->DoubleUnderline) putchar('d');
if(mvbfont->SmallCaps) putchar('c');
putchar('\n');
}
void FontDump(FILE *HelpFile)
{
FONTHEADER FontHdr;
long FileStart;
OLDFONT oldfont;
NEWFONT newfont;
NEWSTYLE newstyle;
MVBFONT mvbfont;
MVBSTYLE mvbstyle;
int i,n;
/* Go to the FONT file and get the headers */
FileStart=ftell(HelpFile);
my_fread(&FontHdr,sizeof(FontHdr),HelpFile);
n=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/FontHdr.NumFacenames;
fontname=my_malloc(FontHdr.NumFacenames*sizeof(char *));
fseek(HelpFile,FileStart+FontHdr.FacenamesOffset,SEEK_SET);
for(i=0;i<FontHdr.NumFacenames;i++)
{
my_fread(buffer,n,HelpFile);
buffer[n]='\0';
printf("Font name %d: %s\n",i,buffer);
fontname[i]=my_strdup(buffer);
}
puts("Font Facename Height Family Foregr Backgr Style");
fseek(HelpFile,FileStart+FontHdr.DescriptorsOffset,SEEK_SET);
if(FontHdr.FacenamesOffset>=16)
{
for(i=0;i<FontHdr.NumDescriptors;i++)
{
my_fread(&mvbfont,sizeof(mvbfont),HelpFile);
PrintMvbFont(i,&mvbfont);
}
fseek(HelpFile,FileStart+FontHdr.FormatsOffset,SEEK_SET);
for(i=0;i<FontHdr.NumFormats;i++)
{
my_fread(&mvbstyle,sizeof(mvbstyle),HelpFile);
printf("Style %d",mvbstyle.StyleNum);
if(mvbstyle.BasedOn) printf(" based on %d",mvbstyle.BasedOn);
printf(" named '%s':\n",mvbstyle.StyleName);
PrintMvbFont(i,&mvbstyle.font);
}
}
else if(FontHdr.FacenamesOffset>=12)
{
for(i=0;i<FontHdr.NumDescriptors;i++)
{
my_fread(&newfont,sizeof(newfont),HelpFile);
PrintNewFont(i,&newfont);
}
fseek(HelpFile,FileStart+FontHdr.FormatsOffset,SEEK_SET);
for(i=0;i<FontHdr.NumFormats;i++)
{
my_fread(&newstyle,sizeof(newstyle),HelpFile);
printf("Style %d",newstyle.StyleNum);
if(newstyle.BasedOn) printf(" based on %d",newstyle.BasedOn);
printf(" named '%s':\n",newstyle.StyleName);
PrintNewFont(i,&newstyle.font);
}
}
else
{
for(i=0;i<FontHdr.NumDescriptors;i++)
{
my_fread(&oldfont,sizeof(oldfont),HelpFile);
printf("%3d: %-32.32s %4d.%d %-6s %02X%02X%02X %02X%02X%02X ",i,fontname[oldfont.FontName],oldfont.HalfPoints/2,(oldfont.HalfPoints&1)*5,FontFamily(oldfont.FontFamily<6?lookup[oldfont.FontFamily]:oldfont.FontFamily),oldfont.FGRGB[2],oldfont.FGRGB[1],oldfont.FGRGB[0],oldfont.BGRGB[2],oldfont.BGRGB[1],oldfont.BGRGB[0]);
if(oldfont.Attributes&FONT_BOLD) putchar('b');
if(oldfont.Attributes&FONT_ITAL) putchar('i');
if(oldfont.Attributes&FONT_UNDR) putchar('u');
if(oldfont.Attributes&FONT_STRK) putchar('s');
if(oldfont.Attributes&FONT_DBUN) putchar('d');
if(oldfont.Attributes&FONT_SMCP) putchar('c');
putchar('\n');
}
}
}
void PhrImageDump(FILE *HelpFile)
{
long FileLength;
unsigned int bytes;
PHRINDEXHDR PhrIndexHdr;
unsigned char *ptr;
if(SearchFile(HelpFile,"|PhrIndex",NULL))
{
my_fread(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile);
if(SearchFile(HelpFile,"|PhrImage",&FileLength))
{
if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize)
{
HexDump(HelpFile,FileLength,0L);
}
else
{
if(FileLength!=PhrIndexHdr.phrimagecompressedsize)
{
fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileLength);
}
ptr=my_malloc(PhrIndexHdr.phrimagesize);
bytes=DecompressIntoBuffer(2,HelpFile,FileLength,ptr,PhrIndexHdr.phrimagesize);
HexDumpMemory(ptr,bytes);
free(ptr);
}
}
}
}
void BTreeDump(FILE *HelpFile,char text[])
{
int n,i,j;
long count;
BUFFER buf;
char format[10];
char *ptr;
n=GetFirstPage(HelpFile,&buf,NULL);
while(n)
{
for(i=0;i<n;i++)
{
for(ptr=text;*ptr;ptr++)
{
if(*ptr=='%')
{
j=strcspn(ptr,"hsdiouxX!");
memcpy(format,ptr,j+1);
format[j+1]='\0';
if(format[j]=='!')
{
count=getdw(HelpFile);
while(count>=8)
{
printf(" (%ld)",getdw(HelpFile));
printf("%08lx",getdw(HelpFile));
count-=8;
}
}
else if(format[j]=='h')
{
format[j]='s';
printf(format,unhash(getdw(HelpFile)));
}
else if(format[j]=='s')
{
my_gets(buffer,sizeof(buffer),HelpFile);
printf(format,buffer);
}
else if(strchr(format,'l'))
{
printf(format,getdw(HelpFile));
}
else
{
printf(format,my_getw(HelpFile));
}
ptr+=j;
}
else
{
putchar(*ptr);
}
}
}
n=GetNextPage(HelpFile,&buf);
}
}
void PhraseDump(void)
{
unsigned int n;
for(n=0;n<PhraseCount;n++)
{
printf("%-5d - ",n);
PrintPhrase(n,NULL,NULL);
putchar('\n');
}
}
void SysDump(FILE *HelpFile)
{
SYSTEMHEADER SysHdr;
SYSTEMRECORD *SysRec;
struct tm *TimeRec;
char *ptr;
my_fread(&SysHdr,sizeof(SysHdr),HelpFile);
if(SysHdr.Minor==15)
{
ptr="HC30";
}
else if(SysHdr.Minor==21)
{
ptr="HC31/HCP";
}
else if(SysHdr.Minor==27)
{
ptr="WMVC/MVCC";
}
else if(SysHdr.Minor==33)
{
if(mvp)
{
ptr="MVC";
}
else
{
ptr="HCRTF";
}
}
else ptr="Unknown";
printf("%s Help Compiler used.\n",ptr);
printf("System Flags & Compression Method=0x%04x\n",SysHdr.Flags);
if(SysHdr.GenDate)
{
TimeRec=localtime(&SysHdr.GenDate);
printf("Help File Generated: %s",asctime(TimeRec));
}
if(SysHdr.Minor<16)
{
my_gets(HelpFileTitle,33,HelpFile);
printf("TITLE=%s\n",HelpFileTitle);
}
else for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
switch(SysRec->RecordType)
{
case 0x0001:
printf("TITLE=%s\n",SysRec->Data);
break;
case 0x0002:
printf("COPYRIGHT=%s\n",SysRec->Data);
break;
case 0x0003:
printf("CONTENTS=0x%08lX\n",*(long *)SysRec->Data);
break;
case 0x0004:
printf("[MACRO] %s\n",SysRec->Data);
break;
case 0x0005:
puts("Icon in System record");
break;
case 0x0006:
printf("[WINDOW] ");
if(SysRec->DataSize==sizeof(SECWINDOW))
{
PrintWindow(stdout,(SECWINDOW *)SysRec->Data);
}
else if(SysRec->DataSize==sizeof(MVBWINDOW))
{
PrintMVBWindow(stdout,(MVBWINDOW *)SysRec->Data);
}
else
{
HexDumpMemory(SysRec->Data,SysRec->DataSize);
error("[WINDOW] data size does not match");
}
break;
case 0x0008:
printf("CITATION=%s\n",SysRec->Data);
break;
case 0x0009:
if(!mvp) printf("LCID=0x%X 0x%X 0x%X\n",*(short *)(SysRec->Data+8),*(short *)SysRec->Data,*(short *)(SysRec->Data+2));
break;
case 0x000A:
if(!mvp) printf("CNT=%s\n",SysRec->Data);
break;
case 0x000B:
// if(!mvp) printf("CHARSET=%d\n",*(unsigned char *)(SysRec->Data+1));
break;
case 0x000C:
if(mvp)
{
printf("[FTINDEX] dtype %s\n",SysRec->Data);
}
else
{
printf("DEFFONT=%s,%d,%d\n",SysRec->Data+2,*(unsigned char *)SysRec->Data,*(unsigned char *)(SysRec->Data+1));
}
break;
case 0x000D:
if(mvp) printf("[GROUPS] %s\n",SysRec->Data);
break;
case 0x000E:
if(mvp)
{
printf("[KEYINDEX] keyword=%c, \"%s\"\n",SysRec->Data[1],SysRec->Data+30);
}
else
{
printf("INDEX_SEPARATORS=\"%s\"\n",SysRec->Data);
}
break;
case 0x0012:
if(SysRec->Data[0]) printf("LANGUAGE=%s\n",SysRec->Data);
break;
case 0x0013:
ptr=SysRec->Data+strlen(SysRec->Data)+1;
printf("[DLLMAPS] %s=%s,",SysRec->Data,ptr);
ptr+=strlen(ptr)+1;
printf("%s,",ptr);
ptr+=strlen(ptr)+1;
printf("%s,",ptr);
ptr+=strlen(ptr)+1;
printf("%s\n",ptr);
break;
default:
fprintf(stderr,"Unknown record type: 0x%04X\n",SysRec->RecordType);
HexDumpMemory(SysRec->Data,SysRec->DataSize);
}
}
}
/* dump the contents of |TOPIC for debugging */
void DumpTopic(FILE *HelpFile,long TopicPos)
{
TOPICLINK TopicLink;
TOPICHEADER30 *TopicHdr30;
TOPICHEADER *TopicHdr;
char *ptr;
char *str;
char *cmd;
long l;
unsigned short x1,x2;
unsigned char b;
int cols,col;
short i;
char *LinkData1;
char *LinkData2;
long TopicNum;
long TopicOffset;
if(!SearchFile(HelpFile,"|TOPIC",&TopicFileLength)) return;
TopicOffset=0L;
if(TopicPos<12) TopicPos=12L;
TopicNum=16;
while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
{
puts("----------------------------------------------------------------------------");
printf("TopicLink Type %02x: BlockSize=%08lx DataLen1=%08lx DataLen2=%08lx\n",TopicLink.RecordType,TopicLink.BlockSize,TopicLink.DataLen1,TopicLink.DataLen2);
printf("TopicPos=%08lx TopicOffset=%08lx PrevBlock=%08lx NextBlock=%08lx\n",TopicPos,TopicOffset,TopicLink.PrevBlock,TopicLink.NextBlock);
if(TopicLink.DataLen1>sizeof(TOPICLINK))
{
LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK));
if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
}
else LinkData1=NULL;
if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
{
LinkData2=my_malloc(TopicLink.DataLen2+1);
if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
}
else LinkData2=NULL;
if(LinkData1) HexDumpMemory(LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK));
if(TopicLink.RecordType==TL_TOPICHDR)
{
if(before31)
{
TopicHdr30=(TOPICHEADER30 *)LinkData1;
puts("============================================================================");
printf("TopicHeader TopicNum=%ld BlockSize=%ld PrevTopicNum=%d NextTopicNum=%d\n",TopicNum,TopicHdr30->BlockSize,TopicHdr30->PrevTopicNum,TopicHdr30->NextTopicNum);
TopicNum++;
}
else
{
TopicHdr=(TOPICHEADER *)LinkData1;
puts("============================================================================");
printf("TopicHeader TopicNum=%ld BlockSize=%ld NextTopicOffset=%08lx\n",TopicHdr->TopicNum,TopicHdr->BlockSize,TopicHdr->NextTopic);
printf("NonScroll=%08lx Scroll=%08lx BrowseBck=%08lx BrowseFor=%08lx\n",TopicHdr->NonScroll,TopicHdr->Scroll,TopicHdr->BrowseBck,TopicHdr->BrowseFor);
}
}
else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
{
switch(TopicLink.RecordType)
{
case TL_DISPLAY30:
fputs("Text ",stdout);
break;
case TL_DISPLAY:
fputs("Display ",stdout);
break;
case TL_TABLE:
fputs("Table ",stdout);
break;
}
ptr=LinkData1;
printf("expandedsize=%ld ",scanlong(&ptr));
if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
{
x1=scanword(&ptr);
TopicOffset+=x1;
printf("topicoffsetincrement=%u ",x1);
}
if(TopicLink.RecordType==TL_TABLE)
{
cols=(unsigned char)*ptr++;
x1=*ptr++;
printf("columns=%d type=%d ",cols,x1);
switch(x1)
{
case 0:
case 2:
printf("minwidth=%d ",*(short *)ptr);
ptr+=2;
case 1:
case 3:
break;
default:
error("Unknown TableType %d",x1);
}
for(col=0;col<cols;col++)
{
printf("width=%d gap=%d ",*(short *)ptr,*(short *)(ptr+2));
ptr+=4;
}
}
putchar('\n');
str=LinkData2;
for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
{
if(TopicLink.RecordType==TL_TABLE)
{
printf("column=%d %04x %d ",*(short *)ptr,*(unsigned short *)(ptr+2),*(unsigned char *)(ptr+4)-0x80);
ptr+=5;
}
printf("%02x %d id=%04x ",*(unsigned char *)ptr,*(unsigned char *)(ptr+1)-0x80,*(unsigned short *)(ptr+2));
ptr+=4;
x2=*((unsigned short *)ptr)++;
if(x2&0x0001) printf("unknownbit01=%ld ",scanlong(&ptr)); /* found in MVBs, purpose unknown, may mean that x2 is compressed long */
if(x2&0x0002) printf("topspacing=%d ",scanint(&ptr));
if(x2&0x0004) printf("bottomspacing=%d ",scanint(&ptr));
if(x2&0x0008) printf("linespacing=%d ",scanint(&ptr));
if(x2&0x0010) printf("leftindent=%d ",scanint(&ptr));
if(x2&0x0020) printf("rightindent=%d ",scanint(&ptr));
if(x2&0x0040) printf("firstlineindent=%d ",scanint(&ptr));
if(x2&0x0080) fputs("unknownbit80set",stdout);
if(x2&0x0100)
{
x1=(unsigned char)*ptr++;
if(x1&1) fputs("box ",stdout);
if(x1&2) fputs("topborder ",stdout);
if(x1&4) fputs("leftborder ",stdout);
if(x1&8) fputs("bottomborder ",stdout);
if(x1&0x10) fputs("rightborder ",stdout);
if(x1&0x20) fputs("thickborder ",stdout);
if(x1&0x40) fputs("doubleborder ",stdout);
if(x1&0x80) fputs("unknownborder",stdout);
printf("%04x ",*((unsigned short *)ptr)++);
}
if(x2&0x0200)
{
i=scanint(&ptr);
printf("tabs=%d ",i);
while(i-->0)
{
x1=scanword(&ptr);
printf("stop=%d ",x1&0x3FFF);
if(x1&0x4000)
{
x1=scanword(&ptr);
if(x1==1)
{
fputs("right ",stdout);
}
else if(x1==2)
{
fputs("center ",stdout);
}
else
{
error("unknowntabmodifier=%02x",x1);
}
}
}
}
if(x2&0x0400) fputs("rightalign ",stdout);
if(x2&0x0800) fputs("centeralign ",stdout);
if(x2&0x1000) fputs("keeplinestogether ",stdout);
if(x2&0x2000) fputs("unknownbit2000set ",stdout); /* found in PRINTMAN.HLP */
if(x2&0x4000) fputs("unknownbit4000set ",stdout); /* found in PRINTMAN.HLP, RATTLER.HLP */
if(x2&0x8000) fputs("unknownbit8000set",stdout);
putchar('\n');
while(1)
{
str=PrintString(str,strlen(str))+1;
if((unsigned char)ptr[0]==0xFF)
{
ptr++;
break;
}
else switch((unsigned char)ptr[0])
{
case 0x20:
printf("{vfld%ld}",*(long *)(ptr+1));
ptr+=5;
break;
case 0x21:
printf("{dtype%d}",*(short *)(ptr+1));
ptr+=3;
break;
case 0x80: /* font change */
printf("[font=%u]",*(short *)(ptr+1));
ptr+=3;
break;
case 0x81:
puts("[LF]");
ptr++;
break;
case 0x82:
puts("[CR]");
ptr++;
break;
case 0x83:
fputs("[TAB]",stdout);
ptr++;
break;
case 0x86:
ptr++;
b=*ptr++;
if(b==0x05) cmd="ewc"; else cmd="bmc";
goto picture;
case 0x87:
ptr++;
b=*ptr++;
if(b==0x05) cmd="ewl"; else cmd="bml";
goto picture;
case 0x88:
ptr++;
b=*ptr++;
if(b==0x05) cmd="ewr"; else cmd="bmr";
picture:
printf("[%s %02x ",cmd,b);
l=scanlong(&ptr);
switch(b)
{
case 0x22: /* HC31 */
x1=scanword(&ptr);
printf("hotspots=%u ",x1);
case 0x03: /* HC30 */
switch(*(unsigned short *)ptr)
{
case 0:
fputs("baggage ",stdout);
break;
case 1:
fputs("embedded ",stdout);
break;
default:
error("Unknown %04x",((unsigned short *)ptr)[0]);
}
printf("bm%u]",((unsigned short *)ptr)[1]);
break;
case 0x05:
printf("%04x ",((unsigned short *)ptr)[0]);
printf("%04x ",((unsigned short *)ptr)[1]);
printf("%04x ",((unsigned short *)ptr)[2]);
printf("%s]",ptr+6);
break;
default:
error("Unknown picture flag %02x",b);
}
ptr+=l;
break;
case 0x89: /* end of hot spot */
fputs("[U]",stdout);
ptr++;
break;
case 0x8B: /* non-break-space */
fputs("[~]",stdout);
ptr++;
break;
case 0x8C: /* non-break-hyphen */
fputs("[-]",stdout);
ptr++;
break;
case 0xC8: /* macro */
printf("[!%s]",ptr+3);
ptr+=*(short *)(ptr+1)+3;
break;
case 0xCC: /* macro without font change */
printf("[*!%s]",ptr+3);
ptr+=*(short *)(ptr+1)+3;
break;
case 0xE0: /* Popup HC30 */
printf("[^TOPIC%ld]",*(long *)(ptr+1));
ptr+=5;
break;
case 0xE1: /* Jump HC30 */
printf("[TOPIC%ld]",*(long *)(ptr+1));
ptr+=5;
break;
case 0xE2: /* Popup HC31 */
printf("[^%08lx]",*(long *)(ptr+1));
ptr+=5;
break;
case 0xE3: /* Jump HC31 */
printf("[%08lx]",*(long *)(ptr+1));
ptr+=5;
break;
case 0xE6: /* Popup without font change */
printf("[*^%08lx]",*(long *)(ptr+1));
ptr+=5;
break;
case 0xE7: /* Jump without font change */
printf("[*%08lx]",*(long *)(ptr+1));
ptr+=5;
break;
case 0xEA: /* Popup into external file / secondary window */
cmd="^";
goto jump;
case 0xEB: /* Jump into external file / secondary window */
cmd="";
goto jump;
case 0xEE: /* Popup into external file / secondary window without font change */
cmd="^*";
goto jump;
case 0xEF: /* Jump into external file / secondary window without font change */
cmd="*";
jump:
switch(ptr[3])
{
case 0:
printf("[%s%08lx] ",cmd,*(long *)(ptr+4));
break;
case 1: /* Popup into secondary window (silly) */
printf("[%s%08lx>%d]",cmd,*(long *)(ptr+4),(unsigned char)ptr[8]);
break;
case 4:
printf("[%s%08lx@%s] ",cmd,*(long *)(ptr+4),ptr+8);
break;
case 6: /* Popup into external file / secondary window (silly) */
printf("[%s%08lx>%s@%s] ",cmd,*(long *)(ptr+4),ptr+8,strchr(ptr+8,'\0')+1);
break;
default:
putchar('[');
for(i=0;i<*(short *)(ptr+1);i++) printf("%02x",(unsigned char)ptr[i]);
putchar(']');
}
ptr+=*(short *)(ptr+1)+3;
break;
default:
printf("[%02x]",(unsigned char)*ptr++);
}
}
putchar('\n');
}
}
if(LinkData2&&(TopicLink.RecordType!=TL_DISPLAY30&&TopicLink.RecordType!=TL_DISPLAY&&TopicLink.RecordType!=TL_TABLE))
{
PrintString(LinkData2,TopicLink.DataLen2);
putchar('\n');
}
if(LinkData1) free(LinkData1);
if(LinkData2) free(LinkData2);
if(before31)
{
TopicPos+=TopicLink.NextBlock;
if(TopicPos>=TopicFileLength) break;
}
else
{
if(TopicLink.NextBlock<=0) break;
TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
TopicPos=TopicLink.NextBlock;
}
}
}
void AliasList(FILE *hpj) /* write [ALIAS] section to HPJ file */
{
int i,n;
BOOL headerwritten;
headerwritten=FALSE;
for(i=0;i<ContextRecs;i=n)
{
for(n=i+1;n<ContextRecs&&ContextRec[i].TopicOffset==ContextRec[n].TopicOffset;n++)
{
if(!headerwritten)
{
fputs("Creating [ALIAS] section...\n",stderr);
fputs("[ALIAS]\n",hpj);
headerwritten=TRUE;
}
fprintf(hpj,"%s=",unhash(ContextRec[n].HashValue));
fprintf(hpj,"%s\n",unhash(ContextRec[i].HashValue));
}
}
if(headerwritten) putc('\n',hpj);
}
void CTXOMAPList(FILE *HelpFile,FILE *hpj) /* write [MAP] section to HPJ file */
{
CTXOMAPREC CTXORec;
unsigned short n,i;
char *ptr;
if(SearchFile(HelpFile,"|CTXOMAP",NULL))
{
n=my_getw(HelpFile);
if(n)
{
fputs("Creating [MAP] section...\n",stderr);
fputs("[MAP]\n",hpj);
for(i=0;i<n;i++)
{
my_fread(&CTXORec,sizeof(CTXORec),HelpFile);
ptr=TopicName(CTXORec.TopicOffset);
if(ptr)
{
fprintf(hpj,"%s %ld\n",ptr,CTXORec.MapID);
}
else
{
fprintf(hpj,"TOPIC%08lx %ld\n",CTXORec.TopicOffset,CTXORec.MapID);
}
}
putc('\n',hpj);
}
}
}
void GuessFromKeywords(FILE *HelpFile)
{
long *keytopic;
char kwdata[10];
char kwbtree[10];
int m,i,n,k,l,j,map;
long FileLength,KWDataOffset,TopicOffset;
BUFFER buf;
fputs("Guessing...",stderr);
for(k=0;k<2;k++) for(map='0';map<='z';map++)
{
if(k)
{
if(!keyindex[map-'0']) continue;
sprintf(kwdata,"|%cKWDATA",map);
sprintf(kwbtree,"|%cKWBTREE",map);
}
else
{
if(!lists[map-'0']) continue;
sprintf(kwdata,"|%cWDATA",map);
sprintf(kwbtree,"|%cWBTREE",map);
}
if(SearchFile(HelpFile,kwdata,&FileLength))
{
keytopic=my_malloc(FileLength);
my_fread(keytopic,FileLength,HelpFile);
if(SearchFile(HelpFile,kwbtree,NULL))
{
for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
{
for(i=0;i<n;i++)
{
my_gets(keyword,sizeof(keyword),HelpFile);
m=my_getw(HelpFile);
KWDataOffset=getdw(HelpFile);
for(j=0;j<m;j++)
{
TopicOffset=keytopic[KWDataOffset/4+j];
Guess(keyword,TopicOffset);
for(l=0;l<alternatives;l++)
{
if(alternative[l].OtherTopicOffset==TopicOffset)
{
Guess(keyword,alternative[l].TopicOffset);
}
}
}
}
fputc('.',stderr);
}
free(keytopic);
}
}
}
if(guessed>0)
{
fprintf(stderr,"%ld context ids found\n",guessed);
}
else
{
fputs("no context ids found\n(you may use option /g to turn off guessing on this help file)\n",stderr);
}
}
/* 1. extract topic names from topic macros, embedded pictures, and hotspot macros */
/* 2. build browse sequence start list */
/* 3. extract embedded pictures */
void FirstPass(FILE *HelpFile)
{
SYSTEMRECORD *SysRec;
TOPICLINK TopicLink;
char *LinkData1;
char *LinkData2;
char *ptr;
long l1,TopicNum,TopicPos,TopicOffset,BogusTopicOffset;
int n,i,col,cols;
BUFFER buf;
TOPICHEADER30 *TopicHdr30;
TOPICHEADER *TopicHdr;
char filename[20];
unsigned short x1,x2;
short y1;
MFILE *f;
if(extractmacros)
{
for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
{
if(SysRec->RecordType==0x0004)
{
strcpy(TopicTitle,"[CONFIG] section");
CheckMacro(SysRec->Data);
}
}
if(SearchFile(HelpFile,"|TopicId",NULL))
{
for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
{
for(i=0;i<n;i++)
{
getdw(HelpFile);
my_gets(buffer,sizeof(buffer),HelpFile);
AddTopic(buffer,FALSE);
}
}
guessing=FALSE; /* it's not neccessary to guess context ids if you know them */
}
}
browses=0;
browsenums=1;
if(!SearchFile(HelpFile,"|TOPIC",&TopicFileLength)) return;
TopicOffset=0L;
TopicPos=12L;
TopicNum=16;
while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
{
if(before31)
{
if(TopicPos+TopicLink.NextBlock>=TopicFileLength) break;
}
else
{
if(TopicLink.NextBlock<=0) break;
}
if(TopicLink.DataLen1>sizeof(TOPICLINK))
{
LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
}
else LinkData1=NULL;
if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
{
LinkData2=my_malloc(TopicLink.DataLen2+1);
if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
}
else LinkData2=NULL;
if(TopicLink.RecordType==TL_TOPICHDR) /* display a topic header record */
{
fprintf(stderr,"\rTopic %ld...",TopicNum-15);
if(before31)
{
TopicHdr30=(TOPICHEADER30 *)LinkData1;
if(resolvebrowse)
{
if(TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum>TopicNum
|| TopicHdr30->NextTopicNum==-1&&TopicHdr30->PrevTopicNum>TopicNum
|| TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum==-1)
{
AddBrowse(TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum)
{
MergeBrowse(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&(TopicHdr30->PrevTopicNum==-1||TopicHdr30->PrevTopicNum>TopicNum))
{
BackLinkBrowse(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
else if(TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum&&(TopicHdr30->NextTopicNum==-1||TopicHdr30->NextTopicNum>TopicNum))
{
LinkBrowse(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
}
}
}
else
{
BogusTopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
if(BogusTopicOffset!=TopicOffset)
{
alternative=my_realloc(alternative,(alternatives+1)*sizeof(ALTERNATIVE));
alternative[alternatives].TopicOffset=TopicOffset;
alternative[alternatives].OtherTopicOffset=BogusTopicOffset;
alternatives++;
}
TopicHdr=(TOPICHEADER *)LinkData1;
if(resolvebrowse)
{
if(TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck>TopicOffset
|| TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>TopicOffset
|| TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck==-1L)
{
AddBrowse(TopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset)
{
MergeBrowse(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>TopicOffset))
{
BackLinkBrowse(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>TopicOffset))
{
LinkBrowse(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
}
}
if(extractmacros)
{
if(TopicLink.DataLen2&&*LinkData2)
{
strlcpy(TopicTitle,LinkData2,sizeof(TopicTitle));
if(guessing)
{
Guess(LinkData2,TopicOffset);
if(BogusTopicOffset!=TopicOffset)
{
Guess(LinkData2,BogusTopicOffset);
}
}
}
else
{
strcpy(TopicTitle,"<< untitled topic >>");
}
if(TopicLink.DataLen2)
{
for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=strlen(LinkData2+i)+1)
{
CheckMacro(LinkData2+i);
}
}
}
}
TopicNum++;
}
else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
{
ptr=LinkData1;
scanlong(&ptr);
if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
{
TopicOffset+=scanword(&ptr);
}
if(TopicLink.RecordType==TL_TABLE)
{
cols=(unsigned char)*ptr++;
x1=(unsigned char)*ptr++;
switch(x1)
{
case 0: /* found in CALC.HLP and TERMINAL.HLP */
case 2:
ptr+=2;
case 1:
case 3:
break;
default:
error("Unknown TableType %d",x1);
}
ptr+=4*cols;
}
for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
{
if(TopicLink.RecordType==TL_TABLE) ptr+=5;
ptr+=4;
x2=*(unsigned short *)ptr;
ptr+=2;
if(x2&0x0001) scanlong(&ptr); /* found in MVBs, purpose */
/* unknown, may mean that x2 is really compressed long */
if(x2&0x0002) scanint(&ptr);
if(x2&0x0004) scanint(&ptr);
if(x2&0x0008) scanint(&ptr);
if(x2&0x0010) scanint(&ptr);
if(x2&0x0020) scanint(&ptr);
if(x2&0x0040) scanint(&ptr);
if(x2&0x0100) ptr+=3;
if(x2&0x0200)
{
y1=scanint(&ptr);
while(y1-->0) if(scanword(&ptr)&0x4000) scanword(&ptr);
}
while(ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK))
{
if((unsigned char)ptr[0]==0xFF)
{
ptr++;
break;
}
else switch((unsigned char)ptr[0])
{
case 0x21: /* dtype (MVB) */
case 0x80: /* font change */
ptr+=3;
break;
case 0x81:
case 0x82:
case 0x83:
case 0x89: /* end of hotspot */
case 0x8B: /* non-break-space */
case 0x8C: /* non-break-hyphen */
ptr++;
break;
case 0x86:
case 0x87:
case 0x88:
ptr++;
x1=*ptr++;
l1=scanlong(&ptr);
switch(x1)
{
case 0x22: /* HC31 */
x1=scanword(&ptr);
/* fall thru */
case 0x03: /* HC30 */
switch(((unsigned short *)ptr)[0])
{
case 1:
for(x2=1;x2<extensions;x2++) if(!extension[x2]) break;
if(x2>=extensions)
{
extension=my_realloc(extension,(x2+1)*sizeof(char));
while(extensions<=x2) extension[extensions++]=0;
}
sprintf(filename,"bm%u",x2);
f=CreateMap(ptr+2,l1-2);
x1=ExtractBitmap(filename,f);
CloseMap(f);
extension[x2]=x1|0x10;
break;
}
break;
case 0x05:
if(ptr[6]=='!'&&strchr(ptr+7,','))
{
CheckMacro(strchr(ptr+7,',')+1);
}
break;
}
ptr+=l1;
break;
case 0xC8: /* macro */
case 0xCC: /* macro without font change */
CheckMacro(ptr+3);
ptr+=*(short *)(ptr+1)+3;
break;
case 0x20: /* vfld (MVC) */
case 0xE0: /* popup jump HC30 */
case 0xE1: /* topic jump HC30 */
case 0xE2: /* popup jump HC31 */
case 0xE3: /* topic jump HC31 */
case 0xE6: /* popup jump without font change */
case 0xE7: /* topic jump without font change */
ptr+=5;
break;
case 0xEA: /* popup jump into external file */
case 0xEB: /* topic jump into external file / secondary window */
case 0xEE: /* popup jump into external file without font change */
case 0xEF: /* topic jump into external file / secondary window without font change */
switch((unsigned char)ptr[3])
{
case 0:
case 1:
break;
case 4:
StoreReference(ptr+8,TOPIC,NULL,*(long *)(ptr+4));
break;
case 6:
StoreReference(strchr(ptr+8,'\0')+1,TOPIC,NULL,*(long *)(ptr+4));
break;
default:
error("Unknown modifier %02x in tag %02x",(unsigned char)ptr[3],(unsigned char)ptr[0]);
}
ptr+=*(short *)(ptr+1)+3;
break;
default:
error("Unknown %02x",*(unsigned char *)ptr);
ptr++;
}
}
}
}
if(LinkData1) free(LinkData1);
if(LinkData2) free(LinkData2);
if(before31)
{
TopicPos+=TopicLink.NextBlock;
}
else
{
TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
TopicPos=TopicLink.NextBlock;
}
}
}
int _cdecl CTXOMAPRecCmp(const void *a,const void *b)
{
if(((CTXOMAPREC *)a)->TopicOffset<((CTXOMAPREC *)b)->TopicOffset) return -1;
if(((CTXOMAPREC *)a)->TopicOffset>((CTXOMAPREC *)b)->TopicOffset) return 1;
return 0;
}
void ContextList(FILE *HelpFile)
{
unsigned short maprecs,m;
int j,window,len;
BOOL morekeywords;
CTXOMAPREC *map;
char filename[13];
TOPICLINK TopicLink;
char *LinkData1;
char *LinkData2;
char *ptr;
long TopicPos,TopicNum,TopicOffset;
if(SearchFile(HelpFile,"|CTXOMAP",NULL))
{
maprecs=my_getw(HelpFile);
if(maprecs)
{
map=my_malloc((long)maprecs*sizeof(CTXOMAPREC));
my_fread(map,(long)maprecs*sizeof(CTXOMAPREC),HelpFile);
qsort(map,maprecs,sizeof(CTXOMAPREC),CTXOMAPRecCmp);
}
}
else
{
maprecs=0;
}
strcpy(filename,name);
strcat(filename,ext);
if(!SearchFile(HelpFile,"|TOPIC",&TopicFileLength)) return;
TopicOffset=0L;
TopicPos=12L;
TopicNum=1;
j=0;
len=80;
window=-1;
morekeywords=TRUE;
m=0;
while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
{
if(TopicLink.DataLen1>sizeof(TOPICLINK))
{
LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
}
else LinkData1=NULL;
if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
{
LinkData2=my_malloc(TopicLink.DataLen2+1);
if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
}
else LinkData2=NULL;
if(TopicLink.NextBlock<=0||TopicLink.RecordType==TL_TOPICHDR) /* display a topic header record */
{
if(TopicNum>1) putchar('\n');
if(before31) TopicOffset=TopicPos;
while(m<maprecs&&map[m].TopicOffset<TopicOffset)
{
printf(" WinHelp(wnd,\"%s\",HELP_CONTEXT,%lu)\n",filename,map[m].MapID);
m++;
}
if(!before31)
{
while(j<ContextRecs&&ContextRec[j].TopicOffset<TopicOffset)
{
if(len==80)
{
fputs(" Jump",stdout);
}
else
{
fputs(" Popup",stdout);
}
printf("Id(`%s",filename);
if(window!=-1) printf(">%s",GetWindowName(window));
printf("',`%s')\n",unhash(ContextRec[j].HashValue));
j++;
}
if(morekeywords)
{
if(NextKeywordRec>=KeywordRecs)
{
if(NextKeywordOffset<0x7FFFFFFFL)
{
CollectKeywords(HelpFile);
}
else
{
morekeywords=FALSE;
}
}
while(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset<TopicOffset)
{
if(KeywordRec[NextKeywordRec].Footnote=='K')
{
printf(" JumpKeyword(`%s',`%s')\n",filename,KeywordRec[NextKeywordRec].Keyword);
}
else if(KeywordRec[NextKeywordRec].Footnote=='A')
{
printf(" ALink(`%s@%s')\n",KeywordRec[NextKeywordRec].Keyword,filename);
}
NextKeywordRec++;
}
}
window=ListWindows(HelpFile,TopicOffset);
}
if(TopicLink.NextBlock<=0) break;
if(LinkData2&&*LinkData2)
{
printf("Topic %ld: %s",TopicNum,LinkData2);
len=80;
}
else
{
len=printf("Topic %ld: untitled: ",TopicNum);
}
TopicNum++;
}
else if(LinkData2&&len<79)
{
for(ptr=LinkData2;len<79&&ptr<LinkData2+TopicLink.DataLen2;ptr++) if(*ptr)
{
putchar(*ptr);
len++;
}
}
if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
{
ptr=LinkData1;
scanlong(&ptr);
TopicOffset+=scanword(&ptr);
}
if(LinkData1) free(LinkData1);
if(LinkData2) free(LinkData2);
if(before31)
{
if(TopicPos+TopicLink.NextBlock>=TopicFileLength) break;
TopicPos+=TopicLink.NextBlock;
}
else
{
if(TopicLink.NextBlock<=0) break;
TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
TopicPos=TopicLink.NextBlock;
}
}
}
BOOL HelpDeCompile(FILE *HelpFile,char *dumpfile,int mode,char *exportname,long offset)
{
char filename[81];
char hpjfilename[81];
long FileLength;
FILE *rtf;
FILE *hpj;
int d;
long topic;
if(!SearchFile(HelpFile,NULL,&FileLength)) return FALSE;
if(!dumpfile)
{
switch(mode)
{
case 0:
SysLoad(HelpFile);
fprintf(stderr,"Decompiling %s...\n",HelpFileTitle);
ContextLoad(HelpFile);
PhraseLoad(HelpFile);
ExportBitmaps(HelpFile);
fputs("Pass 1...\n",stderr);
FirstPass(HelpFile); /* valid only after ExportBitmaps */
putc('\n',stderr);
if(!before31&&guessing) GuessFromKeywords(HelpFile); /* after FirstPass, before SysList */
strcpy(hpjfilename,name);
if(mvp)
{
strcat(hpjfilename,".MVP");
}
else
{
strcat(hpjfilename,".HPJ");
}
hpj=my_fopen(hpjfilename,"wt");
if(hpj)
{
strcpy(filename,name);
strcat(filename,".ICO");
SysList(HelpFile,hpj,filename); /* after ContextLoad */
ListBaggage(HelpFile,hpj,before31);
if(!mvp) AliasList(hpj); /* after ContextLoad, before TopicDump */
strcpy(filename,name);
strcat(filename,".PH");
PhraseList(filename); /* after PhraseLoad */
BuildName(filename,TopicsPerRTF>0);
rtf=my_fopen(filename,"wt");
if(rtf)
{
FontLoad(HelpFile,rtf,hpj);
fputs("Pass 2...\n",stderr);
fprintf(hpj,"[FILES]\n%s\n\n",filename);
rtf=TopicDump(HelpFile,rtf,hpj,FALSE);
putc('}',rtf);
putc('\n',stderr);
my_fclose(rtf);
}
NotInAnyTopic=FALSE;
CTXOMAPList(HelpFile,hpj);
if(extensions&&before31) ListBitmaps(hpj);
if(win95) ListRose(HelpFile,hpj);
my_fclose(hpj);
}
if(PhraseOffsets)
{
if(win95)
{
puts("Help Compiler will issue Note HC1002: Using existing phrase table");
}
else
{
puts("Help Compiler will issue Warning 5098: Using old key-phrase table");
}
}
if(missing) puts("Help Compiler will issue Error 1230: File 'missing.bmp' not found");
if(NotInAnyTopic) puts("Help Compiler will issue Warning 4098: Context string(s) in [MAP] section not defined in any topic");
if(!extractmacros) puts("Help Compiler may issue Warning 4131: Hash conflict between 'x' and 'y'.");
if(warnings)
{
printf("HELPDECO had problems with %s. Rebuilt helpfile may behave bad.\n",HelpFileName);
}
if(helpcomp[0])
{
if(win95&&SearchFile(HelpFile,"|Petra",NULL)) strcat(helpcomp," /a");
printf("Use %s %s to recompile ",helpcomp,hpjfilename);
if(AnnoFile) fputs("annotated ",stdout);
puts("helpfile.");
}
break;
case 1:
HexDump(HelpFile,FileLength,offset);
break;
case 2:
ListFiles(HelpFile);
break;
case 3: /* create lookalike RTF */
SysLoad(HelpFile);
fprintf(stderr,"Writing %s...\n",HelpFileTitle);
exportplain=TRUE;
ExportBitmaps(HelpFile);
PhraseLoad(HelpFile);
BuildName(filename,TopicsPerRTF>0);
rtf=my_fopen(filename,"wt");
if(rtf)
{
FontLoad(HelpFile,rtf,NULL);
rtf=TopicDump(HelpFile,rtf,NULL,TRUE);
putc('}',rtf);
putc('\n',stderr);
my_fclose(rtf);
}
break;
case 4: /* generate contents file */
SysLoad(HelpFile);
fprintf(stderr,"Scanning %s...\n",HelpFileTitle);
ContextLoad(HelpFile);
PhraseLoad(HelpFile);
checkexternal=TRUE;
ExportBitmaps(HelpFile);
FirstPass(HelpFile);
putc('\n',stderr);
if(!before31&&guessing) GuessFromKeywords(HelpFile); /* after FirstPass, before SysList */
strcpy(filename,name);
strcat(filename,".CNT");
rtf=my_fopen(filename,"wt");
if(rtf)
{
GenerateContent(HelpFile,rtf);
my_fclose(rtf);
}
break;
case 5: /* create entry point list */
resolvebrowse=FALSE;
checkexternal=TRUE;
SysLoad(HelpFile);
fprintf(stderr,"Parsing %s...\n",HelpFileTitle);
ContextLoad(HelpFile);
PhraseLoad(HelpFile);
ExportBitmaps(HelpFile);
FirstPass(HelpFile);
putc('\n',stderr);
if(!before31&&guessing) GuessFromKeywords(HelpFile); /* after FirstPass, before SysList */
ContextList(HelpFile);
break;
case 6: /* check external references */
case 7:
resolvebrowse=FALSE;
checkexternal=TRUE;
SysLoad(HelpFile);
fprintf(stderr,"Checking %s...\n",HelpFileTitle);
PhraseLoad(HelpFile);
FirstPass(HelpFile);
putc('\n',stderr);
if(!external)
{
printf("No references to external files found in %s.\n",HelpFileName);
}
else if(mode==6)
{
CheckReferences();
}
else
{
ListReferences();
}
break;
}
}
else
{
if(!SearchFile(HelpFile,dumpfile,&FileLength))
{
filename[0]='|';
strlcpy(filename+1,dumpfile,sizeof(filename)-1);
if(!SearchFile(HelpFile,filename,&FileLength))
{
fprintf(stderr,"Internal file %s not found.\n",dumpfile);
return TRUE;
}
dumpfile=filename;
}
printf("FileName: %s FileSize: %ld\n",dumpfile,FileLength);
if(exportname) /* export internal file */
{
FILE *f;
f=my_fopen(exportname,"wb");
if(f)
{
copy(HelpFile,FileLength,f);
my_fclose(f);
}
}
else if(mode==1)
{
HexDump(HelpFile,FileLength,offset);
}
else if(strcmp(dumpfile,"|TOPIC")==0)
{
SysLoad(HelpFile);
PhraseLoad(HelpFile);
DumpTopic(HelpFile,offset);
}
else if(strcmp(dumpfile+strlen(dumpfile)-4,".GRP")==0)
{
GroupDump(HelpFile);
}
else if(strcmp(dumpfile,"@LINK")==0)
{
LinkDump(HelpFile);
}
else if(sscanf(dumpfile,"%ld!%d",&topic,&d)==2&&topic!=0L&&d==0)
{
AnnotationDump(HelpFile,FileLength,dumpfile);
}
else if(strcmp(dumpfile,"|Phrases")==0||strcmp(dumpfile,"|PhrIndex")==0)
{
SysLoad(HelpFile);
PhraseLoad(HelpFile);
PhraseDump();
}
else if(strcmp(dumpfile,"|SYSTEM")==0)
{
SysDump(HelpFile);
}
else if(strcmp(dumpfile,"|TOMAP")==0)
{
ToMapDump(HelpFile,FileLength);
}
else if(strcmp(dumpfile,"|CONTEXT")==0)
{
BTreeDump(HelpFile,"ContextId: %h TopicOffset: 0x%08lx\n");
}
else if(dumpfile[0]=='|'&&(strcmp(dumpfile+2,"WBTREE")==0||strcmp(dumpfile+2,"KWBTREE")==0))
{
fseek(HelpFile,7,SEEK_CUR);
d=getc(HelpFile);
fseek(HelpFile,-8,SEEK_CUR);
if(d=='!')
{
BTreeDump(HelpFile,"Keyword: '%s' Occurrance: %!\n");
}
else
{
BTreeDump(HelpFile,"Keyword: '%s' Count: %u KWDataAddress: 0x%08lx\n");
}
}
else if(dumpfile[0]=='|'&&(strcmp(dumpfile+2,"WMAP")==0||strcmp(dumpfile+2,"KWMAP")==0))
{
KWMapDump(HelpFile);
}
else if(dumpfile[0]=='|'&&(strcmp(dumpfile+2,"WDATA")==0||strcmp(dumpfile+2,"KWDATA")==0))
{
KWDataDump(HelpFile,FileLength);
}
else if(strcmp(dumpfile,"|VIOLA")==0)
{
BTreeDump(HelpFile,"TopicOffset: 0x%08lx WindowNumber: %ld\n");
}
else if(strcmp(dumpfile,"|CTXOMAP")==0)
{
CTXOMAPDump(HelpFile);
}
else if(strcmp(dumpfile,"|CATALOG")==0)
{
CatalogDump(HelpFile);
}
else if(strcmp(dumpfile,"|Petra")==0)
{
BTreeDump(HelpFile,"TopicOffset: 0x%08lx SourceFileName: %s\n");
}
else if(strcmp(dumpfile,"|TopicId")==0)
{
BTreeDump(HelpFile,"TopicOffset: 0x%08lx ContextId: %s\n");
}
else if(strcmp(dumpfile,"|Rose")==0)
{
BTreeDump(HelpFile,"KeywordHashValue: 0x%08lx\nMacro: %s\nTitle: %s\n");
}
else if(strcmp(dumpfile,"|TTLBTREE")==0)
{
BTreeDump(HelpFile,"TopicOffset: 0x%08lx TopicTitle: %s\n");
}
else if(strcmp(dumpfile,"|FONT")==0)
{
FontDump(HelpFile);
}
else /* generic */
{
topic=ftell(HelpFile);
if(my_getw(HelpFile)==0x293B) /* if it's a B+ tree */
{
my_getw(HelpFile);
my_getw(HelpFile);
filename[0]='\0';
while((d=getc(HelpFile))>0) /* format according to Structure */
{
switch(d)
{
case 'L':
case '4':
case 'a':
strcat(filename,"0x%08lx ");
break;
case '2':
strcat(filename,"%5u ");
break;
case 'F':
case 'i':
case 'z':
strcat(filename,"'%s' ");
break;
case '!':
strcat(filename,"%!");
break;
default:
error("Unknown Btree field type '%c'",d);
}
}
strcat(filename,"\n");
fseek(HelpFile,topic,SEEK_SET);
BTreeDump(HelpFile,filename);
}
else
{
fseek(HelpFile,topic,SEEK_SET);
HexDump(HelpFile,FileLength,offset);
}
}
}
return TRUE;
}
int _cdecl main(int argc,char *argv[])
{
char AnnoFileName[81];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
FILE *f;
int mode;
BOOL annotate;
char *filename;
char *dumpfile;
char *exportname;
long offset;
int i,j;
/* initialize hash value coding oldtable */
memset(oldtable,0,sizeof(oldtable));
for(i=0;i<9;i++) oldtable['1'+i]=i+1;
oldtable['0']=10;
oldtable['.']=12;
oldtable['_']=13;
for(i=0;i<26;i++) oldtable['A'+i]=oldtable['a'+i]=17+i;
exportname=dumpfile=filename=NULL;
AnnoFileName[0]='\0';
mode=0;
offset=0;
annotate=FALSE;
/* scan arguments */
for(i=1;i<argc;i++)
{
if(argv[i][0]=='/'||argv[i][0]=='-')
{
switch(tolower((unsigned char)argv[i][1]))
{
case 'a':
if(argv[i][2])
{
strlcpy(AnnoFileName,argv[i]+2,sizeof(AnnoFileName));
}
else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
{
strlcpy(AnnoFileName,argv[i+1],sizeof(AnnoFileName));
i++;
}
annotate=TRUE;
break;
case 'b':
resolvebrowse=FALSE;
break;
case 'c':
mode=4;
break;
case 'd':
mode=2;
break;
case 'e':
mode=7;
break;
case 'f':
listtopic=TRUE;
break;
case 'g':
guessing=FALSE;
break;
case 'h': // add entry to prefix table
for(j=0;j<sizeof(prefix)/sizeof(prefix[0])&&prefix[j];j++) ;
if(j<sizeof(prefix)/sizeof(prefix[0]))
{
if(argv[i][2])
{
prefix[j]=argv[i]+2;
}
else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
{
prefix[j]=argv[i+1];
i++;
}
}
else
{
fprintf(stderr,"Prefix table full.\n");
}
break;
case 'i':
reportderived=TRUE;
break;
case 'l':
mode=5;
break;
case 'm':
extractmacros=FALSE;
break;
case 'n':
nopagebreak=TRUE;
break;
case 'p':
mode=6;
break;
case 'r':
mode=3;
break;
case 's':
if(argv[i][2])
{
TopicsPerRTF=atoi(argv[i]+2);
}
else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
{
TopicsPerRTF=atoi(argv[i+1]);
i++;
}
break;
case 't':
if(argv[i][2])
{
offset=strtoul(argv[i]+2,NULL,0);
}
else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
{
offset=strtoul(argv[i+1],NULL,0);
i++;
}
break;
case 'x':
mode=1;
break;
case 'y':
overwrite=TRUE;
break;
case 'z':
exportLZ77=TRUE;
break;
default:
fprintf(stderr,"unknown option '%s' ignored\n",argv[i]);
}
}
else if(exportname)
{
fprintf(stderr,"additional parameter '%s' ignored\n",argv[i]);
}
else if(dumpfile)
{
exportname=argv[i];
}
else if(filename)
{
dumpfile=argv[i];
}
else
{
filename=argv[i];
}
}
if(filename)
{
strupr(filename);
_splitpath(filename,drive,dir,name,ext);
if(ext[0]=='\0') strcpy(ext,".HLP");
mvp=ext[1]=='M';
_makepath(HelpFileName,drive,dir,name,ext);
f=fopen(HelpFileName,"rb");
if(f)
{
if(annotate)
{
if(AnnoFileName[0]=='\0') _makepath(AnnoFileName,drive,dir,name,".ANN");
AnnoFile=fopen(AnnoFileName,"rb");
if(!AnnoFile)
{
fprintf(stderr,"Couldn't find annotation file '%s'\n",AnnoFileName);
}
}
prefixhash[0]=0L;
for(i=1;prefix[i];i++)
{
prefixhash[i]=hash(prefix[i]);
}
if(!HelpDeCompile(f,dumpfile,mode,exportname,offset))
{
fprintf(stderr,"%s isn't a valid WinHelp file !\n",HelpFileName);
}
if(annotate&&AnnoFile) fclose(AnnoFile);
my_fclose(f);
}
else
{
fprintf(stderr,"Can not open '%s'\n",HelpFileName);
}
}
else
{
fprintf(stderr,"HELPDECO - decompile *.HLP/*.MVB files of Windows 3.x / 95 - %d bit Version 2.1\n"
"M.Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany, CIS 100326,2776\n"
"\n"
"usage: HELPDECO helpfile[.hlp] [/y] - decompile helpfile into all sources\n"
" HELPDECO helpfile[.hlp] [/y] /a[annfile.ANN] - and add annotations\n"
" HELPDECO helpfile[.hlp] /r [/y] [/n] - decompile into lookalike RTF\n"
" HELPDECO helpfile[.hlp] /c [/y] - generate Win95 .CNT content file\n"
" HELPDECO helpfile[.hlp] /l - list entry points of this helpfile\n"
" HELPDECO helpfile[.hlp] /e [/f] - list references to other helpfiles\n"
" HELPDECO helpfile[.hlp] /p [/f] - check references to other helpfiles\n"
" HELPDECO helpfile[.hlp] /d [/x] - display internal directory\n"
" HELPDECO helpfile[.hlp] \"internalfile\" [/x] - display internal file\n"
" HELPDECO helpfile[.hlp] \"internalfile\" filename - export internal file\n"
"options: /y overwrite without warning, /f list referencing topics, /x hex dump\n"
" /g no guessing, /hprefix add known contextid prefix, /n no page breaks\n"
"To recreate all source files neccessary to rebuild a Windows helpfile, create\n"
"a directory, change to this directory and call HELPDECO with the path and name\n"
"of the helpfile to dissect. HELPDECO will extract all files contained in the\n"
"helpfile in two passes and deposit them in the current directory. You may then\n"
"rebuild the helpfile using the appropriate help compiler HC30, HC31, HCP, HCW,\n"
"HCRTF, MVC, WMVC or MVCC. The file will not be identical, but should look and\n"
"work like the original. This program is Freeware. Use at your own risk. No\n"
"part of it may be used commercially. No fees may be charged on distributing.\n"
#ifndef _WIN32
"Launch from Windows 95/Windows NT command line to handle larger helpfiles."
#endif
,sizeof(int)*8);
}
return 0;
}