home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 September / PCWorld_2002-09_cd.bin / Software / Vyzkuste / helpdeco / HELPDECO.C < prev    next >
Text File  |  1997-01-27  |  178KB  |  6,105 lines

  1. /*
  2. HELPDECO - Utility-Programm zum Zerlegen von Windows Hilfedateien
  3. HELPDECO - utility program to dissect Windows help files
  4.  
  5. HELPDECO zerlegt HLP-Hilfedateien von Windows 3.0, 3.1, 3.11 und '95 und
  6. viele MVB-Dateien des Multimedia-Viewers in alle für den jeweiligen
  7. Hilfecompiler HC30, HC31, HCP, HCW, HCRTF, WMVC, MMVC oder MVC zum
  8. erneuten Zusammenbau erforderlichen Dateien. Dazu gehören:
  9. HPJ - die Projektdatei, als Parameter für den Hilfecompiler anzugeben
  10. MVP - die Multimediaprojektdatei, als Parameter für den MM-Compiler
  11. RTF - die Textdatei mit dem gesamten Hilfetext und allen Fußnoten
  12. PH  - die Phrasen-Datei (wie sie auch vom Hilfecompiler erzeugt wird)
  13. ICO - ein eventuell der Hilfedatei zugeordnetes Icon
  14. BMP/WMF/SHG/MRB - alle Bilder in Dateien mit passendem Format
  15. Baggage - alle als Baggage in der Hilfedatei enthaltenen Dateien
  16.  
  17. HELPDECO dissects HLP help files of Windows 3.0, 3.1, 3.11, and '95 and
  18. many MVB multi media viewer titles into all files required for a rebuild
  19. using the appropriate help compiler HC30, HC31, HCP, HCW, HCRTF, WMVC,
  20. MMVC or MVC:
  21. HPJ - help project file, use as parameter when calling help compiler
  22. MVP - multi media project file, parameter for multi media help compiler
  23. RTF - text file containing whole content of help file and all footnotes
  24. PH  - phrases file (same as produced by help compiler)
  25. ICO - icon of help file if embedded
  26. BMP/WMF/SHG/MRB - embedded pictures in appropriate format
  27. Baggage - all baggage files contained in help file
  28.  
  29. HELPDECO wird von der MS-DOS Kommandozeile aus mit dem Namen der zu
  30. bearbeitenden Datei, eventuell dem Namen einer internen Datei und
  31. eventuellen Optionen aufgerufen. HELPDECO läuft von der Kommandozeile
  32. von Windows 95 oder Windows NT als 32-bit Applikation zur Bearbeitung
  33. größerer Hilfedateien.
  34. Call HELPDECO from MS-DOS command line. Supply name of help file to use,
  35. optional name of internal file, and options if appropriate.
  36. HELPDECO runs from Windows 95 or Windows NT command line as 32-bit
  37. application to handle larger help files.
  38.  
  39. HELPDECO
  40. Zeigt Benutzungshinweise
  41. Displays usage
  42.  
  43. HELPDECO helpfilename
  44. Zerlegt die Hilfedatei in alle zum erneuten Zusammenbau benötigten Dateien.
  45. Diese Dateien werden im aktuellen (möglichst leeren) Verzeichnis abgelegt.
  46. Existierende Dateien werden ohne Rückfrage überschrieben wenn die Option /y
  47. angegeben wird.
  48. Decompiles help file into all sources needed for a rebuild. All files are
  49. created in current directory (should be empty). Existing files will be
  50. overwritten without asking if option /y was specified.
  51.  
  52. Hinter dem Kommando können durch Leerzeichen abgesetzte Optionen angeben
  53. werden:
  54. Options may be appended to the command, separated using blanks, as follows:
  55.  
  56. /m
  57. kann verwendet werden, um das Durchsuchen von macros nach Topicnamen zu
  58. verhindern, wenn dabei Probleme auftreten. Hilfecompiler wird Warnung 4131
  59. melden.
  60. May be used to stop parsing macros for topic names. Help compiler will emit
  61. Warning 4131.
  62.  
  63. /b
  64. kann verwendet werden, um das Auflösen von Browse-Sequenzen zu verhindern,
  65. wenn dabei Probleme auftreten. Hilfequelltextdatei enthält dann keine +
  66. Fußnoten.
  67. May be used to stop resolving browse sequences. Help source file than
  68. contains no + footnotes.
  69.  
  70. /g
  71. Schaltet das zeitintensive Raten von Kontextnamen aus. Anzuwenden wenn
  72. HELPDECO 'no context ids found' meldet oder man nicht an lesbaren
  73. Kontextnamen interessiert ist. Die Funktionsweise von HELPDECO wird dadurch
  74. nicht beeinträchtigt.
  75. Zum Raten versucht HELPDECO aus Kapitelüberschriften und Schlüsselwörten
  76. die Kontextnamen zu rekonstruieren, was bei einigen Hilfedateien sehr gut,
  77. bei anderen gar nicht funktioniert, je nach Hilfedateierstellungswerkzeug
  78. oder Arbeitsmethode des Hilfeautors.
  79. Turns off time consuming guessing of context names. Applicable when
  80. HELPDECO reports 'no context ids found' or when you are not interested in
  81. legible context ids. This doesn't affect functionality.
  82. During guessing HELPDECO tries to reconstruct context ids from topic titles
  83. and keywords. This may produce good results on some help files and no result
  84. at all on others, depending on autoring tool used or working method of help
  85. author.
  86.  
  87. /i
  88. Wenn man sehen möchte, welche Kontextnamen HELPDECO errät...
  89. If you want to see which context ids are guessed by HELPDECO...
  90.  
  91. /hprefix
  92. HELPDECO versucht aus Kapitelüberschriften und Schlüsselwörtern Kontextnamen
  93. zu erraten, wobei auch idh_ oder helpid_ vorangestellt werden.
  94. Wenn die Hilfedatei mit anderen bekannten Prefixen erstellt wurde, kann
  95. die Tabelle durch die Option /h prefix erweitert werden.
  96. HELPDECO tries to guess context ids from topic titles and keywords, possibly
  97. prefixed by idh_ or helpid_. If the help file was created using other
  98. prefixes, they may be added to the table using the /h prefix option.
  99.  
  100. /a [annotationfilename.ANN]
  101. Fügt zusätzlich alle Anmerkungen aus der angegebenen Anmerkungsdatei als
  102. Anmerkungen des Benutzers ANN in die RTF-Datei ein. Fehlt der annotation-
  103. filename, verwendet HELPDECO helpfilename.ANN dafür.
  104. Adds all annotations from annotationfile as annotations of user ANN into
  105. RTF file. Default annotationfilename is helpfilename.ANN.
  106.  
  107. /s n
  108. Zerteilt die Ausgabe alle n Seiten in eine eigene RTF Datei, z.B. zerlegt
  109.   HELPDECO helpdemo /s 100
  110. die Datei helpdemo.hlp und erzeugt RTF-Dateien mit den Namen helpdem1.rtf,
  111. helpdem2.rtf,.. die jeweils höchstens 100 Seiten enthalten.
  112. Splits output every n pages into a separate RTF file. For example
  113.   HELPDECO helpdemo /s 100
  114. decompiles helpdeco.hlp and creates different RTF files helpdem1.rtf,
  115. helpdem2.rtf,.. each containing no more than 100 topics.
  116.  
  117. HELPDECO helpfilename /r
  118. Erzeugt aus der Hilfedatei eine RTF-Datei, die von WinWord geladen dasselbe
  119. Aussehen hat wie die von WinHelp angezeigten Hilfeseiten. Damit kann eine
  120. Hilfedatei komplett gedruckt oder weiterverarbeitet werden. Zusätzlich
  121. Option /n angeben, wenn an Kapitelgrenzen kein Seitenwechsel stattfinden soll.
  122. Converts help file into RTF file of same appearance if loaded into WinWord
  123. as if displayed by WinHelp. To print or work with complete content. Specify
  124. additional option /n, if no page breaks should separate topics.
  125.  
  126. HELPDECO helpfilename /c
  127. Erzeugt aus der Hilfedatei eine *.CNT-Datei für WinHlp32, die alle Kapitel
  128. mit Überschriften in der Reihenfolge enthält, in der sie in der Hilfedatei
  129. auftreten. Die Datei muß dann mit HCW 4.00 oder einem Texteditor in eine
  130. hierarchische Struktur überarbeitet werden.
  131. Generates a *.CNT file used by WinHlp32, containing all chapters that have
  132. titles assigned in the order they appear in the helpfile. This file should
  133. then be edited using HCW 4.00 or any text editor into a hierarchical order.
  134.  
  135. HELPDECO helpfilename /e
  136. Zeigt alle Referenzen auf externe Hilfedateien.
  137. Lists all references to external help files.
  138.  
  139. HELPDECO helpfilename /e /f
  140. Zeigt alle Referenzen auf externe Hilfedateien und die Titel der Topics in
  141. denen sie auftraten.
  142. Lists all references to external help files and titles of topics that
  143. contained these references.
  144.  
  145. HELPDECO helpfilename /p
  146. Prüft Referenzen auf externe Hilfedateien. Die referenzierten Hilfedateien
  147. müssen für HELPDECO zugreifbar sein. Dieser Aufruf erzeugt keine neuen
  148. Dateien und modifiziert keine existierenden. Fehler werden auf stdout (den
  149. Bildschirm) geschrieben.
  150. Checks references to external help files. Referenced help file need to be
  151. available to HELPDECO. This call doesn't produce any new files and doesn't
  152. modify existing files. Errors are reported to stdout (screen).
  153.  
  154. HELPDECO helpfilename /d
  155. Zeigt das interne Inhaltsverzeichnis der Hilfedatei. Es kann auch eine
  156. *.MVB,*.M??,*.ANN,*.CAC,*.AUX Datei anstelle der *.HLP-Datei angegeben
  157. werden.
  158. Displays internal directory of help file. You may supply a *.MVB,*.M??,
  159. *.ANN,*.CAC,*.AUX file instead of a *.HLP file.
  160.  
  161. HELPDECO helpfilename /x
  162. Zeigt das interne Inhaltsverzeichnis als HexDump
  163. Displays hex dump of internal directory
  164.  
  165. HELPDECO helpfilename "internalfilename"
  166. Zeigt die genannte interne Datei in einem passenden Format an, soweit die
  167. interne Datei anzeigbar ist, sonst als HexDump. Sie können die Ausgabe in
  168. eine Datei umleiten durch anhängen von >outfile.
  169. Displays internal file in appropriate format if known, else hex dump. You
  170. may redirect output into a file using >outfile.
  171.  
  172. HELPDECO helpfilename "internalfilename" /x
  173. Zeigt die genannte interne Datei als HexDump
  174. Displays hex dump of internal file
  175.  
  176. HELPDECO helpfilename "internalfilename" /x /t offset
  177. Zeigt die genannte interne Datei als HexDump beginnend bei Position
  178. offset, der als Dezimalzahl oder mit vorangestelltem 0x als Hexadezimal-
  179. zahl akzeptiert wird.
  180. Displays hex dump of internal file starting at offset, which may be
  181. specified in decimal or preceeded with 0x in hex.
  182.  
  183. HELPDECO helpfilename "internalfilename" filename
  184. Exportiert die genannte interne Datei in filename
  185. Exports internal file into filename
  186.  
  187. *.ANN, *.CAC, *.AUX
  188. Diese Dateien sind auch wie Hilfedateien formatiert, HELPDECO kann aber nur
  189. verwendet werden, um ihr Inhaltsverzeichnis anzuzeigen oder um einzelne
  190. Dateien anzuzeigen und zu exportieren.
  191. These files are formatted like helpfiles, but HELPDECO can only be used to
  192. display their internal directory or display or export internal files.
  193.  
  194. HELPDECO wurde erstellt von / was written by
  195. Manfred Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany
  196. CIS 100326,2776
  197.  
  198. Wenn Sie weitere Fragen, Probleme (oder Antworten!) haben, können Sie
  199. durch eine EMail an 100326.2776@compuserve.com mit mir in Kontakt treten.
  200. If you have more questions, problems (or answers!), please feel free to
  201. send me an EMail to 100326.2776@compuserve.com
  202.  
  203. HELPDECO basiert auf HELPDUMP von Pete Davis veröffentlicht in:
  204. HELPDECO is based upon HELPDUMP from Pete Davis published in:
  205. The Windows Help File Format, Dr. Dobbs Journal, Sep/Oct 1993
  206. Thanks to Holger Haase, who did a lot of work on picture file formats.
  207. And thanks to Jürgen Müller for pointing out 32-bit differences.
  208. Thanks to Bent Lynggaard for the information on help file free lists
  209. and his contribution on context id guessing.
  210.  
  211. HELPFILE.TXT enthält eine Beschreibung des Windows Hilfedateiformats
  212. wie HELPDECO es versteht und weiterer Dateiformate wie MRB/SHG.
  213. See HELPFILE.TXT for a description of the Windows help file format
  214. as parsed by HELPDECO and related file formats like MRB/SHG.
  215.  
  216. Die neueste Version von HELPDECO befindet sich stets in:
  217. The newest public version of HELPDECO is always available at:
  218. CompuServe: Dr. Dobbs Journal DDJFOR Undocumented Corner HELPDCxx.ZIP
  219. InterNet: ftp://gmutant.wrlc.org/pub/winhelp
  220.  
  221. HELPDECO ist Freeware. Der Einsatz erfolgt auf eigene Gefahr. Kein
  222. Programmteil darf kommerziell verwendet werden. Für das Kopieren dürfen
  223. keine Gebühren verlangt werden (Sharewarehandel Finger weg).
  224. HELPDECO is freeware. Use at your own risk. No part of the program may be
  225. used commercially. No fees may be charged on distributing the program
  226. (shareware distributors keep off).
  227.  
  228. Die Verwendung von Inhalten zerlegter Hilfedateien kann eine Verletzung
  229. des Urheberrechtes bedeuten.
  230. The use of parts of decompiled help files may constitute a violation of
  231. copyright law.
  232.  
  233. Version 2.1:
  234. macros may contain rtf meta characters
  235. Win95 topic names may contain (nearly) all characters
  236. 0x8000 in SWin->Maximize allowed
  237. ForeHelp creates PhrIndex/PhrImage _and_ _empty_ Phrases file
  238. HCRTF complained about > footnotes before # footnotes
  239. macro parsing changed again
  240.  
  241. Version 2.0:
  242. Guess context ids from titles and keywords based on idea of Bent Lynggaard
  243. Recompiled 16 bit EXE without register calling convention. BC++ 3.1 bug.
  244. Doesn't print last (stray) topic of HC30 help files
  245. Can list entry points into this help file (option /l)
  246.  
  247. Version 1.9: faster & better than ever...
  248. changed TopicPos, TopicOffset, Keyword maintenance
  249. changed unhash to 40 bit integer arithmetic
  250. fixed keyword footnotes [Bent Lynggaard]
  251. no [ALIAS] in MVP files
  252. no hidden text in option /r RTF files
  253. some changes in font and stylesheet handling
  254. corrects rounding error of HC31 on negative values
  255. handles non-underlined topic jumps
  256.  
  257. Version 1.8: used some spare days to clean up the to-do list...
  258. better tracking of TopicOffset during decompilation
  259. lists and checks references to external files, shows referencing topics
  260. can add annotations from .ANN file to decompiled .RTF file
  261. fixed bug in handling of pictures containing JumpId-macro hotspots
  262. changed parsing of macros (3rd attempt to guess what Microsoft did)
  263. fixed bug in popup/jump to external file / secondary window
  264. fixed bug in > footnote / |VIOLA internal file handling
  265. fixed bug in keyword assignment
  266. now removes LZ77 compression from exported SHGs/MRBs
  267. recreates Win 95 (HCW 4.00) [MACROS] section from internal |Rose file
  268. 32 bit version available
  269. handles LANGUAGE, [CHARTAB] and [GROUP] section of media view files
  270.  
  271. Version 1.7
  272. removed unneccessary output statement
  273.  
  274. Version 1.6 can now check references to external help files plus:
  275. duplicate macro names preceeding picture hotspot info skipped
  276. does not write Win95 commands to multi-media help project files
  277. changed unhash to circumvent Microsoft-C++ float rounding error
  278. handles keywords defined inside topic text
  279.  
  280. Version 1.5
  281. fixed static on buffer of TopicName function (affected HC30 files)
  282.  
  283. Version 1.4 fixes some bugs reported by different users:
  284. buffer overflow in expanding LZ77&RunLen (byPacked 3) images fixed
  285. embedded images {bmxwd} larger than 32k supported
  286. extract topic names from jump into external file if no file specified
  287. handles more phrases on HCRTF generated (Win95) help files
  288. Windows 3.1 (HC31) |Phrases always Zeck compressed
  289. LinkData2 buffer enlarged 1 byte to store trailing NUL character
  290.  
  291. Version 1.3
  292. parses examples of {bmc} etc. statements contained in help text correctly
  293. can now generate a *.CNT content file for Windows 95 / WinHlp32
  294. Microsoft C: ctype macros (isalnum/isprint) don't work with signed char
  295.  
  296. Version 1.2 fixes some severe bugs introduced in version 1.1 and:
  297. tells you which help compiler to use
  298. collects multiple keyword footnotes into single lines
  299. handles \r\n in COPYRIGHT
  300. converts SPC-macro (but only in [CONFIG] section)
  301. does not generate duplicate MAP-statements if possible
  302. {button} and {mci,mci_left,mci_right} commands supported
  303. [BITMAP]-section in HCRTF help files irritated transparent bitmaps
  304.  
  305. Version 1.1 now supports more features of Win95/HCRTF 4.00/WinHlp32:
  306. Supports LCID, CHARSET, AUTO-SIZE HEIGHT, CNT, INDEX_SEPARATORS
  307. Additional Win95 Macros (to extract original topic names)
  308. [CONFIG:n] of Win95 supported (internal file |CFn)
  309. Secondary windows with > footnote supported (internal file |VIOLA)
  310. Transparent bitmaps supported (bmct,bmlt,bmrt)
  311. Expanded internal limits as HCRTF allows larger items
  312. Now does RunLen compressed device dependend bitmaps
  313. Bugs in handling of metafiles removed
  314. Bug in placement of pack(1) removed
  315. Parsing of macros changed (is it really better now ?)
  316. */
  317. #include "helpdeco.h"
  318.  
  319. /* neccessary compiler options for 16 bit version using Borland C/C++:
  320. //   bcc -ml -K -Os -p helpdeco.c helpdec1.c
  321. // Don't compile using Register Calling Convention in BC3.1: compiler bug.
  322. // To compile 32 bit version using Microsoft VC4.0 create a new workspace
  323. // for a WIN32 console application, insert HELPDECO.C and HELPDEC1.C into
  324. // it and compile. Use /KNOWEAS 16 bit version as stub to create dual-mode
  325. // application.
  326. // Byte align ! Portable to little endian machines only.
  327. */
  328.  
  329. FILEREF *external;
  330. char HelpFileName[81];
  331. char name[_MAX_FNAME];
  332. char ext[_MAX_EXT];
  333. FILE *AnnoFile;
  334. HASHREC *hashrec;
  335. int hashrecs=0;
  336. BROWSE *browse;
  337. int browses;
  338. int browsenums;
  339. long scaling;
  340. int rounderr;
  341. START *start;
  342. int starts;
  343. BOOL lzcompressed,Hall;
  344. BOOL before31,after31;
  345. BOOL win95;
  346. BOOL mvp,multi;
  347. BOOL warnings,missing;
  348. long *Topic;
  349. int Topics;                /* 16 bit: max. 16348 Topics */
  350. GROUP *group;
  351. int groups;
  352. CONTEXTREC *ContextRec;
  353. int ContextRecs;            /* 16 bit: max. 8191 Context Records */
  354. ALTERNATIVE *alternative;
  355. int alternatives;
  356. BOOL overwrite=FALSE;
  357. BOOL exportLZ77=FALSE;
  358. BOOL extractmacros=TRUE;
  359. BOOL guessing=TRUE;
  360. long guessed=0L;
  361. BOOL listtopic=FALSE;
  362. BOOL nopagebreak=FALSE;
  363. BOOL resolvebrowse=TRUE;
  364. BOOL reportderived=FALSE;
  365. BOOL checkexternal=FALSE;
  366. BOOL exportplain=FALSE;
  367. #define MAXKEYWORDS (64<<(sizeof(int)*2)) /* 16 bit: 1024, 32 bit: 16348 */
  368. int NextKeywordRec,KeywordRecs;
  369. KEYWORDREC *KeywordRec;
  370. TOPICOFFSET NextKeywordOffset;
  371. char helpcomp[13];
  372. char HelpFileTitle[81];
  373. char TopicTitle[256];
  374. char *Phrases;
  375. unsigned int *PhraseOffsets;
  376. unsigned int PhraseCount;
  377. long TopicFileLength;
  378. int TopicBlockSize; /* 2k or 4k */
  379. int DecompressSize; /* 4k or 16k */
  380. char buffer[4096];
  381. char keyword[512];
  382. char index_separators[40]=",;";
  383. char *extension;
  384. int extensions=0;
  385. /* index into bmpext: bit 0=multiresolution bit 1=bitmap, bit 2=metafile, bit 3=hotspot data, bit 4=embedded, bit 5=transparent */
  386. char *bmpext[]={"???","MRB","BMP","MRB","WMF","MRB","MRB","MRB","SHG","MRB","SHG","MRB","SHG","MRB","SHG","MRB"};
  387. char **stopwordfilename;
  388. int stopwordfiles;
  389. char **fontname;
  390. int fontnames;
  391. unsigned char DefFont;
  392. FONTDESCRIPTOR *font;
  393. int fonts;
  394. unsigned char lookup[]={0,3,1,2,4,5}; /* to translate font styles */
  395. struct { unsigned char r,g,b; } color[128];
  396. int colors;
  397. char **windowname;
  398. int windownames;
  399. BOOL NotInAnyTopic;
  400. int TopicsPerRTF;
  401. BOOL lists['z'-'0'+1];
  402. BOOL keyindex['z'-'0'+1];
  403. static signed char table[256]=
  404. {
  405.     '\x00', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7', '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF',
  406.     '\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF',
  407.     '\xF0', '\x0B', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\x0C', '\xFF',
  408.     '\x0A', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F',
  409.     '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F',
  410.     '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0D',
  411.     '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F',
  412.     '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2A', '\x2B', '\x2C', '\x2D', '\x2E', '\x2F',
  413.     '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', '\x58', '\x59', '\x5A', '\x5B', '\x5C', '\x5D', '\x5E', '\x5F',
  414.     '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6A', '\x6B', '\x6C', '\x6D', '\x6E', '\x6F',
  415.     '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', '\x78', '\x79', '\x7A', '\x7B', '\x7C', '\x7D', '\x7E', '\x7F',
  416.     '\x80', '\x81', '\x82', '\x83', '\x0B', '\x85', '\x86', '\x87', '\x88', '\x89', '\x8A', '\x8B', '\x8C', '\x8D', '\x8E', '\x8F',
  417.     '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x98', '\x99', '\x9A', '\x9B', '\x9C', '\x9D', '\x9E', '\x9F',
  418.     '\xA0', '\xA1', '\xA2', '\xA3', '\xA4', '\xA5', '\xA6', '\xA7', '\xA8', '\xA9', '\xAA', '\xAB', '\xAC', '\xAD', '\xAE', '\xAF',
  419.     '\xB0', '\xB1', '\xB2', '\xB3', '\xB4', '\xB5', '\xB6', '\xB7', '\xB8', '\xB9', '\xBA', '\xBB', '\xBC', '\xBD', '\xBE', '\xBF',
  420.     '\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF'
  421. };
  422. char oldtable[256];
  423. 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'};
  424. char *prefix[]={"","idh_","helpid_",NULL,NULL,NULL,NULL,NULL};
  425. long prefixhash[sizeof(prefix)/sizeof(prefix[0])];
  426. FONTDESCRIPTOR CurrentFont;
  427.  
  428. long hash(char *name) /* convert 3.1/'95 topic name to hash value */
  429. {
  430.     long hash;
  431.     unsigned char *ptr;
  432.  
  433.     if(*name=='\0') return 1L;
  434.     for(hash=0L,ptr=(unsigned char *)name;*ptr;ptr++)
  435.     {
  436.     hash=hash*43L+table[*ptr];
  437.     }
  438.     return hash;
  439. }
  440.  
  441. char *unhash(unsigned long hash) /* deliver 3.1 context id that fits hash value */
  442. {
  443.     static char buffer[15];
  444.     int i,j,k;
  445.     unsigned long hashlo,divlo,result,mask;
  446.     unsigned char hashhi,divhi;
  447.     char ch;
  448.  
  449.     i=0;
  450.     j=hashrecs;
  451.     while(i<j)
  452.     {
  453.     k=(i+j)/2;
  454.     if(hashrec[k].hash<(long)hash)
  455.     {
  456.         i=k+1;
  457.     }
  458.     else if(hashrec[k].hash>(long)hash)
  459.     {
  460.         j=k;
  461.     }
  462.     else return hashrec[k].name;
  463.     }
  464.     for(i=0;i<43;i++)
  465.     {
  466.     buffer[j=14]='\0';
  467.     hashlo=hash;
  468.     hashhi=i;
  469.     while(1)
  470.     {
  471.         divhi=21;
  472.         divlo=0x80000000UL;
  473.         result=0UL;
  474.         for(mask=0x80000000UL;mask;mask>>=1)
  475.         {
  476.         if(hashhi>divhi||hashhi==divhi&&hashlo>=divlo)
  477.         {
  478.             result|=mask;
  479.             hashhi-=divhi;
  480.             if(divlo>hashlo) hashhi--;
  481.             hashlo-=divlo;
  482.         }
  483.         divlo>>=1;
  484.         if(divhi&1) divlo|=0x80000000UL;
  485.         divhi>>=1;
  486.         }
  487.         ch=untable[(int)hashlo];
  488.         if(!ch) break;
  489.         buffer[--j]=ch;
  490.         if(result==0) return buffer+j;
  491.         hashlo=result;
  492.     }
  493.     }
  494.     /* should never happen */
  495.     error("Can not find a matching string for hash value %08lx",hash);
  496.     sprintf(buffer,"HASH%08lx",hash);
  497.     return buffer;
  498. }
  499.  
  500. char *ContextId(unsigned long hash) /* unhash and verify for legal entry point */
  501. {
  502.     char *ptr;
  503.     int i;
  504.  
  505.     for(i=0;i<ContextRecs;i++)
  506.     {
  507.     if(ContextRec[i].HashValue==hash)
  508.     {
  509.         return unhash(hash);
  510.     }
  511.     }
  512.     ptr=unhash(hash);
  513.     printf("Help Compiler will issue Warning 4113: Unresolved jump or popup '%s'\n",ptr);
  514.     return ptr;
  515. }
  516.  
  517. void AddTopic(char *TopicName,BOOL derived) /* adds a known topic name to hash decode list */
  518. {
  519.     long x;
  520.     int i,j,k;
  521.  
  522.     x=hash(TopicName);
  523.     i=0;
  524.     j=hashrecs;
  525.     while(i<j)
  526.     {
  527.     k=(i+j)/2;
  528.     if(hashrec[k].hash<x)
  529.     {
  530.         i=k+1;
  531.     }
  532.     else if(hashrec[k].hash>x)
  533.     {
  534.         j=k;
  535.     }
  536.     else
  537.     {
  538.         if(stricmp(TopicName,hashrec[k].name)!=0)
  539.         {
  540.         if(!hashrec[k].derived)
  541.         {
  542.             if(!derived) fprintf(stderr,"ContextId %s already defined as %s\n",TopicName,hashrec[k].name);
  543.             return;
  544.         }
  545.         if(derived) return;
  546.         free(hashrec[k].name);
  547.         hashrec[k].name=my_strdup(TopicName);
  548.         }
  549.         if(!derived&&hashrec[k].derived)
  550.         {
  551.         guessed--;
  552.         hashrec[k].derived=FALSE;
  553.         }
  554.         return;
  555.     }
  556.     }
  557.     /* %100 to decrease memory fragmentation */
  558.     if(hashrecs%100==0) hashrec=my_realloc(hashrec,(hashrecs+100)*sizeof(HASHREC));
  559.     if(i<hashrecs) memmove(hashrec+i+1,hashrec+i,sizeof(HASHREC)*(hashrecs-i));
  560.     hashrec[i].name=my_strdup(TopicName);
  561.     hashrec[i].derived=derived;
  562.     hashrec[i].hash=x;
  563.     if(derived) guessed++;
  564.     hashrecs++;
  565. }
  566.  
  567. /* ContextRec is sorted by TopicOffset. Binary search for an entry for
  568. // TopicOffset topic. If more than one entry with identical TopicOffset
  569. // is stored, return index of the first one, if not found return -1 */
  570. int FindContext(long topic)
  571. {
  572.     int i,lwb,upb;
  573.  
  574.     lwb=0;
  575.     upb=ContextRecs;
  576.     while(lwb<upb)
  577.     {
  578.     i=(lwb+upb)/2;
  579.     if(ContextRec[i].TopicOffset>topic)
  580.     {
  581.         upb=i;
  582.     }
  583.     else if(ContextRec[i].TopicOffset<topic)
  584.     {
  585.         lwb=i+1;
  586.     }
  587.     else
  588.     {
  589.         while(i>0&&ContextRec[i-1].TopicOffset==topic) i--;
  590.         return i;
  591.     }
  592.     }
  593.     return -1;
  594. }
  595.  
  596. /* Look if buf contains str (caseinsensitive) and update case of str so it is
  597. // matching case appearance in buf and return TRUE, FALSE if not found */
  598. int FindString(unsigned char *buf,int buflen,unsigned char *str,int len)
  599. {
  600.     int i,j;
  601.     unsigned char ch;
  602.  
  603.     buflen-=len;
  604.     for(i=0;i<=buflen;i++)
  605.     {
  606.         for(j=0;j<len;j++)
  607.         {
  608.         if(table[ch=buf[j]]!=table[str[j]]) break;
  609.             str[j]=ch;
  610.         }
  611.         if(j==len)
  612.         {
  613.             return TRUE;
  614.         }
  615.     buf++;
  616.     }
  617.     return FALSE;
  618. }
  619.  
  620. /* Many people and authoring systems create context ids from topic titles,
  621. // either by replacing every illegal char with _ or . or leaving it out,
  622. // sometimes using only part of the topic title or prefixing it with idh_
  623. // or helpid_. This function tries all variants and may find a context id
  624. // matching the hash value.
  625. // And if it doesn't derive a context id, don't bother, unhash delivers
  626. // one matching the hash code. This code is just to please the user.
  627. // Win95 allows for nearly every char to be used as a context ident. */
  628. BOOL Derive(unsigned char *str,unsigned long desiredhash,char *buffer)
  629. {
  630.     int i,j,k,l,m,n,o,p,s;
  631.     unsigned long hash,h,x,y;
  632.     char *ptr;
  633.     unsigned char ch;
  634.  
  635.     l=2;
  636.     s=strlen(str);
  637.     for(i=!win95;i<l;i++) /* three variants what to do with illegal characters */
  638.     {                /* but only if an illegal character found (see below) */
  639.     for(j=0;prefix[j];j++)
  640.     {
  641.         k=0;
  642.         while(str[k])
  643.         {
  644.         hash=prefixhash[j];
  645.         strcpy(buffer,prefix[j]);
  646.         ptr=strchr(buffer,'\0');
  647.         for(m=k;str[m];m++)
  648.         {
  649.             ch=str[m];
  650.             if(i)
  651.             {
  652.             n=oldtable[ch];
  653.             if(n==0)
  654.             {
  655.                 if(i==2)
  656.                 {
  657.                 n=oldtable[ch='_'];
  658.                 }
  659.                 else
  660.                 {
  661.                 continue;
  662.                 }
  663.             }
  664.             }
  665.             else /* Win95 */
  666.             {
  667.             n=table[ch];
  668.             if(n==0) continue;
  669.             }
  670.             *ptr++=ch;
  671.             hash=43L*hash+n;
  672.             for(x=1L,o=0;o<6;o++,x*=43L)
  673.             {
  674.             h=desiredhash-x*hash;
  675.             if(h<x)
  676.             {
  677.                 y=x;
  678.                 p=0;
  679.                 while(h)
  680.                 {
  681.                 y/=43L;
  682.                 ch=untable[(unsigned char)(h/y)];
  683.                                 if(!ch) break;
  684.                 h%=y;
  685.                 ptr[p++]=ch;
  686.                 }
  687.                 if(h==0&&(FindString(str,s,ptr,p)||p<3))
  688.                 {
  689.                 ptr[p]='\0';
  690.                 return TRUE;
  691.                 }
  692.             }
  693.             }
  694.         }
  695.         if(i)
  696.         {
  697.             while(oldtable[ch=str[k]]) k++;
  698.             if(ch) l=3;
  699.             while((ch=str[k])!='\0'&&!oldtable[ch]) k++;
  700.         }
  701.         else /* win95 */
  702.         {
  703.             while(table[ch=str[k]]) k++;
  704.             if(ch) l=3;
  705.             while((ch=str[k])!='\0'&&!table[ch]) k++;
  706.         }
  707.         }
  708.     }
  709.     }
  710.     return FALSE;
  711. }
  712.  
  713. void Guess(char *str,TOPICOFFSET topic)
  714. {
  715.     int i,j,k,m;
  716.     long hash;
  717.  
  718.     i=FindContext(topic);
  719.     if(i!=-1) /* iff there is a # footnote assigned to that topic offset */
  720.     {
  721.     do
  722.     {
  723.         hash=ContextRec[i].HashValue;
  724.         j=0;
  725.         k=hashrecs;
  726.         while(j<k)
  727.         {
  728.         m=(j+k)/2;
  729.         if(hashrec[m].hash<hash)
  730.         {
  731.             j=m+1;
  732.         }
  733.         else if(hashrec[m].hash>hash)
  734.         {
  735.             k=m;
  736.         }
  737.         else break;
  738.         }
  739.         if(j>=k) if(Derive(str,hash,buffer))
  740.         {
  741.         if(reportderived) printf("Derived %s\n",buffer);
  742.         AddTopic(buffer,TRUE);
  743.         }
  744.     }
  745.     while(++i<ContextRecs&&ContextRec[i].TopicOffset==topic);
  746.     }
  747. }
  748.  
  749. void SysLoad(FILE *HelpFile) /* gets global values from SYSTEM file */
  750. {
  751.     SYSTEMRECORD *SysRec;
  752.     SYSTEMHEADER SysHdr;
  753.     SECWINDOW *SWin;
  754.     int i;
  755.     char kwdata[10];
  756.     char kwbtree[10];
  757.  
  758.     if(!SearchFile(HelpFile,"|SYSTEM",NULL))
  759.     {
  760.     fputs("Internal |SYSTEM file not found. Can't continue.\n",stderr);
  761.     exit(1);
  762.     }
  763.     my_fread(&SysHdr,sizeof(SysHdr),HelpFile);
  764.     before31=SysHdr.Minor<16;
  765.     after31=SysHdr.Minor>21;
  766.     multi=SysHdr.Minor==27;
  767.     lzcompressed=!before31&&(SysHdr.Flags==4||SysHdr.Flags==8);
  768.     win95=SysHdr.Minor==33&&!mvp;
  769.     windownames=0;
  770.     windowname=NULL;
  771.     if(before31)
  772.     {
  773.     DecompressSize=TopicBlockSize=2048;
  774.     }
  775.     else
  776.     {
  777.     DecompressSize=0x4000;
  778.     if(SysHdr.Flags==8)
  779.     {
  780.         TopicBlockSize=2048;
  781.     }
  782.     else
  783.     {
  784.         TopicBlockSize=4096;
  785.     }
  786.     }
  787.     if(before31)
  788.     {
  789.     my_gets(HelpFileTitle,33,HelpFile);
  790.     }
  791.     else
  792.     {
  793.     for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  794.     {
  795.         switch(SysRec->RecordType)
  796.         {
  797.         case 0x0001:
  798.         strlcpy(HelpFileTitle,SysRec->Data,sizeof(HelpFileTitle));
  799.         break;
  800.         case 0x0006:
  801.         SWin=(SECWINDOW *)SysRec->Data;
  802.         windowname=my_realloc(windowname,(windownames+1)*sizeof(char *));
  803.         windowname[windownames]=NULL;
  804.         if(SWin->Flags&WSYSFLAG_NAME)
  805.         {
  806.             windowname[windownames]=my_strdup(SWin->Name);
  807.         }
  808.         windownames++;
  809.         break;
  810.         case 0x000E:
  811.         keyindex[SysRec->Data[1]-'0']=TRUE;
  812.         break;
  813.         }
  814.     }
  815.     }
  816.     for(i='A';i<='z';i++)
  817.     {
  818.     sprintf(kwdata,"|%cWDATA",i);
  819.     sprintf(kwbtree,"|%cWBTREE",i);
  820.     if(SearchFile(HelpFile,kwdata,NULL)&&SearchFile(HelpFile,kwbtree,NULL))
  821.     {
  822.         lists[i-'0']=TRUE;
  823.     }
  824.     }
  825. }
  826.  
  827. /*********************************************************************************
  828.  
  829.   GRAPHICS STUFF
  830.   ==============
  831.   ExtractBitmap(..) extracts the graphics stored as |bmXXX in the Windows
  832.   HelpFile to separate files. In this version multiple resolution pictures
  833.   (with and without hotspots) are saved as MRB's, pictures with hotspots are
  834.   converted to the SHG-format, single resolution Bitmaps without hotspots
  835.   to standard BMP-format and all Metafiles without hotspots to the Aldus
  836.   Placeable Metafile WMF format.
  837.  
  838.   GENERAL NOTES ABOUT |bmXXX
  839.   --------------------------
  840.  
  841.   |bmXXX contains one MRB-File. This can be directly recompiled. If hotspots
  842.   in the graphics contained in the MRB-File shall be added or changed with
  843.   SHED, the MRB-File has to be split into one SHG file for each graphic. This
  844.   is very easy as the graphics-data will only be slightly rewrapped. If the
  845.   graphics themselves shall be edited with a standard drawing program, the
  846.   SHG files have to be converted into bitmaps or metafiles (as flagged in
  847.   the SHG). The hotspot data is lost by this step.
  848.  
  849.   MRB-FILES
  850.   ---------
  851.  
  852.   MRBC takes the input-file(s), converts them to "lp"-Files if needed, adds
  853.   a resolution specification if none is given in the file and finally outputs
  854.   a "lp"-File with the ending ".MRB". Depending on the given display type
  855.   MRBC sets the following resolutions:
  856.  
  857.            |       |           | x-resolution | y-resolution
  858.   display-type | extension | std-extension |    (ppi)      |    (ppi)
  859.   -------------+-----------+---------------+--------------+-------------
  860.        CGA     |   ".C*"   |     ".CGA"    |      96      |     48
  861.        EGA     |   ".E*"   |     ".EGA"    |      96      |     72
  862.        VGA     |   ".V*"   |     ".VGA"    |      96      |     96
  863.        8514    |   ".8*"   |     ".854"    |     120      |    120
  864.  
  865.   SHG-Files
  866.   ---------
  867.  
  868.   Structure of SHG-Data:
  869.  
  870.    Offset    | Size      | Ref | Description
  871.    ----------+------------+-----+-----------------------------------------------
  872.       0      | 1 Byte      |    | always 0x01, maybe a version-number
  873.       1      | 1 Word      |  N    | the number of hotspots
  874.       3      | 1 DWord      |  M    | size of macro-data (in bytes)
  875.       7      | 15xN Bytes | HSD | Hotspot-Data (see below)
  876.     7 + 15*N | M Bytes      |    | Macro-Data (ASCIIZ-Strings)
  877.     7+15*N+M | 2*N*ASCIIZ |    | Hotspot-Id and Context- or Macro-Name as
  878.          |          |    | ASCIIZ-String
  879.  
  880.   Structure of Hotspot-Data:
  881.  
  882.    Offset    | Size      | Ref | Description
  883.    ----------+------------+-----+-------------------------------------------------
  884.       0      | 3 Bytes      |    | Hotspot-Type: 0xE3 0x00 0x00 = jump, visible
  885.          |          |    |        0xE7 0x04 0x00 = jump, invisible
  886.          |          |    |        0xE2 0x00 0x00 = pop-up, visible
  887.          |          |    |        0xE6 0x04 0x00 = pop-up, invisible
  888.          |          |    |        0xC8 0x00 0x00 = macro, visible
  889.          |          |    |        0xCC 0x04 0x00 = macro, invisible
  890.       3      | 1 Word      |    | Left border of hotspot
  891.       5      | 1 Word      |    | Top border of hotspot
  892.       7      | 1 Word      |    | Right border - Left border
  893.       9      | 1 Word      |    | Bottom border - Top border
  894.      11      | 1 DWord      |    | 0x00000000 => nothing
  895.          |          |    | 0x00000001 => this is a macro-hotspot
  896.          |          |    |   others   => hash-value of the context-string
  897.          |          |    |        according to the WinHelp standard
  898.  
  899.   03-06-1995 Holger Haase
  900. **********************************************************************************/
  901.  
  902. /* compares filename a[.HLP] and filename b[.HLP], returning 0 if match */
  903. int filenamecmp(const char *a,const char *b)
  904. {
  905.     char aname[_MAX_FNAME],bname[_MAX_FNAME];
  906.     char aext[_MAX_EXT],bext[_MAX_EXT];
  907.     int i;
  908.  
  909.     _splitpath(a,NULL,NULL,aname,aext);
  910.     _splitpath(b,NULL,NULL,bname,bext);
  911.     if(aext[0]=='\0') strcpy(aext,".HLP");
  912.     if(bext[0]=='\0') strcpy(bext,".HLP");
  913.     i=strcmpi(aname,bname);
  914.     if(i) return i;
  915.     return strcmpi(aext,bext);
  916. }
  917.  
  918. /* store external reference in list, checked later */
  919. void StoreReference(char *filename,int type,char *id,long hash)
  920. {
  921.     CHECKREC *ptr;
  922.     FILEREF *ref;
  923.     PLACEREC *place;
  924.  
  925.     for(ref=external;ref;ref=ref->next)
  926.     {
  927.     if(filenamecmp(filename,ref->filename)==0) break;
  928.     }
  929.     if(!ref)
  930.     {
  931.     ref=my_malloc(sizeof(FILEREF)+strlen(filename));
  932.     strcpy(ref->filename,filename);
  933.     ref->check=NULL;
  934.     ref->next=external;
  935.     external=ref;
  936.     }
  937.     for(ptr=ref->check;ptr;ptr=ptr->next)
  938.     {
  939.     if(ptr->type==type&&ptr->hash==hash) break;
  940.     }
  941.     if(!ptr)
  942.     {
  943.     ptr=my_malloc(sizeof(CHECKREC));
  944.     ptr->type=type;
  945.     ptr->hash=hash;
  946.     ptr->id=my_strdup(id);
  947.     ptr->here=NULL;
  948.     ptr->next=ref->check;
  949.     ref->check=ptr;
  950.     }
  951.     if(listtopic&&TopicTitle[0])
  952.     {
  953.     place=my_malloc(sizeof(PLACEREC)+strlen(TopicTitle));
  954.     strcpy(place->topicname,TopicTitle);
  955.     place->next=ptr->here;
  956.     ptr->here=place;
  957.     }
  958. }
  959.  
  960. /* validate each entry in list of external references */
  961. void CheckReferences(void)
  962. {
  963.     FILEREF *ref;
  964.     CHECKREC *ptr;
  965.     FILE *f;
  966.     BOOL found;
  967.     long offset;
  968.     int i,n;
  969.     CTXOMAPREC CTXORec;
  970.     BTREEHEADER BTreeHdr;
  971.     BTREENODEHEADER CurrNode;
  972.     CONTEXTREC ContextRec;
  973.  
  974.     for(ref=external;ref;ref=ref->next)
  975.     {
  976.     f=fopen(ref->filename,"rb");
  977.     if(!f)
  978.     {
  979.         printf("%s not found\n",ref->filename);
  980.     }
  981.     else
  982.     {
  983.         if(SearchFile(f,NULL,NULL))
  984.         {
  985.         for(ptr=ref->check;ptr;ptr=ptr->next)
  986.         {
  987.             found=FALSE;
  988.             if(ptr->type==CONTEXT)
  989.             {
  990.             if(SearchFile(f,"|CTXOMAP",NULL))
  991.             {
  992.                 n=my_getw(f);
  993.                 for(i=0;i<n;i++)
  994.                 {
  995.                 my_fread(&CTXORec,sizeof(CTXORec),f);
  996.                 if(CTXORec.MapID==ptr->hash) /* hash is context id */
  997.                 {
  998.                     found=TRUE;
  999.                     break;
  1000.                 }
  1001.                 }
  1002.             }
  1003.             }
  1004.             else
  1005.             {
  1006.             if(SearchFile(f,"|CONTEXT",NULL))
  1007.             {
  1008.                 my_fread(&BTreeHdr,sizeof(BTreeHdr),f);
  1009.                 offset=ftell(f);
  1010.                 CurrNode.PreviousPage=BTreeHdr.RootPage;
  1011.                 for(n=1;n<BTreeHdr.NLevels;n++)
  1012.                 {
  1013.                 fseek(f,offset+CurrNode.PreviousPage*(long)BTreeHdr.PageSize,SEEK_SET);
  1014.                 my_fread(&CurrNode,sizeof(BTREEINDEXHEADER),f);
  1015.                 for(i=0;i<CurrNode.NEntries;i++)
  1016.                 {
  1017.                     ContextRec.HashValue=getdw(f);
  1018.                     if(ContextRec.HashValue>ptr->hash) break;
  1019.                     CurrNode.PreviousPage=my_getw(f); /* Page */
  1020.                 }
  1021.                 }
  1022.                 fseek(f,offset+CurrNode.PreviousPage*(long)BTreeHdr.PageSize,SEEK_SET);
  1023.                 my_fread(&CurrNode,sizeof(BTREENODEHEADER),f);
  1024.                 for(i=0;i<CurrNode.NEntries;i++)
  1025.                 {
  1026.                 my_fread(&ContextRec,sizeof(ContextRec),f);
  1027.                 if(ContextRec.HashValue==ptr->hash) found=TRUE;
  1028.                 if(ContextRec.HashValue>=ptr->hash) break;
  1029.                 }
  1030.             }
  1031.             }
  1032.             if(!found)
  1033.             {
  1034.             if(ptr->id)
  1035.             {
  1036.                 printf("%s@%s not found\n",ptr->id,ref->filename);
  1037.             }
  1038.             else
  1039.             {
  1040.                 printf("0x%08lx@%s not found\n",ptr->hash,ref->filename);
  1041.             }
  1042.             while(ptr->here)
  1043.             {
  1044.                 printf("  %s\n",ptr->here->topicname);
  1045.                 ptr->here=ptr->here->next;
  1046.             }
  1047.             }
  1048.         }
  1049.         }
  1050.         else
  1051.         {
  1052.         printf("%s isn't a valid WinHelp file !\n",ref->filename);
  1053.         }
  1054.         fclose(f);
  1055.     }
  1056.     }
  1057. }
  1058.  
  1059. /* list each entry in list of external references */
  1060. void ListReferences(void)
  1061. {
  1062.     FILEREF *ref;
  1063.     CHECKREC *ptr;
  1064.  
  1065.     hashrecs=0;
  1066.     for(ref=external;ref;ref=ref->next)
  1067.     {
  1068.     for(ptr=ref->check;ptr;ptr=ptr->next)
  1069.     {
  1070.         printf("%s ",ref->filename);
  1071.         switch(ptr->type)
  1072.         {
  1073.         case TOPIC:
  1074.         printf("topic id ");
  1075.         if(ptr->id)
  1076.         {
  1077.             printf("'%s'",ptr->id);
  1078.         }
  1079.         else
  1080.         {
  1081.             printf("0x%08lx='%s'",ptr->hash,unhash(ptr->hash));
  1082.         }
  1083.         break;
  1084.         case CONTEXT:
  1085.         printf("[MAP] id ");
  1086.         if(ptr->id)
  1087.         {
  1088.             printf("'%s'",ptr->id);
  1089.         }
  1090.         else
  1091.         {
  1092.             printf("0x%08lx=(%ld)",ptr->hash,ptr->hash);
  1093.         }
  1094.         break;
  1095.         }
  1096.         if(ptr->here)
  1097.         {
  1098.         printf(" referenced from:");
  1099.         while(ptr->here)
  1100.         {
  1101.             printf("\n    %s",ptr->here->topicname);
  1102.             ptr->here=ptr->here->next;
  1103.         }
  1104.         }
  1105.         putchar('\n');
  1106.     }
  1107.     }
  1108. }
  1109.  
  1110. void unquote(char *ptr)
  1111. {
  1112.     char *dest;
  1113.  
  1114.     dest=ptr;
  1115.     do
  1116.     {
  1117.         if(*ptr=='\\'&&ptr[1]!='\0')
  1118.         {
  1119.             ptr++;
  1120.         }
  1121.     }
  1122.     while((*dest++=*ptr++)!='\0');
  1123. }
  1124.  
  1125. char *findMatchingChar(char *p) /* searches for matching '`', '"', or '('. */
  1126. {
  1127.     char match = '\'';
  1128.  
  1129.     if(*p=='"') match = '"';
  1130.     else if(*p=='(') match = ')';
  1131.     else if(*p!='`') return p; /* no match for this string */
  1132.     for(p++;*p!='\0'&&*p!=match;p++)
  1133.     {
  1134.     if(*p=='\\'&&p[1]!='\0')
  1135.         {
  1136.         p++;
  1137.         }
  1138.     else if(*p=='`'||*p=='"'||*p=='(')
  1139.         {
  1140.         if(!*(p=findMatchingChar(p))) break; /* error: end of string */
  1141.         }
  1142.     }
  1143.     return p;
  1144. }
  1145.  
  1146. /* scan macro string for topic names and external references */
  1147. BOOL CheckMacroX(char *ptr)
  1148. {
  1149.     static char *macro[]=
  1150.     {
  1151.     "AddAccelerator(ssm","AA(ssm",
  1152.     "AppendItem(sssm","AI(sssm",
  1153.     "AL(ssts",
  1154.     "ChangeButtonBinding(sm","CBB(sm",
  1155.     "CB(ssm",
  1156.     "CE(sm",
  1157.     "ChangeItemBinding(sm","CIB(sm",
  1158.     "ExtInsertItem(sssmss",
  1159.     "IfThen(mm","IT(mm",
  1160.     "IfThenElse(mmm","IE(mmm",
  1161.     "ExecFile(sssm","EF(sssm",
  1162.     "InsertItem(sssms","II(sssms",
  1163.     "JumpContext(x","JC(x","JumpContext(fx","JC(fx",
  1164.     "JumpHash(h","JH(h","JumpHash(fh","JH(fh",
  1165.     "JumpId(c","JI(c","JumpId(fc","JI(fc",
  1166.     "KL(ssts",
  1167.     "MPrintID(c",
  1168.     "Not(m",
  1169.     "PopupId(c","PI(c","PopupId(fc","PI(fc",
  1170.     "PopupContext(x","PC(x","PopupContext(fx","PC(fx",
  1171.     "PopupHash(h","PopupHash(fh",
  1172.     "SetContents(fx",
  1173.     "SE(sssssc",
  1174.     "SH(sssst",
  1175.     "UpdateWindow(fc","UW(fc"
  1176.     };
  1177.     char *name;
  1178.     int namelen,parms,i,n,l;
  1179.     char *parm[20];
  1180.  
  1181.     while(1)
  1182.     {
  1183.     while(*ptr==' ') ptr++;
  1184.     if(*ptr=='\0') return TRUE;
  1185.     if(!isalpha(*ptr)) return FALSE;
  1186.     for(name=ptr,namelen=0;isalnum(*ptr)||*ptr=='_';namelen++) ptr++;
  1187.     while(*ptr==' ') ptr++;
  1188.     if(*ptr!='(') return FALSE;
  1189.         ptr++;
  1190.     parms=0;
  1191.     while(1)
  1192.     {
  1193.         while(*ptr==' ') ptr++;
  1194.         parm[parms]=ptr; /* save after skip spaces */
  1195.         if(*ptr=='`'||*ptr=='"')
  1196.             {
  1197.         parm[parms++]=ptr+1; /* advance past quote */
  1198.                 ptr=findMatchingChar(ptr);
  1199.         if(*ptr=='\0') return FALSE; /* unexpected end of string */
  1200.         *ptr++='\0'; /* zap terminating quote */
  1201.             while(*ptr==' '||*ptr=='\t') ptr++;
  1202.         }
  1203.             else if(*ptr!=')'&&*ptr!='\0')
  1204.             {
  1205.             parm[parms++]=ptr;
  1206.             while(*ptr!=','&&*ptr!=')'&&*ptr!='\0')
  1207.                 {
  1208.                 if(*ptr=='('||*ptr=='`'||*ptr=='"') /* `, " probably not needed */
  1209.                     {
  1210.                         ptr=findMatchingChar(ptr);
  1211.                 if(*ptr=='\0') return FALSE;
  1212.                     }
  1213.                 ptr++;
  1214.                 }
  1215.         }
  1216.         if(*ptr==')') break;
  1217.         if(*ptr!=',') return FALSE;
  1218.         *ptr++='\0';
  1219.     }
  1220.     *ptr++='\0';
  1221.     for(i=0;i<sizeof(macro)/sizeof(macro[0]);i++)
  1222.     {
  1223.         if(strcspn(macro[i],"(")==namelen&&memicmp(macro[i],name,namelen)==0&&strlen(macro[i]+namelen+1)>=parms) break;
  1224.     }
  1225.     if(i<sizeof(macro)/sizeof(macro[0])) /* macro of interest */
  1226.     {
  1227.         char *at;
  1228.  
  1229.         for(n=0;n<parms;n++) unquote(parm[n]);
  1230.         for(n=0;n<parms;n++)
  1231.         {
  1232.         if(macro[i][namelen+1+n]=='m')
  1233.         {
  1234.             CheckMacroX(parm[n]); /* recursive */
  1235.         }
  1236.         else if(macro[i][namelen+1+n]=='c')
  1237.         {
  1238.             if(extractmacros)
  1239.             {
  1240.             while(parm[n][0]==' ') parm[n]++;
  1241.             for(l=strlen(parm[n]);l>0&&parm[n][l-1]==' ';l--) ;
  1242.             parm[n][l]='\0';
  1243.             AddTopic(parm[n],FALSE);
  1244.             }
  1245.         }
  1246.         else if(macro[i][namelen+1+n]=='f')
  1247.         {
  1248.             at=strchr(parm[n],'>');
  1249.             if(at)
  1250.             {
  1251.             if(filenamecmp(parm[n],name)!=0)
  1252.             {
  1253.                 if(macro[i][namelen+2+n]=='c')
  1254.                 {
  1255.                 StoreReference(parm[n],TOPIC,parm[n+1],hash(parm[n+1]));
  1256.                 n++;
  1257.                 }
  1258.                 else if(macro[i][namelen+2+n]=='x')
  1259.                 {
  1260.                 StoreReference(parm[n],CONTEXT,parm[n+1],strtoul(parm[n+1],NULL,0));
  1261.                 n++;
  1262.                 }
  1263.                 else if(macro[i][namelen+2+n]=='h')
  1264.                 {
  1265.                 StoreReference(parm[n],TOPIC,parm[n+1],strtoul(parm[n+1],NULL,0));
  1266.                 n++;
  1267.                 }
  1268.             }
  1269.             }
  1270.         }
  1271.         else if(macro[i][namelen+1+n]=='t')
  1272.         {
  1273.             at=strchr(parm[n],'@');
  1274.             if(at)
  1275.             {
  1276.             if(filenamecmp(at+1,name)!=0)
  1277.             {
  1278.                 *at='\0';
  1279.                 StoreReference(at+1,TOPIC,parm[n],hash(parm[n]));
  1280.             }
  1281.             else
  1282.             {
  1283.                 AddTopic(parm[n],FALSE);
  1284.             }
  1285.             }
  1286.             else
  1287.             {
  1288.             AddTopic(parm[n],FALSE);
  1289.             }
  1290.         }
  1291.         }
  1292.     }
  1293.     while(*ptr==' ') ptr++;
  1294.     if(*ptr!=':'&&*ptr!=';') break;
  1295.     ptr++;
  1296.     }
  1297.     return TRUE;
  1298. }
  1299.  
  1300. void CheckMacro(char *ptr)
  1301. {
  1302.     char *temp;
  1303.  
  1304.     if(!multi)
  1305.     {
  1306.     temp=my_strdup(ptr);
  1307.     if(!CheckMacroX(temp)) fprintf(stderr,"Bad macro: %s\n",ptr);
  1308.     free(temp);
  1309.     }
  1310. }
  1311.  
  1312. /* check hotspot info of bitmap for topic names and external references and
  1313. // extract (write to file) if checkexternal not set, write out first bitmap
  1314. // or metafile only (no SHG/MRB) if exportplain (create lookalike rtf) set */
  1315. int ExtractBitmap(char *szFilename,MFILE *f)
  1316. {
  1317.     FILE *fTarget;
  1318.     char *filename;
  1319.     int type;
  1320.     unsigned int i,n,j;
  1321.     unsigned char byType,byPacked;
  1322.     long l,pos,offset,nextpict,FileStart;
  1323.     BITMAPFILEHEADER bmfh;
  1324.     BITMAPINFOHEADER bmih;
  1325.     APMFILEHEADER afh;
  1326.     unsigned short *wp;
  1327.     unsigned short wMagic,mapmode,colors;
  1328.     unsigned long dwRawSize,dwDataSize,dwHotspotOffset,dwOffsBitmap,dwHotspotSize,dwPictureOffset,xPels,yPels;
  1329.  
  1330.     FileStart=f->tell(f);
  1331.     wMagic=GetWord(f);
  1332.     if(wMagic!=0x506C /* SHG */ &&wMagic!=0x706C /* MRB */)
  1333.     {
  1334.     error("Unknown magic 0x%04X (%c%c)",wMagic,wMagic&0x00FF,wMagic>>8);
  1335.     return 0;
  1336.     }
  1337.     fTarget=NULL;
  1338.     n=GetWord(f);
  1339.     type=!exportplain&&n>1; /* contains multiple resolutions */
  1340.     /* do not depend on wMagic, because it is sometimes incorrect */
  1341.     nextpict=4+4*n;
  1342.     for(j=0;j<n;j++)
  1343.     {
  1344.     f->seek(f,FileStart+4+4*j);
  1345.     dwOffsBitmap=GetDWord(f);
  1346.     f->seek(f,FileStart+dwOffsBitmap);
  1347.     byType=f->get(f); /* type of picture: 5=DDB, 6=DIB, 8=METAFILE */
  1348.     byPacked=f->get(f); /* packing method: 0=unpacked, 1=RunLen, 2=LZ77, 3=both */
  1349.     if(byType==6&&byPacked<4||byType==5&&byPacked<2)
  1350.     {
  1351.         type|=2; /* contains bitmap */
  1352.         memset(&bmfh,0,sizeof(bmfh));
  1353.         memset(&bmih,0,sizeof(bmih));
  1354.         bmfh.bfType=0x4D42; /* bitmap magic ("BM") */
  1355.         bmih.biSize=sizeof(bmih);
  1356.         xPels=GetCDWord(f);
  1357.         /* HC30 doesn't like certain PelsPerMeter */
  1358.         if(!before31) bmih.biXPelsPerMeter=(xPels*79L+1)/2;
  1359.         yPels=GetCDWord(f);
  1360.         if(!before31) bmih.biYPelsPerMeter=(yPels*79L+1)/2;
  1361.         bmih.biPlanes=GetCWord(f);
  1362.         bmih.biBitCount=GetCWord(f);
  1363.         bmih.biWidth=GetCDWord(f);
  1364.         bmih.biHeight=GetCDWord(f);
  1365.         colors=(int)(bmih.biClrUsed=GetCDWord(f));
  1366.         if(!colors) colors=(unsigned short)1<<bmih.biBitCount;
  1367.         bmih.biClrImportant=GetCDWord(f);
  1368.         if(after31&&bmih.biClrImportant==1) type|=0x20; /* contains transparent bitmap */
  1369.         dwDataSize=GetCDWord(f);
  1370.         dwHotspotSize=GetCDWord(f);
  1371.         dwPictureOffset=GetDWord(f);
  1372.         dwHotspotOffset=GetDWord(f);
  1373.         if(exportplain||n==1&&(dwHotspotOffset==0L||dwHotspotSize==0L))
  1374.         {
  1375.         if(checkexternal) break;
  1376.         strcat(szFilename,".BMP");
  1377.         fTarget=my_fopen(szFilename,"wb");
  1378.         if(fTarget)
  1379.         {
  1380.             fwrite(&bmfh,1,sizeof(bmfh),fTarget);
  1381.             fwrite(&bmih,1,sizeof(bmih),fTarget);
  1382.             if(byType==6)
  1383.             {
  1384.             CopyBytes(f,colors*4L,fTarget);
  1385.             }
  1386.             else
  1387.             {
  1388.             putdw(0x000000L,fTarget);
  1389.             putdw(0xFFFFFFL,fTarget);
  1390.             }
  1391.             bmfh.bfOffBits=sizeof(bmfh)+sizeof(bmih)+colors*4L;
  1392.             bmih.biSizeImage=(((bmih.biWidth*bmih.biBitCount+31)/32)*4)*bmih.biHeight;
  1393.             if(byType==5) /* convert DDB to DIB */
  1394.             {
  1395.             long width,length;
  1396.             unsigned char count,value;
  1397.             int pad;
  1398.  
  1399.             width=((bmih.biWidth*bmih.biBitCount+15)/16)*2;
  1400.             pad=(int)(((width+3)/4)*4-width);
  1401.             count=value=0;
  1402.             for(l=0;l<bmih.biHeight;l++)
  1403.             {
  1404.                 if(byPacked==1)
  1405.                 {
  1406.                 for(length=0;length<width;length++)
  1407.                 {
  1408.                     if((count&0x7F)==0)
  1409.                     {
  1410.                     count=f->get(f);
  1411.                     value=f->get(f);
  1412.                     }
  1413.                     else if(count&0x80)
  1414.                     {
  1415.                     value=f->get(f);
  1416.                     }
  1417.                     putc(value,fTarget);
  1418.                     count--;
  1419.                 }
  1420.                 }
  1421.                 else
  1422.                 {
  1423.                 CopyBytes(f,width,fTarget);
  1424.                 }
  1425.                 if(pad) fwrite(buffer,pad,1,fTarget);
  1426.             }
  1427.             }
  1428.             else
  1429.             {
  1430.             DecompressIntoFile(byPacked,f,dwDataSize,fTarget);
  1431.             }
  1432.             /* update bitmap headers */
  1433.             bmfh.bfSize=ftell(fTarget);
  1434.             fseek(fTarget,0L,SEEK_SET);
  1435.             fwrite(&bmfh,1,sizeof(bmfh),fTarget);
  1436.             fwrite(&bmih,1,sizeof(bmih),fTarget);
  1437.         }
  1438.         break;
  1439.         }
  1440.     }
  1441.     else if(byType==8&&byPacked<4) /* Windows MetaFile */
  1442.     {
  1443.         type|=4; /* contains metafile */
  1444.         memset(&afh,0,sizeof(afh));
  1445.         mapmode=GetCWord(f); /* mapping mode */
  1446.         afh.rcBBox.right=GetWord(f); /* width of metafile-picture */
  1447.         afh.rcBBox.bottom=GetWord(f); /* height of metafile-picture */
  1448.         dwRawSize=GetCDWord(f);
  1449.         dwDataSize=GetCDWord(f);
  1450.         dwHotspotSize=GetCDWord(f);
  1451.         dwPictureOffset=GetDWord(f);
  1452.         dwHotspotOffset=GetDWord(f);
  1453.         if(exportplain||n==1&&(dwHotspotOffset==0L||dwHotspotSize==0L))
  1454.         {
  1455.         if(checkexternal) break;
  1456.         afh.dwKey=0x9AC6CDD7L;
  1457.         afh.wInch=2540;
  1458.         wp=(unsigned short *)&afh;
  1459.         for(i=0;i<10;i++) afh.wChecksum^=*wp++;
  1460.         strcat(szFilename,".WMF");
  1461.         fTarget=my_fopen(szFilename,"wb");
  1462.         if(fTarget)
  1463.         {
  1464.             fwrite(&afh,1,sizeof(afh),fTarget);
  1465.             DecompressIntoFile(byPacked,f,dwDataSize,fTarget);
  1466.         }
  1467.         break;
  1468.         }
  1469.     }
  1470.     else
  1471.     {
  1472.         error("Unknown format (%d) or packing method (%d)",byType,byPacked);
  1473.         break;
  1474.     }
  1475.     type|=8; /* contains hotspot info (set before accessing bmpext) */
  1476.     if(!checkexternal)
  1477.     {
  1478.         if(!fTarget)
  1479.         {
  1480.         strcat(szFilename,".");
  1481.         strcat(szFilename,bmpext[type&0x0F]);
  1482.         fTarget=my_fopen(szFilename,"wb");
  1483.         if(!fTarget) break;
  1484.         my_putw(wMagic,fTarget);
  1485.         my_putw(n,fTarget);
  1486.         }
  1487.         fseek(fTarget,4+4*j,SEEK_SET);
  1488.         putdw(nextpict,fTarget);
  1489.         fseek(fTarget,nextpict,SEEK_SET);
  1490.         putc(byType,fTarget);
  1491.         if(exportLZ77)
  1492.         {
  1493.         putc(byPacked,fTarget);
  1494.         }
  1495.         else
  1496.         {
  1497.         putc(byPacked&1,fTarget);
  1498.         }
  1499.         if(byType==8)
  1500.         {
  1501.         putcw(mapmode,fTarget); /* mapping mode */
  1502.         my_putw(afh.rcBBox.right,fTarget); /* width of metafile-picture */
  1503.         my_putw(afh.rcBBox.bottom,fTarget); /* height of metafile-picture */
  1504.         putcdw(dwRawSize,fTarget);
  1505.         }
  1506.         else
  1507.         {
  1508.         putcdw(xPels,fTarget);
  1509.         putcdw(yPels,fTarget);
  1510.         putcw(bmih.biPlanes,fTarget);
  1511.         putcw(bmih.biBitCount,fTarget);
  1512.         putcdw(bmih.biWidth,fTarget);
  1513.         putcdw(bmih.biHeight,fTarget);
  1514.         putcdw(bmih.biClrUsed,fTarget);
  1515.         putcdw(bmih.biClrImportant,fTarget);
  1516.         }
  1517.         pos=ftell(fTarget);
  1518.         putdw(0,fTarget); /* changed later ! */
  1519.         putdw(0,fTarget); /* changed later ! */
  1520.         putdw(0,fTarget); /* changed later ! */
  1521.         putdw(0,fTarget); /* changed later ! */
  1522.         if(byType==6) CopyBytes(f,colors*4L,fTarget);
  1523.         offset=ftell(fTarget);
  1524.         f->seek(f,FileStart+dwOffsBitmap+dwPictureOffset);
  1525.         if(exportLZ77)
  1526.         {
  1527.         dwDataSize=CopyBytes(f,dwDataSize,fTarget);
  1528.         }
  1529.         else
  1530.         {
  1531.         dwDataSize=DecompressIntoFile(byPacked&2,f,dwDataSize,fTarget);
  1532.         }
  1533.     }
  1534.     if(dwHotspotSize)
  1535.     {
  1536.         f->seek(f,FileStart+dwOffsBitmap+dwHotspotOffset);
  1537.         if(f->get(f)!=1)
  1538.         {
  1539.         fputs("No hotspots\n",stderr);
  1540.         dwHotspotSize=0L;
  1541.         }
  1542.         else
  1543.         {
  1544.         unsigned int hotspots,n,j,l;
  1545.         unsigned long MacroDataSize;
  1546.         char *ptr;
  1547.         HOTSPOT *hotspot;
  1548.  
  1549.         hotspots=GetWord(f);
  1550.         MacroDataSize=GetDWord(f);
  1551.         hotspot=my_malloc(hotspots*sizeof(HOTSPOT));
  1552.         f->read(f,hotspot,sizeof(HOTSPOT)*hotspots);
  1553.         if(checkexternal)
  1554.         {
  1555.             while(MacroDataSize-->0) f->get(f);
  1556.         }
  1557.         else
  1558.         {
  1559.             putc(1,fTarget);
  1560.             my_putw(hotspots,fTarget);
  1561.             putdw(MacroDataSize,fTarget);
  1562.             fwrite(hotspot,sizeof(HOTSPOT),hotspots,fTarget);
  1563.             if(MacroDataSize) CopyBytes(f,MacroDataSize,fTarget);
  1564.         }
  1565.         for(n=0;n<hotspots;n++)
  1566.         {
  1567.             j=StringRead(buffer,sizeof(buffer),f)+1;
  1568.             l=j+StringRead(buffer+j,sizeof(buffer)-j,f)+1;
  1569.             if(fTarget) fwrite(buffer,l,1,fTarget);
  1570.             if(extractmacros) switch(hotspot[n].id0)
  1571.             {
  1572.             case 0xC8: /* macro (never seen) */
  1573.             case 0xCC: /* macro without font change */
  1574.             CheckMacro(buffer+j);
  1575.             break;
  1576.             case 0xE0: /* popup jump HC30 */
  1577.             case 0xE1: /* topic jump HC30 */
  1578.             case 0xE2: /* popup jump HC31 */
  1579.             case 0xE3: /* topic jump HC31 */
  1580.             case 0xE6: /* popup jump without font change */
  1581.             case 0xE7: /* topic jump without font change */
  1582.             if(hash(buffer+j)!=hotspot[n].hash)
  1583.             {
  1584.                 fprintf(stderr,"Wrong hash %08lx instead %08lx for '%s'\n",hotspot[n].hash,hash(buffer+j),buffer+j);
  1585.             }
  1586.             AddTopic(buffer+j,FALSE);
  1587.             break;
  1588.             case 0xEA: /* popup jump into external file */
  1589.             case 0xEB: /* topic jump into external file / secondary window */
  1590.             case 0xEE: /* popup jump into external file without font change */
  1591.             case 0xEF: /* topic jump into external file / secondary window without font change */
  1592.             if(hotspot[n].id1!=0&&hotspot[n].id1!=1&&hotspot[n].id1!=4&&hotspot[n].id1!=6||hotspot[n].id2!=0)
  1593.             {
  1594.             }
  1595.             else
  1596.             {
  1597.                 filename=strchr(buffer+j,'@');
  1598.                 if(filename) *filename++='\0';
  1599.                 ptr=strchr(buffer+j,'>');
  1600.                 if(ptr) *ptr='\0';
  1601.                 if(filename)
  1602.                 {
  1603.                 StoreReference(filename,TOPIC,buffer+j,hash(buffer+j));
  1604.                 }
  1605.                 else
  1606.                 {
  1607.                 AddTopic(buffer+j,FALSE);
  1608.                 }
  1609.                 break;
  1610.             }
  1611.             default:
  1612.             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);
  1613.             }
  1614.         }
  1615.         free(hotspot);
  1616.         }
  1617.     }
  1618.     if(!checkexternal)
  1619.     {
  1620.         dwPictureOffset=offset-nextpict;
  1621.         nextpict=ftell(fTarget);
  1622.         /* fix up some locations */
  1623.         fseek(fTarget,pos,SEEK_SET);
  1624.         putdw((dwDataSize<<1)+1,fTarget);
  1625.         putdw((dwHotspotSize<<1)+1,fTarget);
  1626.         putdw(dwPictureOffset,fTarget);
  1627.         if(dwHotspotSize) putdw(dwPictureOffset+dwDataSize,fTarget);
  1628.     }
  1629.     }
  1630.     if(fTarget) my_fclose(fTarget);
  1631.     return type;
  1632. }
  1633. /****************************************************************************
  1634. // END OF GRAPHICS STUFF
  1635. //**************************************************************************/
  1636.  
  1637. char *getbitmapname(unsigned int n) /* retrieve extension of exported bitmap n */
  1638. {
  1639.     static char name[12];
  1640.  
  1641.     if(n<extensions&&extension[n])
  1642.     {
  1643.     sprintf(name,"bm%u.%s",n,bmpext[extension[n]&0x0F]);
  1644.     }
  1645.     else if(n==65535U)
  1646.     {
  1647.     missing=TRUE;
  1648.     fputs("There was a picture file rejected on creation of helpfile.\n",stderr);
  1649.     strcpy(name,"missing.bmp");
  1650.     }
  1651.     else /* should never happen */
  1652.     {
  1653.     warnings=TRUE;
  1654.     fprintf(stderr,"Bitmap bm%u not exported\n",n);
  1655.     sprintf(name,"bm%u.bmp",n);
  1656.     }
  1657.     return name;
  1658. }
  1659.  
  1660. void ListBitmaps(FILE *hpj) /* writes out [BITMAPS] section */
  1661. {
  1662.     int i;
  1663.  
  1664.     if(hpj&&extensions)
  1665.     {
  1666.     fputs("[BITMAPS]\n",hpj);
  1667.     for(i=0;i<extensions;i++) if(extension[i])
  1668.     {
  1669.         fprintf(hpj,"bm%u.%s\n",i,bmpext[extension[i]&0x0F]);
  1670.     }
  1671.     putc('\n',hpj);
  1672.     }
  1673. }
  1674.  
  1675. void ExportBitmaps(FILE *HelpFile) /* export all bitmaps */
  1676. {
  1677.     BUFFER buf;
  1678.     MFILE *mf;
  1679.     char *leader;
  1680.     char FileName[20];
  1681.     long FileLength;
  1682.     int i,num,n,type;
  1683.     long savepos;
  1684.  
  1685.     leader="|bm"+before31;
  1686.     SearchFile(HelpFile,NULL,NULL);
  1687.     for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
  1688.     {
  1689.     for(i=0;i<n;i++)
  1690.     {
  1691.         my_gets(FileName,sizeof(FileName),HelpFile);
  1692.         getdw(HelpFile);
  1693.         if(memcmp(FileName,leader,strlen(leader))==0)
  1694.         {
  1695.         savepos=ftell(HelpFile);
  1696.         if(SearchFile(HelpFile,FileName,&FileLength))
  1697.         {
  1698.             mf=CreateVirtual(HelpFile);
  1699.             type=ExtractBitmap(FileName+(FileName[0]=='|'),mf);
  1700.             CloseMap(mf);
  1701.             if(type)
  1702.             {
  1703.             num=atoi(FileName+(FileName[0]=='|')+2);
  1704.             if(num>=extensions)
  1705.             {
  1706.                 extension=my_realloc(extension,(num+1)*sizeof(char));
  1707.                 while(extensions<=num) extension[extensions++]=0;
  1708.             }
  1709.             extension[num]=type;
  1710.             }
  1711.         }
  1712.         fseek(HelpFile,savepos,SEEK_SET);
  1713.         }
  1714.     }
  1715.     }
  1716. }
  1717.  
  1718. char *TopicName(long topic)
  1719. {
  1720.     static char name[20];
  1721.     int i;
  1722.  
  1723.     if(before31)
  1724.     {
  1725.     if(topic==0L) topic=Topic[0];
  1726.     for(i=16;i<Topics;i++) if(Topic[i]==topic)
  1727.     {
  1728.         sprintf(name,"TOPIC%d",i);
  1729.         return name;
  1730.     }
  1731.     }
  1732.     else
  1733.     {
  1734.     if(topic==-1L)
  1735.     {
  1736.         NotInAnyTopic=TRUE;
  1737.         return "21KSYK4"; /* evaluates to -1 without generating help compiler warning */
  1738.     }
  1739.     i=FindContext(topic);
  1740.     if(i!=-1)
  1741.     {
  1742.         return unhash(ContextRec[i].HashValue);
  1743.     }
  1744.     }
  1745.     if(topic) fprintf(stderr,"Can not find topic offset %08lx\n",topic);
  1746.     return NULL;
  1747. }
  1748.  
  1749. char *GetWindowName(long n) /* secondary window name from window number */
  1750. {
  1751.     if(windowname==NULL||n<0||n>=windownames||windowname[n]==NULL) return "main";
  1752.     return windowname[n];
  1753. }
  1754.  
  1755. /* create HPJ file from contents of |SYSTEM internal file */
  1756. void SysList(FILE *HelpFile,FILE *hpj,char *IconFileName)
  1757. {
  1758.     long FileLength;
  1759.     SYSTEMHEADER SysHdr;
  1760.     SYSTEMRECORD *SysRec;
  1761.     STOPHEADER StopHdr;
  1762.     char name[51];
  1763.     char *ptr;
  1764.     long color;
  1765.     FILE *f;
  1766.     int fbreak,macro,windows,i,keywords,dllmaps,n;
  1767.  
  1768.     if(hpj&&SearchFile(HelpFile,"|SYSTEM",NULL))
  1769.     {
  1770.     my_fread(&SysHdr,sizeof(SysHdr),HelpFile);
  1771.     if(SysHdr.Minor==15)
  1772.     {
  1773.         strcpy(helpcomp,"HC30");
  1774.     }
  1775.     else if(SysHdr.Minor==21)
  1776.     {
  1777.         strcpy(helpcomp,"HC31/HCP");
  1778.     }
  1779.     else if(SysHdr.Minor==27)
  1780.     {
  1781.         strcpy(helpcomp,"WMVC/MVCC");
  1782.     }
  1783.     else if(SysHdr.Minor==33)
  1784.     {
  1785.         if(mvp)
  1786.         {
  1787.         strcpy(helpcomp,"MVC");
  1788.         }
  1789.         else
  1790.         {
  1791.         strcpy(helpcomp,"HCRTF");
  1792.         }
  1793.     }
  1794.     fputs("[OPTIONS]\n",hpj);
  1795.     if(before31) /* If 3.0 get title */
  1796.     {
  1797.         my_gets(HelpFileTitle,33,HelpFile);
  1798.         if(HelpFileTitle[0]!='\0'&&HelpFileTitle[0]!='\n')
  1799.         {
  1800.         fprintf(hpj,"TITLE=%s\n",HelpFileTitle);
  1801.         }
  1802.         fprintf(hpj,"INDEX=%s\n",TopicName(0L));
  1803.         if(PhraseCount)
  1804.         {
  1805.         fputs("COMPRESS=TRUE\n",hpj);
  1806.         }
  1807.         else
  1808.         {
  1809.         fputs("COMPRESS=FALSE\n",hpj);
  1810.         }
  1811.         for(i='A';i<='z';i++) if(lists[i-'0']&&i!='K')
  1812.         {
  1813.         fprintf(hpj,"MULTIKEY=%c\n",i);
  1814.         }
  1815.         putc('\n',hpj);
  1816.     }
  1817.     else  /* else get 3.1 System records */
  1818.     {
  1819.         macro=0;
  1820.         fbreak=0;
  1821.         windows=0;
  1822.         keywords=0;
  1823.         dllmaps=0;
  1824.         for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  1825.         {
  1826.         switch(SysRec->RecordType)
  1827.         {
  1828.         case 0x0001:
  1829.             if(SysRec->Data[0]) fprintf(hpj,"TITLE=%s\n",SysRec->Data);
  1830.             break;
  1831.         case 0x0002:
  1832.             ptr=strchr(SysRec->Data,'\r');
  1833.             if(ptr) strcpy(ptr,"%date");
  1834.             if(SysRec->Data[0]) fprintf(hpj,"COPYRIGHT=%s\n",SysRec->Data);
  1835.             break;
  1836.         case 0x0003:
  1837.             ptr=TopicName(*(long *)SysRec->Data);
  1838.             if(ptr) fprintf(hpj,"CONTENTS=%s\n",ptr);
  1839.             break;
  1840.         case 0x0004:
  1841.             macro=1;
  1842.             break;
  1843.         case 0x0005:
  1844.             fprintf(hpj,"ICON=%s\n",IconFileName);
  1845.             f=my_fopen(IconFileName,"wb");
  1846.             if(f)
  1847.             {
  1848.             fwrite(SysRec->Data,SysRec->DataSize,1,f);
  1849.             my_fclose(f);
  1850.             }
  1851.             break;
  1852.         case 0x0006:
  1853.             windows++;
  1854.             break;
  1855.         case 0x0008:
  1856.             if(SysRec->Data[0]) fprintf(hpj,"CITATION=%s\n",SysRec->Data);
  1857.             break;
  1858.         case 0x0009:
  1859.             if(!mvp) fprintf(hpj,"LCID=0x%X 0x%X 0x%X\n",*(short *)(SysRec->Data+8),*(short *)SysRec->Data,*(short *)(SysRec->Data+2));
  1860.             break;
  1861.         case 0x000A:
  1862.             if(!mvp&&SysRec->Data[0]) fprintf(hpj,"CNT=%s\n",SysRec->Data);
  1863.             break;
  1864.         case 0x000B:
  1865. //            if(!mvp) fprintf(hpj,"CHARSET=%d\n",*(unsigned char *)(SysRec->Data+1));
  1866.             break;
  1867.         case 0x000C:
  1868.             if(mvp)
  1869.             {
  1870.             fbreak=1;
  1871.             }
  1872.             else
  1873.             {
  1874.             fprintf(hpj,"DEFFONT=%s,%d,%d\n",SysRec->Data+2,*(unsigned char *)SysRec->Data,*(unsigned char *)(SysRec->Data+1));
  1875.             }
  1876.             break;
  1877.         case 0x000D:
  1878.             if(mvp) groups++;
  1879.             break;
  1880.         case 0x000E:
  1881.             if(mvp)
  1882.             {
  1883.             keywords=1;
  1884.             }
  1885.             else
  1886.             {
  1887.             fprintf(hpj,"INDEX_SEPARATORS=\"%s\"\n",SysRec->Data);
  1888.             strlcpy(index_separators,SysRec->Data,sizeof(index_separators));
  1889.             }
  1890.             break;
  1891.         case 0x0012:
  1892.             if(SysRec->Data[0]) fprintf(hpj,"LANGUAGE=%s\n",SysRec->Data);
  1893.             break;
  1894.         case 0x0013:
  1895.             dllmaps=1;
  1896.             break;
  1897.         }
  1898.         }
  1899.         if(win95)
  1900.         {
  1901.         i=0;
  1902.         if(lzcompressed) i|=8;
  1903.         if(Hall) i|=4; else if(PhraseCount) i|=2;
  1904.         fprintf(hpj,"COMPRESS=%d\n",i);
  1905.         }
  1906.         else if(!lzcompressed)
  1907.         {
  1908.         fputs("COMPRESS=OFF\n",hpj);
  1909.         }
  1910.         else if(PhraseCount)
  1911.         {
  1912.         fputs("COMPRESS=HIGH\n",hpj);
  1913.         }
  1914.         else
  1915.         {
  1916.         fputs("COMPRESS=MEDIUM\n",hpj);
  1917.         }
  1918.         if(SysHdr.Flags==8) fputs("CDROMOPT=TRUE\n",hpj);
  1919.         for(i='A';i<='z';i++) if(lists[i-'0']&&i!='K'&&(i!='A'||!win95))
  1920.         {
  1921.         fprintf(hpj,"MULTIKEY=%c\n",i);
  1922.         }
  1923.         putc('\n',hpj);
  1924.         if(windows)
  1925.         {
  1926.         fputs("[WINDOWS]\n",hpj);
  1927.         for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  1928.         {
  1929.             if(SysRec->RecordType==0x0006)
  1930.             {
  1931.             if(SysRec->DataSize==sizeof(SECWINDOW))
  1932.             {
  1933.                 PrintWindow(hpj,(SECWINDOW *)SysRec->Data);
  1934.             }
  1935.             else
  1936.             {
  1937.                 PrintMVBWindow(hpj,(MVBWINDOW *)SysRec->Data);
  1938.             }
  1939.             }
  1940.         }
  1941.         putc('\n',hpj);
  1942.         }
  1943.         if(macro)
  1944.         {
  1945.         fputs("[CONFIG]\n",hpj);
  1946.         for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  1947.         {
  1948.             if(SysRec->RecordType==0x0004)
  1949.             {
  1950.             if(sscanf(SysRec->Data,"SPC(%ld)%n",&color,&n)>0)
  1951.             {
  1952.                 fprintf(hpj,"SPC(%u,%u,%u)%s\n",(unsigned char)(color),(unsigned char)(color>>8),(unsigned char)(color>>16),SysRec->Data+n);
  1953.             }
  1954.             else
  1955.             {
  1956.                 fprintf(hpj,"%s\n",SysRec->Data);
  1957.             }
  1958.             }
  1959.         }
  1960.         putc('\n',hpj);
  1961.         }
  1962.         if(fbreak)
  1963.         {
  1964.         fputs("[FTINDEX]\n",hpj);
  1965.         for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  1966.         {
  1967.             if(SysRec->RecordType==0x000C)
  1968.             {
  1969.             ptr=strtok(SysRec->Data," ");
  1970.             if(ptr)
  1971.             {
  1972.                 fprintf(hpj,"dtype%s",ptr);
  1973.                 ptr=strtok(NULL," ");
  1974.                 if(ptr)
  1975.                 {
  1976.                 fprintf(hpj,"=%s",ptr);
  1977.                 ptr=strtok(NULL," ");
  1978.                 if(ptr)
  1979.                 {
  1980.                     fprintf(hpj,"!%s",ptr);
  1981.                     ptr=strtok(NULL," ");
  1982.                     if(ptr)
  1983.                     {
  1984.                     fprintf(hpj,",%s",ptr+1);
  1985.                     if(SearchFile(HelpFile,ptr,NULL))
  1986.                     {
  1987.                         for(n=0;n<stopwordfiles;n++)
  1988.                         {
  1989.                         if(strcmp(stopwordfilename[n],ptr)==0) break;
  1990.                         }
  1991.                         if(n==stopwordfiles)
  1992.                         {
  1993.                         stopwordfilename=my_realloc(stopwordfilename,(stopwordfiles+1)*sizeof(char *));
  1994.                         stopwordfilename[stopwordfiles++]=my_strdup(ptr);
  1995.                         f=my_fopen(ptr+1,"wt");
  1996.                         if(f)
  1997.                         {
  1998.                             my_fread(&StopHdr,sizeof(StopHdr),HelpFile);
  1999.                             for(n=0;n<StopHdr.BytesUsed;n+=1+strlen(buffer))
  2000.                             {
  2001.                             i=getc(HelpFile);
  2002.                             my_fread(buffer,i,HelpFile);
  2003.                             buffer[i]='\0';
  2004.                             fprintf(f,"%s\n",buffer);
  2005.                             }
  2006.                             my_fclose(f);
  2007.                         }
  2008.                         }
  2009.                     }
  2010.                     ptr=strtok(NULL," ");
  2011.                     if(ptr) fprintf(hpj,",%s",ptr);
  2012.                     }
  2013.                 }
  2014.                 }
  2015.                 putc('\n',hpj);
  2016.             }
  2017.             }
  2018.         }
  2019.         putc('\n',hpj);
  2020.         }
  2021.         if(groups||multi&&browsenums>1)
  2022.         {
  2023.         group=my_malloc(groups*sizeof(GROUP));
  2024.         fputs("[GROUPS]\n",hpj);
  2025.         i=0;
  2026.         for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  2027.         {
  2028.             if(SysRec->RecordType==0x000D)
  2029.             {
  2030.             ptr=strchr(SysRec->Data,' ');
  2031.             if(ptr) *ptr++='\0';
  2032.             groups=SearchFile(HelpFile,SysRec->Data,NULL);
  2033.             n=strcspn(SysRec->Data,".");
  2034.             SysRec->Data[n]='\0';
  2035.             if(ptr&&strcmp(ptr,"\"\" ")==0)
  2036.             {
  2037.                 fprintf(hpj,"group=%s\n",SysRec->Data);
  2038.             }
  2039.             else
  2040.             {
  2041.                 fprintf(hpj,"group=%s,%s\n",SysRec->Data,ptr);
  2042.             }
  2043.             group[i].Name=my_strdup(SysRec->Data);
  2044.             if(groups)
  2045.             {
  2046.                 my_fread(&group[i].GroupHeader,sizeof(group[i].GroupHeader),HelpFile);
  2047.                 if(group[i].GroupHeader.GroupType==2)
  2048.                 {
  2049.                 group[i].Bitmap=my_malloc(group[i].GroupHeader.BitmapSize);
  2050.                 my_fread(group[i].Bitmap,group[i].GroupHeader.BitmapSize,HelpFile);
  2051.                 }
  2052.             }
  2053.             else
  2054.             {
  2055.                 group[i].GroupHeader.GroupType=0;
  2056.             }
  2057.             i++;
  2058.             }
  2059.         }
  2060.         if(multi) for(i=1;i<browsenums;i++) fprintf(hpj,"group=BROWSE%04x\n",i);
  2061.         if(SearchFile(HelpFile,"|GMACROS",&FileLength))
  2062.         {
  2063.             long len;
  2064.             long pos;
  2065.             long off;
  2066.  
  2067.             getdw(HelpFile); /* possible count or group number */
  2068.             for(pos=4L;pos<FileLength;pos+=len)
  2069.             {
  2070.             len=getdw(HelpFile); /* length of record containing two longs and two strings */
  2071.             off=getdw(HelpFile); /* offset of second string in record (first is at pos 8) */
  2072.             if(off<=0L) off=len;
  2073.             if(len<8) break;
  2074.             if(len<off) break;
  2075.             if(off>8)
  2076.             {
  2077.                 my_fread(buffer,off-8,HelpFile);
  2078.                 buffer[off-8]='\0';
  2079.                 fprintf(hpj,"entry=%s\n",buffer);
  2080.             }
  2081.             if(len>off)
  2082.             {
  2083.                 my_fread(buffer,len-off,HelpFile);
  2084.                 buffer[len-off]='\0';
  2085.                 fprintf(hpj,"exit=%s\n",buffer);
  2086.             }
  2087.             }
  2088.         }
  2089.         putc('\n',hpj);
  2090.         }
  2091.         if(dllmaps)
  2092.         {
  2093.         fputs("[DLLMAPS]\n",hpj);
  2094.         for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  2095.         {
  2096.             if(SysRec->RecordType==0x0013)
  2097.             {
  2098.             if(strcmp(SysRec->Data,"MVMCI")!=0&&strcmp(SysRec->Data,"MVIMAGE")!=0&&strcmp(SysRec->Data,"MVBRKR")!=0)
  2099.             {
  2100.                 ptr=SysRec->Data+strlen(SysRec->Data)+1;
  2101.                 fprintf(hpj,"%s=%s,",SysRec->Data,ptr);
  2102.                 ptr=ptr+strlen(ptr)+1;
  2103.                 fprintf(hpj,"%s,",ptr);
  2104.                 ptr=ptr+strlen(ptr)+1;
  2105.                 fprintf(hpj,"%s,",ptr);
  2106.                 ptr=ptr+strlen(ptr)+1;
  2107.                 fprintf(hpj,"%s\n",ptr);
  2108.             }
  2109.             }
  2110.         }
  2111.         putc('\n',hpj);
  2112.         }
  2113.         if(keywords)
  2114.         {
  2115.         fputs("[KEYINDEX]\n",hpj);
  2116.         for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  2117.         {
  2118.             if(SysRec->RecordType==0x000E)
  2119.             {
  2120.             fprintf(hpj,"keyword=%c,\"%s\"\n",SysRec->Data[1],SysRec->Data+30);
  2121.             }
  2122.         }
  2123.         putc('\n',hpj);
  2124.         }
  2125.         for(i=0;i<windows;i++)
  2126.         {
  2127.         sprintf(name,"|CF%d",i);
  2128.         if(SearchFile(HelpFile,name,&FileLength))
  2129.         {
  2130.             fprintf(hpj,"[CONFIG:%d]\n",i);
  2131.             /* may use [CONFIG-GetWindowName] instead, but WindowName need not be defined */
  2132.             for(n=0;n<FileLength;n+=strlen(buffer)+1)
  2133.             {
  2134.             my_gets(buffer,sizeof(buffer),HelpFile);
  2135.             fprintf(hpj,"%s\n",buffer);
  2136.             }
  2137.             putc('\n',hpj);
  2138.         }
  2139.         }
  2140.     }
  2141.     }
  2142. }
  2143.  
  2144. /* load phrases for decompression from old Phrases file or new PhrIndex,
  2145. // PhrImage files of HCRTF */
  2146. BOOL PhraseLoad(FILE *HelpFile)
  2147. {
  2148.     long FileLength;
  2149.     char junk[30];
  2150.     BOOL newphrases;
  2151.     PHRINDEXHDR PhrIndexHdr;
  2152.     unsigned int n;
  2153.     long l,offset;
  2154.     long SavePos;
  2155.  
  2156.     if(SearchFile(HelpFile,"|PhrIndex",NULL))
  2157.     {
  2158.     my_fread(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile);
  2159.     SavePos=ftell(HelpFile);
  2160.     if(SearchFile(HelpFile,"|PhrImage",&FileLength))
  2161.     {
  2162.         if(FileLength!=PhrIndexHdr.phrimagecompressedsize)
  2163.         {
  2164.         fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileLength);
  2165.         }
  2166.         PhraseCount=(unsigned int)PhrIndexHdr.entries;
  2167.         PhraseOffsets=my_malloc(sizeof(unsigned int)*(PhraseCount+1));
  2168.         Phrases=my_malloc(PhrIndexHdr.phrimagesize);
  2169.         if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize)
  2170.         {
  2171.         my_fread(Phrases,PhrIndexHdr.phrimagesize,HelpFile);
  2172.         }
  2173.         else
  2174.         {
  2175.         DecompressIntoBuffer(2,HelpFile,FileLength,Phrases,PhrIndexHdr.phrimagesize);
  2176.         }
  2177.         fseek(HelpFile,SavePos,SEEK_SET);
  2178.         GetBit(NULL);
  2179.         offset=0;
  2180.         PhraseOffsets[0]=offset;
  2181.         for(l=0;l<PhrIndexHdr.entries;l++)
  2182.         {
  2183.         for(n=1;GetBit(HelpFile);n+=1<<PhrIndexHdr.bits) ;
  2184.         if(GetBit(HelpFile)) n+=1;
  2185.         if(PhrIndexHdr.bits>1) if(GetBit(HelpFile)) n+=2;
  2186.         if(PhrIndexHdr.bits>2) if(GetBit(HelpFile)) n+=4;
  2187.         if(PhrIndexHdr.bits>3) if(GetBit(HelpFile)) n+=8;
  2188.         if(PhrIndexHdr.bits>4) if(GetBit(HelpFile)) n+=16;
  2189.         offset+=n;
  2190.         PhraseOffsets[(int)l+1]=offset;
  2191.         }
  2192.     }
  2193.     Hall=TRUE;
  2194.     fprintf(stderr,"%u phrases loaded\n",PhraseCount);
  2195.     }
  2196.     else if(SearchFile(HelpFile,"|Phrases",&FileLength))
  2197.     {
  2198.     PhraseCount=my_getw(HelpFile);
  2199.     newphrases=PhraseCount==0x0800; /* VC4.0: MSDEV\HELP\MSDEV40.MVB */
  2200.     if(newphrases) PhraseCount=my_getw(HelpFile);
  2201.     if(my_getw(HelpFile)!=0x0100)
  2202.     {
  2203.         error("Unknown |Phrases file structure");
  2204.         return FALSE;
  2205.     }
  2206.     if(PhraseCount)
  2207.     {
  2208.         if(before31)
  2209.         {
  2210.         offset=(PhraseCount+1)*sizeof(short);
  2211.         FileLength-=(PhraseCount+1)*sizeof(short)+4;
  2212.         l=FileLength;
  2213.         }
  2214.         else
  2215.         {
  2216.         l=getdw(HelpFile);
  2217.         if(newphrases)
  2218.         {
  2219.             my_fread(&junk,sizeof(junk),HelpFile);
  2220.             offset=(PhraseCount+1)*sizeof(short);
  2221.             FileLength-=(PhraseCount+1)*sizeof(short)+sizeof(junk)+10;
  2222.         }
  2223.         else
  2224.         {
  2225.             offset=(PhraseCount+1)*sizeof(short);
  2226.             FileLength-=(PhraseCount+1)*sizeof(short)+8;
  2227.         }
  2228.         }
  2229.         PhraseOffsets=my_malloc(sizeof(unsigned int)*(PhraseCount+1));
  2230.         for(n=0;n<=PhraseCount;n++) PhraseOffsets[n]=my_getw(HelpFile)-offset;
  2231.         Phrases=my_malloc(l);
  2232.         DecompressIntoBuffer((before31?0:2),HelpFile,FileLength,Phrases,l);
  2233.         fprintf(stderr,"%u phrases loaded\n",PhraseCount);
  2234.     }
  2235.     Hall=FALSE;
  2236.     }
  2237.     return TRUE;
  2238. }
  2239.  
  2240. /* write phrase PhraseNum to out and returns advanced out
  2241. // or to f it out = NULL or uses PrintString if f = NULL, returns NULL then */
  2242. char *PrintPhrase(unsigned int PhraseNum,char *out,FILE *f)
  2243. {
  2244.     char *ptr;
  2245.     size_t len;
  2246.  
  2247.     if(PhraseNum>=PhraseCount)
  2248.     {
  2249.     error("Phrase %u does not exist",PhraseNum);
  2250.     return out;
  2251.     }
  2252.     ptr=Phrases+PhraseOffsets[PhraseNum];
  2253.     len=PhraseOffsets[PhraseNum+1]-PhraseOffsets[PhraseNum];
  2254.     if(out)
  2255.     {
  2256.     memcpy(out,ptr,len);
  2257.     return out+len;
  2258.     }
  2259.     if(f)
  2260.     {
  2261.     fwrite(ptr,len,1,f);
  2262.     }
  2263.     else
  2264.     {
  2265.     PrintString(ptr,len);
  2266.     }
  2267.     return NULL;
  2268. }
  2269.  
  2270. /* writeout .PH file from already loaded phrases */
  2271. void PhraseList(char *FileName)
  2272. {
  2273.     FILE *f;
  2274.     unsigned int n;
  2275.  
  2276.     if(PhraseCount)
  2277.     {
  2278.     f=my_fopen(FileName,"wt");
  2279.     if(f)
  2280.     {
  2281.         for(n=0;n<PhraseCount;n++)
  2282.         {
  2283.         PrintPhrase(n,NULL,f);
  2284.         putc('\n',f);
  2285.         }
  2286.         my_fclose(f);
  2287.     }
  2288.     }
  2289. }
  2290.  
  2291. char *FontFamily(unsigned int i)
  2292. {
  2293.     static char *familyname[]={"nil","roman","swiss","modern","script","decor","tech"};
  2294.  
  2295.     if(i>0&&i<7) return familyname[i];
  2296.     return familyname[0];
  2297. }
  2298.  
  2299. /* collect another color into color table */
  2300. unsigned char AddColor(unsigned char r,unsigned char g,unsigned char b)
  2301. {
  2302.     int n;
  2303.  
  2304.     for(n=0;n<colors;n++)
  2305.     {
  2306.     if(r==color[n].r&&g==color[n].g&&b==color[n].b) break;
  2307.     }
  2308.     if(n==colors)
  2309.     {
  2310.     color[colors].r=r;
  2311.     color[colors].g=g;
  2312.     color[colors].b=b;
  2313.     colors++;
  2314.     }
  2315.     return n;
  2316. }
  2317.  
  2318. /* load fonts from help file, filling up internal font structure,
  2319. // writing fonttbl, colortbl, and styletbl to rtf file */
  2320. void FontLoad(FILE *HelpFile,FILE *rtf,FILE *hpj)
  2321. {
  2322.     static char *BestFonts[]={"Arial","Times New Roman","MS Sans Serif","MS Serif","Helv","TmsRmn","MS Sans Serif","Helvetica","Times Roman","Times"};
  2323.     CHARMAPHEADER CharmapHeader;
  2324.     FONTHEADER FontHdr;
  2325.     FILE *f;
  2326.     char FontName[33];
  2327.     char CharMap[33];
  2328.     char *ptr;
  2329.     char *p;
  2330.     long FontStart;
  2331.     int i,j,k,l,len;
  2332.     unsigned char *family;
  2333.     BOOL charmap;
  2334.     OLDFONT oldfont;
  2335.     NEWFONT newfont;
  2336.     MVBFONT mvbfont;
  2337.     MVBSTYLE *mvbstyle;
  2338.     NEWSTYLE *newstyle;
  2339.     FONTDESCRIPTOR *fd;
  2340.  
  2341.     if(SearchFile(HelpFile,"|FONT",NULL))
  2342.     {
  2343.     FontStart=ftell(HelpFile);
  2344.     my_fread(&FontHdr,sizeof(FontHdr),HelpFile);
  2345.     fontnames=FontHdr.NumFacenames;
  2346.     len=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/fontnames;
  2347.     fontname=my_malloc(fontnames*sizeof(char *));
  2348.     family=my_malloc(fontnames*sizeof(unsigned char));
  2349.     memset(family,0,fontnames*sizeof(unsigned char));
  2350.     charmap=FALSE;
  2351.     mvbstyle=NULL;
  2352.     newstyle=NULL;
  2353.     for(i=0;i<fontnames;i++)
  2354.     {
  2355.         fseek(HelpFile,FontStart+FontHdr.FacenamesOffset+len*i,SEEK_SET);
  2356.         my_fread(FontName,len,HelpFile);
  2357.         FontName[len]='\0';
  2358.         ptr=strchr(FontName,',');
  2359.         if(ptr&&FontHdr.FacenamesOffset>=16)
  2360.         {
  2361.         *ptr++='\0';
  2362.         fseek(HelpFile,FontStart+FontHdr.CharmapsOffset,SEEK_SET);
  2363.         for(j=0;hpj&&j<FontHdr.NumCharmaps;j++)
  2364.         {
  2365.             my_fread(CharMap,32,HelpFile);
  2366.             CharMap[32]='\0';
  2367.             p=strchr(CharMap,',');
  2368.             if(p&&strcmp(p+1,ptr)==0&&strcmp(CharMap,"|MVCHARTAB,0")!=0)
  2369.             {
  2370.             if(!charmap)
  2371.             {
  2372.                 fputs("[CHARMAP]\n",hpj);
  2373.                 charmap=TRUE;
  2374.             }
  2375.             *p++='\0';
  2376.             if(strcmp(p,"0")==0)
  2377.             {
  2378.                 fprintf(hpj,"DEFAULT=%s\n",CharMap);
  2379.             }
  2380.             else
  2381.             {
  2382.                 fprintf(hpj,"%s=%s\n",FontName,CharMap);
  2383.             }
  2384.             break;
  2385.             }
  2386.         }
  2387.         }
  2388.         fontname[i]=my_strdup(FontName);
  2389.     }
  2390.     if(charmap) putc('\n',hpj);
  2391.     if(hpj&&FontHdr.FacenamesOffset>=16) for(j=0;j<FontHdr.NumCharmaps;j++)
  2392.     {
  2393.         fseek(HelpFile,FontStart+FontHdr.CharmapsOffset+j*32,SEEK_SET);
  2394.         my_fread(CharMap,32,HelpFile);
  2395.         CharMap[32]='\0';
  2396.         p=strchr(CharMap,',');
  2397.         if(p&&strcmp(CharMap,"|MVCHARTAB,0")!=0)
  2398.         {
  2399.         *p++='\0';
  2400.         if(SearchFile(HelpFile,CharMap,NULL))
  2401.         {
  2402.             my_fread(&CharmapHeader,sizeof(CHARMAPHEADER),HelpFile);
  2403.             f=my_fopen(CharMap,"wt");
  2404.             if(f)
  2405.             {
  2406.             fprintf(f,"%d,\n",CharmapHeader.Entries);
  2407.             for(k=0;k<CharmapHeader.Entries;k++)
  2408.             {
  2409.                 fprintf(f,"%5u,",my_getw(HelpFile));
  2410.                 fprintf(f,"%5u,",my_getw(HelpFile));
  2411.                 fprintf(f,"%3u,",getc(HelpFile));
  2412.                 fprintf(f,"%3u,",getc(HelpFile));
  2413.                 fprintf(f,"%3u,",getc(HelpFile));
  2414.                 fprintf(f,"%3u,\n",getc(HelpFile));
  2415.                 my_getw(HelpFile);
  2416.             }
  2417.             fprintf(f,"%d,\n",CharmapHeader.Ligatures);
  2418.             for(k=0;k<CharmapHeader.Ligatures;k++)
  2419.             {
  2420.                 for(l=0;l<CharmapHeader.LigLen;l++)
  2421.                 {
  2422.                 fprintf(f,"%3u,",getc(HelpFile));
  2423.                 }
  2424.                 putc('\n',f);
  2425.             }
  2426.             my_fclose(f);
  2427.             }
  2428.         }
  2429.         }
  2430.     }
  2431.     fseek(HelpFile,FontStart+FontHdr.DescriptorsOffset,SEEK_SET);
  2432.     colors=1;     /* auto */
  2433.     color[0].r=1;
  2434.     color[0].g=1;
  2435.     color[0].b=0;
  2436.     fonts=FontHdr.NumDescriptors;
  2437.     if(font) free(font);
  2438.     font=my_malloc(fonts*sizeof(FONTDESCRIPTOR));
  2439.     memset(font,0,fonts*sizeof(FONTDESCRIPTOR));
  2440.     if(FontHdr.FacenamesOffset>=16)
  2441.     {
  2442.         scaling=1L;
  2443.         rounderr=0;
  2444.         for(i=0;i<FontHdr.NumDescriptors;i++)
  2445.         {
  2446.         my_fread(&mvbfont,sizeof(mvbfont),HelpFile);
  2447.         fd=font+i;
  2448.         fd->FontName=mvbfont.FontName;
  2449.         fd->HalfPoints=-2L*mvbfont.Height;
  2450.         fd->Bold=mvbfont.Weight>500;
  2451.         fd->Italic=mvbfont.Italic!=0;
  2452.         fd->Underline=mvbfont.Underline!=0;
  2453.         fd->StrikeOut=mvbfont.StrikeOut!=0;
  2454.         fd->DoubleUnderline=mvbfont.DoubleUnderline!=0;
  2455.         fd->SmallCaps=mvbfont.SmallCaps!=0;
  2456.         fd->textcolor=AddColor(mvbfont.FGRGB[0],mvbfont.FGRGB[1],mvbfont.FGRGB[2]);
  2457.         fd->backcolor=AddColor(mvbfont.BGRGB[0],mvbfont.BGRGB[1],mvbfont.BGRGB[2]);
  2458.         fd->FontFamily=mvbfont.PitchAndFamily>>4;
  2459.         fd->style=mvbfont.style;
  2460.         fd->up=mvbfont.up;
  2461.         fd->expndtw=mvbfont.expndtw;
  2462.         }
  2463.         fseek(HelpFile,FontStart+FontHdr.FormatsOffset,SEEK_SET);
  2464.         mvbstyle=my_malloc(FontHdr.NumFormats*sizeof(MVBSTYLE));
  2465.         my_fread(mvbstyle,FontHdr.NumFormats*sizeof(MVBSTYLE),HelpFile);
  2466.         for(i=0;i<FontHdr.NumFormats;i++)
  2467.         {
  2468.         MVBSTYLE *m;
  2469.  
  2470.         m=mvbstyle+i;
  2471.         m->font.FGRGB[0]=AddColor(m->font.FGRGB[0],m->font.FGRGB[1],m->font.FGRGB[2]);
  2472.         m->font.BGRGB[0]=AddColor(m->font.BGRGB[0],m->font.BGRGB[1],m->font.BGRGB[2]);
  2473.         }
  2474.     }
  2475.     else if(FontHdr.FacenamesOffset>=12)
  2476.     {
  2477.         scaling=1L;
  2478.         rounderr=0;
  2479.         for(i=0;i<FontHdr.NumDescriptors;i++)
  2480.         {
  2481.         my_fread(&newfont,sizeof(newfont),HelpFile);
  2482.         fd=font+i;
  2483.         fd->Bold=newfont.Weight>500;
  2484.         fd->Italic=newfont.Italic!=0;
  2485.         fd->Underline=newfont.Underline!=0;
  2486.         fd->StrikeOut=newfont.StrikeOut!=0;
  2487.         fd->DoubleUnderline=newfont.DoubleUnderline!=0;
  2488.         fd->SmallCaps=newfont.SmallCaps!=0;
  2489.         fd->FontName=newfont.FontName;
  2490.         fd->HalfPoints=-2L*newfont.Height;
  2491.         fd->textcolor=AddColor(newfont.FGRGB[0],newfont.FGRGB[1],newfont.FGRGB[2]);
  2492.         fd->backcolor=AddColor(newfont.BGRGB[0],newfont.BGRGB[1],newfont.BGRGB[2]);
  2493.         fd->FontFamily=newfont.PitchAndFamily>>4;
  2494.         }
  2495.         fseek(HelpFile,FontStart+FontHdr.FormatsOffset,SEEK_SET);
  2496.         newstyle=my_malloc(FontHdr.NumFormats*sizeof(NEWSTYLE));
  2497.         my_fread(newstyle,FontHdr.NumFormats*sizeof(NEWSTYLE),HelpFile);
  2498.         for(i=0;i<FontHdr.NumFormats;i++)
  2499.         {
  2500.         NEWSTYLE *m;
  2501.  
  2502.         m=newstyle+i;
  2503.         m->font.FGRGB[0]=AddColor(m->font.FGRGB[0],m->font.FGRGB[1],m->font.FGRGB[2]);
  2504.         m->font.BGRGB[0]=AddColor(m->font.BGRGB[0],m->font.BGRGB[1],m->font.BGRGB[2]);
  2505.         }
  2506.     }
  2507.     else
  2508.     {
  2509.         scaling=10L;
  2510.         rounderr=5;
  2511.         for(i=0;i<FontHdr.NumDescriptors;i++)
  2512.         {
  2513.         my_fread(&oldfont,sizeof(oldfont),HelpFile);
  2514.         fd=font+i;
  2515.         fd->Bold=(oldfont.Attributes&FONT_BOLD)!=0;
  2516.         fd->Italic=(oldfont.Attributes&FONT_ITAL)!=0;
  2517.         fd->Underline=(oldfont.Attributes&FONT_UNDR)!=0;
  2518.         fd->StrikeOut=(oldfont.Attributes&FONT_STRK)!=0;
  2519.         fd->DoubleUnderline=(oldfont.Attributes&FONT_DBUN)!=0;
  2520.         fd->SmallCaps=(oldfont.Attributes&FONT_SMCP)!=0;
  2521.         fd->FontName=oldfont.FontName;
  2522.         fd->HalfPoints=oldfont.HalfPoints;
  2523.         fd->textcolor=AddColor(oldfont.FGRGB[0],oldfont.FGRGB[1],oldfont.FGRGB[2]);
  2524.         fd->backcolor=AddColor(oldfont.BGRGB[0],oldfont.BGRGB[1],oldfont.BGRGB[2]);
  2525.         if(oldfont.FontFamily<6)
  2526.         {
  2527.             fd->FontFamily=lookup[oldfont.FontFamily];
  2528.         }
  2529.         else
  2530.         {
  2531.             fd->FontFamily=oldfont.FontFamily;
  2532.         }
  2533.         }
  2534.     }
  2535.     for(i=0;i<FontHdr.NumDescriptors;i++)
  2536.     {
  2537.         if(font[i].FontName<fontnames)
  2538.         {
  2539.         family[font[i].FontName]=font[i].FontFamily;
  2540.         }
  2541.     }
  2542.     DefFont=0;
  2543.     l=sizeof(BestFonts)/sizeof(BestFonts[0]);
  2544.     if(fontname)
  2545.     {
  2546.         for(i=0;i<fontnames;i++) if(family[i])
  2547.         {
  2548.         for(j=0;j<l;j++)
  2549.         {
  2550.             if(stricmp(fontname[i],BestFonts[j])==0)
  2551.             {
  2552.             DefFont=i;
  2553.             l=j;
  2554.             break;
  2555.             }
  2556.         }
  2557.         }
  2558.     }
  2559.     fprintf(rtf,"{\\rtf1\\ansi\\deff%d\n{\\fonttbl",DefFont);
  2560.     for(i=0;i<fontnames;i++)
  2561.     {
  2562.         fprintf(rtf,"{\\f%d\\f%s %s;}",i,FontFamily(family[i]),fontname[i]);
  2563.         free(fontname[i]);
  2564.     }
  2565.     free(fontname);
  2566.     fputs("}\n",rtf);
  2567.     if(colors>1)
  2568.     {
  2569.         fputs("{\\colortbl;",rtf);
  2570.         for(i=1;i<colors;i++) fprintf(rtf,"\\red%d\\green%d\\blue%d;",color[i].r,color[i].g,color[i].b);
  2571.         fputs("}\n",rtf);
  2572.     }
  2573.     fprintf(rtf,"{\\stylesheet{\\fs%d \\snext0 Normal;}\n",font[0].HalfPoints);
  2574.     if(mvbstyle)
  2575.     {
  2576.         for(i=0;i<FontHdr.NumFormats;i++)
  2577.         {
  2578.         MVBSTYLE *m,*n;
  2579.  
  2580.         m=mvbstyle+i;
  2581.         fprintf(rtf,"{\\*\\cs%u \\additive",m->StyleNum+9);
  2582.         if(m->BasedOn)
  2583.         {
  2584.             n=mvbstyle+(m->BasedOn-1);
  2585.             if(m->font.FontName!=n->font.FontName) fprintf(rtf,"\\f%d",m->font.FontName);
  2586.             if(m->font.expndtw!=n->font.expndtw) fprintf(rtf,"\\expndtw%d",m->font.expndtw);
  2587.             if(m->font.FGRGB[0]!=n->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
  2588.             if(m->font.BGRGB[0]!=n->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
  2589.             if(m->font.Height!=n->font.Height) fprintf(rtf,"\\fs%d",-2L*m->font.Height);
  2590.             if((m->font.Weight>500)!=(n->font.Weight>500)) fprintf(rtf,"\\b%d",m->font.Weight>500);
  2591.             if(m->font.Italic!=n->font.Italic) fprintf(rtf,"\\i%d",m->font.Italic);
  2592.             if(m->font.Underline!=n->font.Underline) fprintf(rtf,"\\ul%d",m->font.Underline);
  2593.             if(m->font.StrikeOut!=n->font.StrikeOut) fprintf(rtf,"\\strike%d",m->font.StrikeOut);
  2594.             if(m->font.DoubleUnderline!=n->font.DoubleUnderline) fprintf(rtf,"\\uldb%d",m->font.DoubleUnderline);
  2595.             if(m->font.SmallCaps!=n->font.SmallCaps) fprintf(rtf,"\\scaps%d",m->font.SmallCaps);
  2596.             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);
  2597.             fprintf(rtf," \\sbasedon%u",m->BasedOn+9);
  2598.         }
  2599.         else
  2600.         {
  2601.             fprintf(rtf,"\\f%d",m->font.FontName);
  2602.             if(m->font.Italic) fputs("\\i",rtf);
  2603.             if(m->font.Weight>500) fputs("\\b",rtf);
  2604.             if(m->font.Underline) fputs("\\ul",rtf);
  2605.             if(m->font.StrikeOut) fputs("\\strike",rtf);
  2606.             if(m->font.DoubleUnderline) fputs("\\uldb",rtf);
  2607.             if(m->font.SmallCaps) fputs("\\scaps",rtf);
  2608.             if(m->font.expndtw) fprintf(rtf,"\\expndtw%d",m->font.expndtw);
  2609.             if(m->font.up>0) fprintf(rtf,"\\up%d",m->font.up);
  2610.             else if(m->font.up<0) fprintf(rtf,"\\dn%d",-m->font.up);
  2611.             fprintf(rtf,"\\fs%d",-2*m->font.Height);
  2612.             if(m->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
  2613.             if(m->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
  2614.         }
  2615.         fprintf(rtf," %s;}\n",m->StyleName);
  2616.         }
  2617.         free(mvbstyle);
  2618.     }
  2619.     else if(newstyle)
  2620.     {
  2621.         for(i=0;i<FontHdr.NumFormats;i++)
  2622.         {
  2623.         NEWSTYLE *m,*n;
  2624.  
  2625.         m=newstyle+i;
  2626.         fprintf(rtf,"{\\*\\cs%u \\additive",m->StyleNum+9);
  2627.         if(m->BasedOn)
  2628.         {
  2629.             n=newstyle+(m->BasedOn-1);
  2630.             if(m->font.FontName!=n->font.FontName) fprintf(rtf,"\\f%d",m->font.FontName);
  2631.             if(m->font.FGRGB[0]!=n->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
  2632.             if(m->font.BGRGB[0]!=n->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
  2633.             if(m->font.Height!=n->font.Height) fprintf(rtf,"\\fs%d",-2L*m->font.Height);
  2634.             if((m->font.Weight>500)!=(n->font.Weight>500)) fprintf(rtf,"\\b%d",m->font.Weight>500);
  2635.             if(m->font.Italic!=n->font.Italic) fprintf(rtf,"\\i%d",m->font.Italic);
  2636.             if(m->font.Underline!=n->font.Underline) fprintf(rtf,"\\ul%d",m->font.Underline);
  2637.             if(m->font.StrikeOut!=n->font.StrikeOut) fprintf(rtf,"\\strike%d",m->font.StrikeOut);
  2638.             if(m->font.DoubleUnderline!=n->font.DoubleUnderline) fprintf(rtf,"\\uldb%d",m->font.DoubleUnderline);
  2639.             if(m->font.SmallCaps!=n->font.SmallCaps) fprintf(rtf,"\\scaps%d",m->font.SmallCaps);
  2640.             fprintf(rtf," \\sbasedon%u",m->BasedOn+9);
  2641.         }
  2642.         else
  2643.         {
  2644.             fprintf(rtf,"\\f%d",m->font.FontName);
  2645.             if(m->font.Italic) fputs("\\i",rtf);
  2646.             if(m->font.Weight>500) fputs("\\b",rtf);
  2647.             if(m->font.Underline) fputs("\\ul",rtf);
  2648.             if(m->font.StrikeOut) fputs("\\strike",rtf);
  2649.             if(m->font.DoubleUnderline) fputs("\\uldb",rtf);
  2650.             if(m->font.SmallCaps) fputs("\\scaps",rtf);
  2651.             fprintf(rtf,"\\fs%d",-2*m->font.Height);
  2652.             if(m->font.FGRGB[0]) fprintf(rtf,"\\cf%d",m->font.FGRGB[0]);
  2653.             if(m->font.BGRGB[0]) fprintf(rtf,"\\cb%d",m->font.BGRGB[0]);
  2654.         }
  2655.         fprintf(rtf," %s;}\n",m->StyleName);
  2656.         }
  2657.         free(newstyle);
  2658.     }
  2659.     if(family) free(family);
  2660.     fputs("}\\pard\\plain\n",rtf);
  2661.     memset(&CurrentFont,0,sizeof(CurrentFont));
  2662.     CurrentFont.FontName=DefFont;
  2663.     if(hpj)
  2664.     {
  2665.         fprintf(stderr,"%u font names, %u font descriptors",fontnames,FontHdr.NumDescriptors);
  2666.         if(FontHdr.FacenamesOffset>=12) printf(", %u font styles",FontHdr.NumFormats);
  2667.         fputs(" loaded\n",stderr);
  2668.     }
  2669.     }
  2670. }
  2671.  
  2672. /* read NumBytes from |TOPIC starting at TopicPos (or if TopicPos is 0
  2673. // where last left off) into dest, returning number of bytes read.
  2674. // TopicRead handles LZ77 decompression and the crossing of topic blocks */
  2675. long TopicRead(FILE *HelpFile,long TopicPos,void *dest,long NumBytes)
  2676. {
  2677.     static TOPICBLOCKHEADER TopicBlockHeader;
  2678.     static unsigned char TopicBuffer[0x4000];
  2679.     static long TopicFileStart;
  2680.     static long TopicBlockNum;
  2681.     unsigned int TopicBlockOffset;
  2682.     static unsigned int DecompSize;
  2683.     static long LastTopicPos;
  2684.     unsigned int n;
  2685.  
  2686.     if(!TopicFileStart) /* first call: HelpFile is at start of |TOPIC */
  2687.     {
  2688.     TopicFileStart=ftell(HelpFile);
  2689.     TopicBlockNum=-1L;
  2690.     }
  2691.     if(!TopicPos) TopicPos=LastTopicPos; /* continue where left off */
  2692.     if((TopicPos-sizeof(TOPICBLOCKHEADER))/DecompressSize!=TopicBlockNum) /* other topic block */
  2693.     {
  2694.     TopicBlockNum=(TopicPos-sizeof(TOPICBLOCKHEADER))/DecompressSize;
  2695.     if(TopicBlockNum*TopicBlockSize>=TopicFileLength) return 0;
  2696.     fseek(HelpFile,TopicFileStart+TopicBlockNum*TopicBlockSize,SEEK_SET);
  2697.     n=TopicBlockSize;
  2698.     if(n+TopicBlockNum*TopicBlockSize>TopicFileLength)
  2699.     {
  2700.         n=(unsigned int)(TopicFileLength-TopicBlockNum*TopicBlockSize);
  2701.     }
  2702.     my_fread(&TopicBlockHeader,sizeof(TOPICBLOCKHEADER),HelpFile);
  2703.     n-=sizeof(TOPICBLOCKHEADER);
  2704.     if(lzcompressed)
  2705.     {
  2706.         DecompSize=DecompressIntoBuffer(2,HelpFile,n,TopicBuffer,sizeof(TopicBuffer));
  2707.     }
  2708.     else
  2709.     {
  2710.         DecompSize=my_fread(TopicBuffer,n,HelpFile);
  2711.     }
  2712.     }
  2713.     TopicBlockOffset=(TopicPos-sizeof(TOPICBLOCKHEADER))%DecompressSize;
  2714.     if(TopicBlockOffset+NumBytes>DecompSize) /* more than available in this block */
  2715.     {
  2716.     n=DecompSize-TopicBlockOffset;
  2717.     if(n) memcpy(dest,TopicBuffer+TopicBlockOffset,n);
  2718.     return n+TopicRead(HelpFile,(TopicBlockNum+1)*DecompressSize+sizeof(TOPICBLOCKHEADER),(char *)dest+n,NumBytes-n);
  2719.     }
  2720.     if(NumBytes) memcpy(dest,TopicBuffer+TopicBlockOffset,NumBytes);
  2721.     LastTopicPos=TopicPos+NumBytes;
  2722.     return NumBytes;
  2723. }
  2724.  
  2725. /* Hall or oldstyle Phrase replacement of str into out */
  2726. char *PhraseReplace(unsigned char *str,long len,char *out)
  2727. {
  2728.     int CurChar;
  2729.  
  2730.     if(Hall)
  2731.     {
  2732.     while(len)
  2733.     {
  2734.         CurChar=*str++;
  2735.         len--;
  2736.         if((CurChar&1)==0) /* phrases 0..127 */
  2737.         {
  2738.         out=PrintPhrase(CurChar/2,out,NULL);
  2739.         }
  2740.         else if((CurChar&3)==1) /* phrases 128..16511 */
  2741.         {
  2742.         CurChar=128+(CurChar/4)*256+*str++;
  2743.         len--;
  2744.         out=PrintPhrase(CurChar,out,NULL);
  2745.         }
  2746.         else if((CurChar&7)==3) /* copy next n characters */
  2747.         {
  2748.         while(CurChar>0)
  2749.         {
  2750.             *out++=*str++;
  2751.             len--;
  2752.             CurChar-=8;
  2753.         }
  2754.         }
  2755.         else if((CurChar&0x0F)==0x07)
  2756.         {
  2757.         while(CurChar>0)
  2758.         {
  2759.             *out++=' ';
  2760.             CurChar-=16;
  2761.         }
  2762.         }
  2763.         else /* if((CurChar&0x0F)==0x0F) */
  2764.         {
  2765.         while(CurChar>0)
  2766.         {
  2767.             *out++='\0';
  2768.             CurChar-=16;
  2769.         }
  2770.         }
  2771.     }
  2772.     }
  2773.     else
  2774.     {
  2775.     while(len)
  2776.     {
  2777.         CurChar=*str++;
  2778.         len--;
  2779.         if(CurChar>0&&CurChar<16) /* phrase 0..1919 */
  2780.         {
  2781.         CurChar=256*(CurChar-1)+*str++;
  2782.         len--;
  2783.         out=PrintPhrase(CurChar/2,out,NULL);
  2784.         if(CurChar&1) *out++=' ';
  2785.         }
  2786.         else
  2787.         {
  2788.         *out++=CurChar;
  2789.         }
  2790.     }
  2791.     }
  2792.     return out;
  2793. }
  2794.  
  2795. /* reads next chunk from |TOPIC like TopicRead, but does phrase decompression
  2796. // if Length > NumBytes, suitable to read LinkData2. If phrase decompression
  2797. // doesn't expands to Length bytes, buffer is padded using 0. TopicPhraseRead
  2798. // always NUL-terminates at dest[Length] just to be save */
  2799. long TopicPhraseRead(FILE *HelpFile,long TopicPos,char *dest,long NumBytes,long Length)
  2800. {
  2801.     char *buffer;
  2802.     long BytesRead;
  2803.  
  2804.     if(Length<=NumBytes) /* no phrase compression in this case */
  2805.     {
  2806.     BytesRead=TopicRead(HelpFile,TopicPos,dest,Length);
  2807.     if(BytesRead==Length&&Length<NumBytes) /* some trailing bytes are not used (bug in HCRTF ?) */
  2808.     {
  2809.         buffer=my_malloc(NumBytes-Length);
  2810.         BytesRead+=TopicRead(HelpFile,0L,buffer,NumBytes-Length);
  2811.         free(buffer);
  2812.     }
  2813.     }
  2814.     else
  2815.     {
  2816.     buffer=my_malloc(NumBytes);
  2817.     BytesRead=TopicRead(HelpFile,TopicPos,buffer,NumBytes);
  2818.     NumBytes=PhraseReplace(buffer,NumBytes,dest)-dest;
  2819.     free(buffer);
  2820.     if(NumBytes>Length)
  2821.     {
  2822.         error("Phrase replacement delivers %ld bytes instead of %ld",NumBytes,Length);
  2823.         exit(1);
  2824.     }
  2825.     }
  2826.     while(NumBytes<=Length) dest[NumBytes++]='\0';
  2827.     return BytesRead;
  2828. }
  2829.  
  2830. void Annotate(long pos,FILE *rtf)
  2831. {
  2832.     long FileLength;
  2833.     char FileName[19];
  2834.     int i;
  2835.     long l;
  2836.  
  2837.     sprintf(FileName,"%ld!0",pos);
  2838.     if(SearchFile(AnnoFile,FileName,&FileLength))
  2839.     {
  2840.     fputs("{\\v {\\*\\atnid ANN}\\chatn {\\*\\annotation \\pard\\plain {\\chatn }",rtf);
  2841.     for(l=0;l<FileLength&&(i=getc(AnnoFile))!=-1;l++)
  2842.     {
  2843.         if(i==0x0D)
  2844.         {
  2845.         fputs("\\par\n",rtf);
  2846.         }
  2847.         else if(i!='{'&&i!='}'&&i!='\\'&&isprint(i))
  2848.         {
  2849.         putc(i,rtf);
  2850.         }
  2851.         else if(i=='{')
  2852.         {
  2853.         fputs("\\{\\-",rtf);
  2854.         }
  2855.         else if(i!='\0'&&i!=0x0A)
  2856.         {
  2857.         fprintf(rtf,"\\'%02x",i);
  2858.         }
  2859.     }
  2860.     fputs("}}",rtf);
  2861.     }
  2862. }
  2863.  
  2864. /* collect all keywords assigned to positions starting at NextKeywordOffset
  2865. // from all keyword lists, saving the first MAXKEYWORDS in KeywordRec
  2866. // (which is allocated to MAXKEYWORDS if NULL) for use in ListKeywords.
  2867. // updates NextKeywordOffset, clears NextKeywordRec, sets KeywordRecs. */
  2868. void CollectKeywords(FILE *HelpFile)
  2869. {
  2870.     unsigned short j,m;
  2871.     int i,n,k,l,map;
  2872.     long FileLength,savepos,KWDataOffset,from;
  2873.     long *keytopic;
  2874.     BUFFER buf;
  2875.     char kwdata[10];
  2876.     char kwbtree[10];
  2877.  
  2878.     fputs("Collecting keywords...",stderr);
  2879.     savepos=ftell(HelpFile);
  2880.     if(KeywordRec) /* free old keywords */
  2881.     {
  2882.     for(i=0;i<KeywordRecs;i++)
  2883.     {
  2884.         if(KeywordRec[i].Keyword) free(KeywordRec[i].Keyword);
  2885.     }
  2886.     }
  2887.     else
  2888.     {
  2889.     KeywordRec=my_malloc(MAXKEYWORDS*sizeof(KEYWORDREC));
  2890.     }
  2891.     NextKeywordRec=KeywordRecs=0;
  2892.     from=NextKeywordOffset;
  2893.     NextKeywordOffset=0x7FFFFFFFL;
  2894.     for(k=0;k<2;k++) for(map='0';map<='z';map++)
  2895.     {
  2896.     if(k)
  2897.     {
  2898.         if(!keyindex[map-'0']) continue;
  2899.         sprintf(kwdata,"|%cKWDATA",map);
  2900.         sprintf(kwbtree,"|%cKWBTREE",map);
  2901.     }
  2902.     else
  2903.     {
  2904.         if(!lists[map-'0']) continue;
  2905.         sprintf(kwdata,"|%cWDATA",map);
  2906.         sprintf(kwbtree,"|%cWBTREE",map);
  2907.     }
  2908.     if(SearchFile(HelpFile,kwdata,&FileLength))
  2909.     {
  2910.         keytopic=my_malloc(FileLength);
  2911.         my_fread(keytopic,FileLength,HelpFile);
  2912.         if(SearchFile(HelpFile,kwbtree,NULL))
  2913.         {
  2914.         for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
  2915.         {
  2916.             for(i=0;i<n;i++)
  2917.             {
  2918.             my_gets(keyword,sizeof(keyword),HelpFile);
  2919.             m=my_getw(HelpFile);
  2920.             KWDataOffset=getdw(HelpFile);
  2921.             for(j=0;j<m;j++)
  2922.             {
  2923.                 if(keytopic[KWDataOffset/4+j]>=from)
  2924.                 {
  2925.                 if(KeywordRecs>=MAXKEYWORDS)
  2926.                 {
  2927.                     NextKeywordOffset=KeywordRec[KeywordRecs-1].TopicOffset;
  2928.                     while(KeywordRec[KeywordRecs-1].TopicOffset==NextKeywordOffset)
  2929.                     {
  2930.                     KeywordRecs--;
  2931.                     if(KeywordRec[KeywordRecs].Keyword) free(KeywordRec[KeywordRecs].Keyword);
  2932.                     }
  2933.                 }
  2934.                 l=KeywordRecs;
  2935.                 while(l>0&&KeywordRec[l-1].TopicOffset>keytopic[KWDataOffset/4+j])
  2936.                 {
  2937.                     KeywordRec[l].KeyIndex=KeywordRec[l-1].KeyIndex;
  2938.                     KeywordRec[l].Footnote=KeywordRec[l-1].Footnote;
  2939.                     KeywordRec[l].Keyword=KeywordRec[l-1].Keyword;
  2940.                     KeywordRec[l].TopicOffset=KeywordRec[l-1].TopicOffset;
  2941.                     l--;
  2942.                 }
  2943.                 KeywordRec[l].KeyIndex=k>0;
  2944.                 KeywordRec[l].Footnote=map;
  2945.                 KeywordRec[l].Keyword=my_strdup(keyword);
  2946.                 KeywordRec[l].TopicOffset=keytopic[KWDataOffset/4+j];
  2947.                 KeywordRecs++;
  2948.                 }
  2949.             }
  2950.             }
  2951.         }
  2952.         free(keytopic);
  2953.         }
  2954.     }
  2955.     }
  2956.     fseek(HelpFile,savepos,SEEK_SET);
  2957.     for(i=0;i<22;i++) fputs("\b \b",stderr);
  2958. }
  2959.  
  2960. /* writes out all keywords appearing up to position TopicOffset and eats
  2961. // them up so they are not written out again. Merges keywords if possible */
  2962. void ListKeywords(FILE *HelpFile,FILE *rtf,long TopicOffset)
  2963. {
  2964.     int len,footnote,keyindex;
  2965.  
  2966.     if(NextKeywordRec>=KeywordRecs)
  2967.     {
  2968.     if(NextKeywordOffset==0x7FFFFFFFL) return;
  2969.     CollectKeywords(HelpFile);
  2970.     }
  2971.     footnote=keyindex=len=0;
  2972.     while(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset<=TopicOffset)
  2973.     {
  2974.     if(len>0&&(KeywordRec[NextKeywordRec].Footnote!=footnote||KeywordRec[NextKeywordRec].KeyIndex!=keyindex||len+strlen(KeywordRec[NextKeywordRec].Keyword)>(after31?1023:254)))
  2975.     {
  2976.         fputs("}\n",rtf);
  2977.         len=0;
  2978.     }
  2979.     if(len>0)
  2980.     {
  2981.         putc(';',rtf);
  2982.     }
  2983.     else if(KeywordRec[NextKeywordRec].KeyIndex)
  2984.     {
  2985.         fprintf(rtf,"{\\up K}{\\footnote\\pard\\plain{\\up K} %c:",KeywordRec[NextKeywordRec].Footnote);
  2986.     }
  2987.     else
  2988.     {
  2989.         fprintf(rtf,"{\\up %c}{\\footnote\\pard\\plain{\\up %c} ",KeywordRec[NextKeywordRec].Footnote,KeywordRec[NextKeywordRec].Footnote);
  2990.     }
  2991.     len+=strlen(KeywordRec[NextKeywordRec].Keyword)+1;
  2992.     putrtf(rtf,KeywordRec[NextKeywordRec].Keyword);
  2993.     footnote=KeywordRec[NextKeywordRec].Footnote;
  2994.     keyindex=KeywordRec[NextKeywordRec].KeyIndex;
  2995.     NextKeywordRec++;
  2996.     }
  2997.     if(len) fputs("}\n",rtf);
  2998. }
  2999.  
  3000. /* create > footnote if topic at TopicOffset has a window assigned to
  3001. // using the |VIOLA internal file. Read VIOLA sequentially, reloading
  3002. // next page only if neccessary, because it is properly ordered. */
  3003. int ListWindows(FILE *HelpFile,long TopicOffset)
  3004. {
  3005.     long savepos;
  3006.     static int n,i;
  3007.     static BUFFER buf;
  3008.     static int VIOLAfound=-1;
  3009.     static VIOLAREC *Viola;
  3010.     int result;
  3011.  
  3012.     if(VIOLAfound==0) return -1;
  3013.     savepos=ftell(HelpFile);
  3014.     if(VIOLAfound==-1)
  3015.     {
  3016.     VIOLAfound=0;
  3017.     if(SearchFile(HelpFile,"|VIOLA",NULL))
  3018.     {
  3019.         n=GetFirstPage(HelpFile,&buf,NULL);
  3020.         if(n)
  3021.         {
  3022.         Viola=my_malloc(n*sizeof(VIOLAREC));
  3023.         my_fread(Viola,n*sizeof(VIOLAREC),HelpFile);
  3024.         i=0;
  3025.         VIOLAfound=1;
  3026.         }
  3027.     }
  3028.     }
  3029.     result=-1;
  3030.     if(VIOLAfound==1)
  3031.     {
  3032.     while(i>=n||TopicOffset>Viola[i].TopicOffset)
  3033.     {
  3034.         if(i>=n)
  3035.         {
  3036.         free(Viola);
  3037.         n=GetNextPage(HelpFile,&buf);
  3038.         if(n==0)
  3039.         {
  3040.             VIOLAfound=0;
  3041.             break;
  3042.         }
  3043.         Viola=my_malloc(n*sizeof(VIOLAREC));
  3044.         my_fread(Viola,n*sizeof(VIOLAREC),HelpFile);
  3045.         i=0;
  3046.         }
  3047.         else
  3048.         {
  3049.         i++;
  3050.         }
  3051.     }
  3052.     if(i<n&&Viola[i].TopicOffset==TopicOffset)
  3053.     {
  3054.         result=Viola[i].WindowNumber;
  3055.     }
  3056.     }
  3057.     fseek(HelpFile,savepos,SEEK_SET);
  3058.     return result;
  3059. }
  3060.  
  3061. /* Browse sequence handling support functions. As an efficient means to
  3062. // resolve browse sequences, FirstPass numbers and saves all browse
  3063. // sequence start positions (that are topics where BrowseNext and
  3064. // BrowsePrev both point to some topic behind) and links them with the
  3065. // following topics. Whenever a topic is merged to the beginning of a
  3066. // browse sequence, it's start topics browse subnumber is incremented.
  3067. // The interesting part is a topic where BrowsePrev and BrowseNext both
  3068. // point to earlier topics. Two different browse sequences need to be
  3069. // merged at this point, that is will get the same browse start number
  3070. // and one start topics subnumber needs to accommodate the other browse
  3071. // sequence. Using the start records, TopicDump does know which browse
  3072. // sequence starts at which topic and knows the browse sequence number
  3073. // and subnumber assigned. */
  3074. void AddStart(long StartTopic,int BrowseNum,int Count)
  3075. {
  3076.     start=my_realloc(start,(starts+1)*sizeof(START));
  3077.     start[starts].StartTopic=StartTopic;
  3078.     start[starts].BrowseNum=BrowseNum;
  3079.     start[starts].Start=Count;
  3080.     starts++;
  3081. }
  3082.  
  3083. void FixStart(int BrowseNum,int NewBrowseNum,int AddCount)
  3084. {
  3085.     int i;
  3086.  
  3087.     for(i=0;i<starts;i++) if(start[i].BrowseNum==BrowseNum)
  3088.     {
  3089.     start[i].BrowseNum=NewBrowseNum;
  3090.     start[i].Start+=AddCount;
  3091.     }
  3092. }
  3093.  
  3094. void AddBrowse(long StartTopic,long NextTopic,long PrevTopic)
  3095. {
  3096.     int i;
  3097.  
  3098.     for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break; /* empty space in array ? */
  3099.     if(i==browses) /* no empty space, add to array */
  3100.     {
  3101.     browse=my_realloc(browse,++browses*sizeof(BROWSE));
  3102.     }
  3103.     browse[i].StartTopic=StartTopic;
  3104.     browse[i].NextTopic=NextTopic;
  3105.     browse[i].PrevTopic=PrevTopic;
  3106.     browse[i].BrowseNum=browsenums++;
  3107.     browse[i].Start=1;
  3108.     browse[i].Count=1;
  3109. }
  3110.  
  3111. void MergeBrowse(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
  3112. {
  3113.     int i,j;
  3114.  
  3115.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3116.     {
  3117.     if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
  3118.     }
  3119.     for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L)
  3120.     {
  3121.     if(browse[j].PrevTopic==TopicOffset||browse[j].PrevTopic==OtherTopicOffset) break;
  3122.     }
  3123.     if(i<browses&&j<browses)
  3124.     {
  3125.     browse[i].Count++;
  3126.     browse[i].NextTopic=browse[j].NextTopic;
  3127.     FixStart(browse[j].BrowseNum,browse[i].BrowseNum,browse[i].Count);
  3128.     browse[j].Start+=browse[i].Count;
  3129.     AddStart(browse[j].StartTopic,browse[i].BrowseNum,browse[j].Start);
  3130.     browse[i].Count+=browse[j].Count;
  3131.     browse[j].StartTopic=-1L;
  3132.     if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3133.     {
  3134.         AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
  3135.         browse[i].StartTopic=-1L;
  3136.     }
  3137.     }
  3138.     else
  3139.     {
  3140.     warnings=TRUE;
  3141.     fprintf(stderr,"Can not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3142.     }
  3143. }
  3144.  
  3145. void LinkBrowse(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
  3146. {
  3147.     int i;
  3148.  
  3149.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3150.     {
  3151.     if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
  3152.     }
  3153.     if(i<browses)
  3154.     {
  3155.     browse[i].NextTopic=NextTopic;
  3156.     browse[i].Count++;
  3157.     if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3158.     {
  3159.         AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
  3160.         browse[i].StartTopic=-1L;
  3161.     }
  3162.     }
  3163.     else
  3164.     {
  3165.     warnings=TRUE;
  3166.     fprintf(stderr,"Can not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3167.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3168.     {
  3169.         fprintf(stderr,"Open browse %08lx %08lx\n",browse[i].PrevTopic,browse[i].NextTopic);
  3170.     }
  3171.     }
  3172. }
  3173.  
  3174. void BackLinkBrowse(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
  3175. {
  3176.     int i;
  3177.  
  3178.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3179.     {
  3180.     if(browse[i].PrevTopic==TopicOffset||browse[i].PrevTopic==OtherTopicOffset) break;
  3181.     }
  3182.     if(i<browses)
  3183.     {
  3184.     browse[i].PrevTopic=PrevTopic;
  3185.     browse[i].Count++;
  3186.     browse[i].Start++;
  3187.     FixStart(browse[i].BrowseNum,browse[i].BrowseNum,1);
  3188.     if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3189.     {
  3190.         AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
  3191.         browse[i].StartTopic=-1L;
  3192.     }
  3193.     }
  3194.     else
  3195.     {
  3196.     warnings=TRUE;
  3197.     fprintf(stderr,"Can not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3198.     }
  3199. }
  3200.  
  3201. unsigned long AddLink(long StartTopic,long NextTopic,long PrevTopic)
  3202. {
  3203.     int i,j;
  3204.     unsigned long result;
  3205.  
  3206.     result=0L;
  3207.     for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break;
  3208.     if(i==browses) browse=my_realloc(browse,++browses*sizeof(BROWSE));
  3209.     for(j=0;j<starts;j++) if(start[j].StartTopic==StartTopic) break;
  3210.     if(j<starts)
  3211.     {
  3212.     browse[i].StartTopic=start[j].StartTopic;
  3213.     browse[i].BrowseNum=start[j].BrowseNum;
  3214.     browse[i].Start=start[j].Start;
  3215.     browse[i].Count=start[j].Start;
  3216.     browse[i].NextTopic=NextTopic;
  3217.     browse[i].PrevTopic=PrevTopic;
  3218.     result=browse[i].BrowseNum+((long)browse[i].Start<<16);
  3219.     }
  3220.     else
  3221.     {
  3222.     warnings=TRUE;
  3223.     fprintf(stderr,"Browse start %08lx not found\n",StartTopic);
  3224.     }
  3225.     return result;
  3226. }
  3227.  
  3228. unsigned long MergeLink(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
  3229. {
  3230.     int i,j;
  3231.     unsigned long result;
  3232.  
  3233.     result=0L;
  3234.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3235.     {
  3236.     if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
  3237.     }
  3238.     for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L)
  3239.     {
  3240.     if(browse[j].PrevTopic==TopicOffset||browse[j].PrevTopic==OtherTopicOffset) break;
  3241.     }
  3242.     if(i<browses&&j<browses)
  3243.     {
  3244.     browse[i].Count++;
  3245.     browse[j].Start--;
  3246.     if(browse[i].Count!=browse[j].Start)
  3247.     {
  3248.         warnings=TRUE;
  3249.         fprintf(stderr,"Prev browse end %d doen't match next browse start %d\n",browse[i].Count,browse[j].Start);
  3250.     }
  3251.     result=browse[i].BrowseNum+((long)browse[i].Count<<16);
  3252.     browse[i].NextTopic=browse[j].NextTopic;
  3253.     browse[i].Count=browse[j].Count;
  3254.     browse[j].StartTopic=-1L;
  3255.     }
  3256.     else
  3257.     {
  3258.     warnings=TRUE;
  3259.     fprintf(stderr,"Can not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3260.     }
  3261.     return result;
  3262. }
  3263.  
  3264. unsigned long LinkLink(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
  3265. {
  3266.     int i;
  3267.     unsigned long result;
  3268.  
  3269.     result=0L;
  3270.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3271.     {
  3272.     if(browse[i].NextTopic==TopicOffset||browse[i].NextTopic==OtherTopicOffset) break;
  3273.     }
  3274.     if(i<browses)
  3275.     {
  3276.     browse[i].NextTopic=NextTopic;
  3277.     browse[i].Count++;
  3278.     result=browse[i].BrowseNum+((long)browse[i].Count<<16);
  3279.     if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3280.     {
  3281.         browse[i].StartTopic=-1L;
  3282.     }
  3283.     }
  3284.     else
  3285.     {
  3286.     warnings=TRUE;
  3287.     fprintf(stderr,"Can not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3288.     }
  3289.     return result;
  3290. }
  3291.  
  3292. unsigned long BackLinkLink(long TopicOffset,long OtherTopicOffset,long NextTopic,long PrevTopic)
  3293. {
  3294.     int i;
  3295.     unsigned long result;
  3296.  
  3297.     result=0L;
  3298.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3299.     {
  3300.     if(browse[i].PrevTopic==TopicOffset||browse[i].PrevTopic==OtherTopicOffset) break;
  3301.     }
  3302.     if(i<browses)
  3303.     {
  3304.     browse[i].PrevTopic=PrevTopic;
  3305.     browse[i].Start--;
  3306.     result=browse[i].BrowseNum+((long)browse[i].Start<<16);
  3307.     if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3308.     {
  3309.         browse[i].StartTopic=-1L;
  3310.     }
  3311.     }
  3312.     else
  3313.     {
  3314.     warnings=TRUE;
  3315.     fprintf(stderr,"Can not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3316.     }
  3317.     return result;
  3318. }
  3319.  
  3320. /* create numbered rtf file names, no numbering if i=0 */
  3321. void BuildName(char *buffer,int i)
  3322. {
  3323.     char num[7];
  3324.  
  3325.     strcpy(buffer,name);
  3326.     if(i)
  3327.     {
  3328.     itoa(i,num,10);
  3329.     if(strlen(buffer)+strlen(num)>8)
  3330.     {
  3331.         buffer[8-strlen(num)]='\0';
  3332.     }
  3333.     strcat(buffer,num);
  3334.     }
  3335.     strcat(buffer,".RTF");
  3336. }
  3337.  
  3338. /* emit rtf commands to change to font i.
  3339. // ul forces underline on, uldb forces doubleunderline on */
  3340. void ChangeFont(FILE *rtf,unsigned int i,BOOL ul,BOOL uldb)
  3341. {
  3342.     FONTDESCRIPTOR *f;
  3343.     long pos;
  3344.  
  3345.     if(i<fonts)
  3346.     {
  3347.     pos=ftell(rtf);
  3348.     f=font+i;
  3349.     if(f->style)
  3350.     {
  3351.         fprintf(rtf,"\\plain\\cs%d",f->style+9);
  3352.         if(uldb) fputs("\\uldb",rtf); else if(ul) fputs("\\ul",rtf);
  3353.     }
  3354.     else
  3355.     {
  3356.         /* HC30 can't reset, so reset using \plain */
  3357.         if(CurrentFont.Bold&&!f->Bold
  3358.         || CurrentFont.Italic&&!f->Italic
  3359.         || CurrentFont.Underline&&!(!uldb&&(ul||f->Underline))
  3360.         || CurrentFont.StrikeOut&&!f->StrikeOut
  3361.         || CurrentFont.DoubleUnderline&&!(uldb||f->DoubleUnderline)
  3362.         || CurrentFont.SmallCaps&&!f->SmallCaps
  3363.         || CurrentFont.FontName&&!f->FontName
  3364.         || CurrentFont.textcolor&&!f->textcolor
  3365.         || CurrentFont.backcolor&&!f->backcolor
  3366.         || CurrentFont.up&&!f->up
  3367.         || CurrentFont.style&&!f->style)
  3368.         {
  3369.         fputs("\\plain",rtf);
  3370.         memset(&CurrentFont,0,sizeof(CurrentFont));
  3371.         CurrentFont.FontName=DefFont;
  3372.         }
  3373.         if(f->FontName!=CurrentFont.FontName) fprintf(rtf,"\\f%d",f->FontName);
  3374.         if(f->Italic&&!CurrentFont.Italic) fputs("\\i",rtf);
  3375.         if(f->Bold&&!CurrentFont.Bold) fputs("\\b",rtf);
  3376.         if(!uldb&&(ul||f->Underline)&&!CurrentFont.Bold) fputs("\\ul",rtf);
  3377.         if(f->StrikeOut&&!CurrentFont.StrikeOut) fputs("\\strike",rtf);
  3378.         if((uldb||f->DoubleUnderline)&&!CurrentFont.DoubleUnderline) fputs("\\uldb",rtf);
  3379.         if(f->SmallCaps&&!CurrentFont.SmallCaps) fputs("\\scaps",rtf);
  3380.         if(f->expndtw!=CurrentFont.expndtw) fprintf(rtf,"\\expndtw%d",f->expndtw);
  3381.         if(f->up!=CurrentFont.up)
  3382.         {
  3383.         if(f->up>0) fprintf(rtf,"\\up%d",f->up);
  3384.         else if(f->up<0) fprintf(rtf,"\\dn%d",-f->up);
  3385.         }
  3386.         if(f->HalfPoints!=CurrentFont.HalfPoints) fprintf(rtf,"\\fs%d",f->HalfPoints);
  3387.         if(f->textcolor!=CurrentFont.textcolor) fprintf(rtf,"\\cf%d",f->textcolor);
  3388.         if(f->backcolor!=CurrentFont.backcolor) fprintf(rtf,"\\cb%d",f->backcolor);
  3389.     }
  3390.     memcpy(&CurrentFont,f,sizeof(CurrentFont));
  3391.     if(ul) CurrentFont.Underline=1;
  3392.     if(uldb)
  3393.     {
  3394.         CurrentFont.Underline=0;
  3395.         CurrentFont.DoubleUnderline=1;
  3396.     }
  3397.     if(ftell(rtf)!=pos) putc(' ',rtf);
  3398.     }
  3399. }
  3400.  
  3401. /* list all groups the topic TopicNum is assigned to and/or emit footnote
  3402. // for browse sequence of this topic as + footnote into rtf file */
  3403. void ListGroups(FILE *rtf,long TopicNum,unsigned long BrowseNum)
  3404. {
  3405.     int i;
  3406.     BOOL grouplisted;
  3407.  
  3408.     grouplisted=FALSE;
  3409.     for(i=0;i<groups;i++) if(group[i].GroupHeader.GroupType==1||group[i].GroupHeader.GroupType==2)
  3410.     {
  3411.     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)))))
  3412.     {
  3413.         if(!grouplisted)
  3414.         {
  3415.         fputs("{\\up +}{\\footnote\\pard\\plain{\\up +} ",rtf);
  3416.         if(BrowseNum) fprintf(rtf,"BROWSE%04x:%04x",(unsigned short)BrowseNum,(unsigned short)(BrowseNum>>16));
  3417.         grouplisted=TRUE;
  3418.         }
  3419.         fprintf(rtf,";%s",group[i].Name);
  3420.     }
  3421.     }
  3422.     if(grouplisted)
  3423.     {
  3424.     fputs("}\n",rtf);
  3425.     }
  3426.     else if(BrowseNum)
  3427.     {
  3428.     fprintf(rtf,"{\\up +}{\\footnote\\pard\\plain{\\up +} BROWSE%04x:%04x}\n",(unsigned short)BrowseNum,(unsigned short)(BrowseNum>>16));
  3429.     }
  3430. }
  3431.  
  3432. /* advances TopicOffset to next block in |TOPIC if setting of TopicPos to
  3433. // NextBlock crosses TOPICBLOCKHEADER */
  3434. TOPICOFFSET NextTopicOffset(TOPICOFFSET TopicOffset,TOPICPOS NextBlock,TOPICPOS TopicPos)
  3435. {
  3436.     /* it should never be neccessary to subtract sizeof(TOPICBLOCKHEADER), as no
  3437.     // TOPICLINK may start in the last (12..21) bytes, but just to make shure... */
  3438.     if((NextBlock-sizeof(TOPICBLOCKHEADER))/DecompressSize!=(TopicPos-sizeof(TOPICBLOCKHEADER))/DecompressSize)
  3439.     {
  3440.     return ((NextBlock-sizeof(TOPICBLOCKHEADER))/DecompressSize)*0x8000L;
  3441.     }
  3442.     return TopicOffset;
  3443. }
  3444.  
  3445. /* TopicDump: converts the internal |TOPIC file to RTF format suitable for
  3446. // recompilation inserting footnotes with information from other internal
  3447. // files as required */
  3448. FILE *TopicDump(FILE *HelpFile,FILE *rtf,FILE *hpj,BOOL makertf)
  3449. {
  3450.     TOPICLINK TopicLink;
  3451.     char *LinkData1;  /* Data associated with this link */
  3452.     long nonscroll=-1L;
  3453.     char *LinkData2;  /* Second set of data */
  3454.     int fontset,i;
  3455.     int NextContextRec;
  3456.     unsigned long BrowseNum;
  3457.     char *hotspot;
  3458.     char *arg;
  3459.     BOOL firsttopic=TRUE;
  3460.     BOOL ul,uldb;
  3461.     int nextbitmap,TopicInRTF,NumberOfRTF;
  3462.     long TopicNum,TopicOffset,TopicPos;
  3463.     int col,cols,lastcol;
  3464.     short *iptr;
  3465.     unsigned short x1,x2,x3;
  3466.     short y1;
  3467.     long l1;
  3468.     char *ptr;
  3469.     char *cmd;
  3470.     char *str;
  3471.     long ActualTopicOffset,MaxTopicOffset;
  3472.     TOPICHEADER30 *TopicHdr30;
  3473.     TOPICHEADER *TopicHdr;
  3474.     long BogusTopicOffset;
  3475.  
  3476.     if(SearchFile(HelpFile,"|TOPIC",&TopicFileLength))
  3477.     {
  3478.     fontset=-1;
  3479.     nextbitmap=1;
  3480.     if(browse) free(browse);
  3481.     browse=NULL;
  3482.     browses=0;
  3483.     NextContextRec=0;
  3484.     ul=uldb=FALSE;
  3485.     hotspot=NULL;
  3486.     TopicOffset=0L;
  3487.     TopicPos=12L;
  3488.     TopicNum=16;
  3489.     TopicInRTF=0;
  3490.     NumberOfRTF=1;
  3491.     while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
  3492.     {
  3493.         if(before31)
  3494.         {
  3495.         if(TopicPos+TopicLink.NextBlock>=TopicFileLength) break;
  3496.         }
  3497.         else
  3498.         {
  3499.         if(TopicLink.NextBlock<=0) break;
  3500.         }
  3501.         if(TopicLink.DataLen1>sizeof(TOPICLINK))
  3502.         {
  3503.         LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
  3504.         if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
  3505.         }
  3506.         else LinkData1=NULL;
  3507.         if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
  3508.         {
  3509.         LinkData2=my_malloc(TopicLink.DataLen2+1);
  3510.         if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
  3511.         }
  3512.         else LinkData2=NULL;
  3513.         if(LinkData1&&TopicLink.RecordType==TL_TOPICHDR) /* display a Topic Header record */
  3514.         {
  3515.         if(TopicsPerRTF&&++TopicInRTF>=TopicsPerRTF)
  3516.         {
  3517.             putc('}',rtf);
  3518.             my_fclose(rtf);
  3519.             BuildName(buffer,++NumberOfRTF);
  3520.             if(hpj) fprintf(hpj,"%s\n",buffer);
  3521.             rtf=my_fopen(buffer,"wt");
  3522.             FontLoad(HelpFile,rtf,NULL);
  3523.             TopicInRTF=0;
  3524.         }
  3525.         else if(!firsttopic)
  3526.         {
  3527.              if(makertf&&nopagebreak)
  3528.              {
  3529.              fputs("\\par\n",rtf);
  3530.              }
  3531.              else
  3532.              {
  3533.              fputs("\\page\n",rtf);
  3534.              }
  3535.         }
  3536.         firsttopic=FALSE;
  3537.         fprintf(stderr,"\rTopic %ld...",TopicNum-15);
  3538.         if(!makertf)
  3539.         {
  3540.             BrowseNum=0L;
  3541.             if(before31)
  3542.             {
  3543.             TopicHdr30=(TOPICHEADER30 *)LinkData1;
  3544.             fprintf(rtf,"{\\up #}{\\footnote\\pard\\plain{\\up #} TOPIC%ld}\n",TopicNum);
  3545.             if(resolvebrowse)
  3546.             {
  3547.                 if(TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum>TopicNum
  3548.                 || TopicHdr30->NextTopicNum==-1&&TopicHdr30->PrevTopicNum>TopicNum
  3549.                 || TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum==-1)
  3550.                 {
  3551.                 BrowseNum=AddLink(TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  3552.                 }
  3553.                 else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum)
  3554.                 {
  3555.                 BrowseNum=MergeLink(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  3556.                 }
  3557.                 else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&(TopicHdr30->PrevTopicNum==-1||TopicHdr30->PrevTopicNum>TopicNum))
  3558.                 {
  3559.                 BrowseNum=BackLinkLink(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  3560.                 }
  3561.                 else if(TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum&&(TopicHdr30->NextTopicNum==-1||TopicHdr30->NextTopicNum>TopicNum))
  3562.                 {
  3563.                 BrowseNum=LinkLink(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  3564.                 }
  3565.             }
  3566.             ListKeywords(HelpFile,rtf,TopicPos);
  3567.             }
  3568.             else
  3569.             {
  3570.             BogusTopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
  3571.             TopicHdr=(TOPICHEADER *)LinkData1;
  3572.             if(TopicHdr->Scroll!=-1L)
  3573.             {
  3574.                 nonscroll=TopicHdr->Scroll;
  3575.             }
  3576.             else
  3577.             {
  3578.                 nonscroll=TopicHdr->NextTopic;
  3579.             }
  3580.             if(resolvebrowse)
  3581.             {
  3582.                 if(TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck>TopicOffset
  3583.                 || TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>TopicOffset
  3584.                 || TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck==-1L)
  3585.                 {
  3586.                 BrowseNum=AddLink(TopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  3587.                 }
  3588.                 else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset)
  3589.                 {
  3590.                 BrowseNum=MergeLink(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  3591.                 }
  3592.                 else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>TopicOffset))
  3593.                 {
  3594.                 BrowseNum=BackLinkLink(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  3595.                 }
  3596.                 else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>TopicOffset))
  3597.                 {
  3598.                 BrowseNum=LinkLink(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  3599.                 }
  3600.             }
  3601.             }
  3602.             ListGroups(rtf,TopicNum-16,BrowseNum);
  3603.             if(LinkData2&&TopicLink.DataLen2>0)
  3604.             {
  3605.             if(*LinkData2)
  3606.             {
  3607.                 fputs("{\\up $}{\\footnote\\pard\\plain{\\up $} ",rtf);
  3608.                 putrtf(rtf,LinkData2);
  3609.                 fputs("}\n",rtf);
  3610.             }
  3611.             for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=strlen(LinkData2+i)+1)
  3612.             {
  3613.                 fputs("{\\up !}{\\footnote\\pard\\plain{\\up !} ",rtf);
  3614.                 if(!after31&&strlen(LinkData2+i)>254)
  3615.                 {
  3616.                 printf("Help compiler will issue Warning 3511: Macro '%s' exceeds limit of 254 characters\n",LinkData2+i);
  3617.                 }
  3618.                 putrtf(rtf,LinkData2+i);
  3619.                 fputs("}\n",rtf);
  3620.             }
  3621.             }
  3622.             while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<=TopicOffset)
  3623.             {
  3624.             fputs("{\\up #}{\\footnote\\pard\\plain{\\up #} ",rtf);
  3625.                         putrtf(rtf,unhash(ContextRec[NextContextRec].HashValue));
  3626.                         fputs("}\n",rtf);
  3627.             if(!mvp) while(NextContextRec+1<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
  3628.             {
  3629.                 NextContextRec++;
  3630.             }
  3631.             NextContextRec++;
  3632.             }
  3633.             i=ListWindows(HelpFile,TopicOffset);
  3634.             if(i!=-1) fprintf(rtf,"{\\up >}{\\footnote\\pard\\plain{\\up >} %s}\n",GetWindowName(i));
  3635.         }
  3636.         TopicNum++;
  3637.         }
  3638.         else if(LinkData1&&LinkData2&&TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  3639.         {
  3640.         if(AnnoFile) Annotate(TopicPos,rtf);
  3641.         ptr=LinkData1;
  3642.         scanlong(&ptr);
  3643.         if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  3644.         {
  3645.             x1=scanword(&ptr);
  3646.             ActualTopicOffset=TopicOffset;
  3647.             MaxTopicOffset=ActualTopicOffset+x1;
  3648.             TopicOffset+=x1;
  3649.         }
  3650.         if(TopicLink.RecordType==TL_TABLE)
  3651.         {
  3652.             fputs("\\trowd",rtf);
  3653.             cols=(unsigned char)*ptr++;
  3654.             x1=(unsigned char)*ptr++;
  3655.             switch(x1)
  3656.             {
  3657.             case 0:
  3658.             case 2:
  3659.             l1=*(short *)ptr; /* min table width */
  3660.             ptr+=2;
  3661.             fputs("\\trqc",rtf);
  3662.             break;
  3663.             case 1:
  3664.             case 3:
  3665.             l1=32767L;
  3666.             break;
  3667.             }
  3668.             iptr=(short *)ptr;
  3669.             if(cols>1)
  3670.             {
  3671.             x1=iptr[0]+iptr[1]+iptr[3]/2;
  3672.             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);
  3673.             x1+=iptr[2]+iptr[3];
  3674.             for(col=2;col<cols;col++)
  3675.             {
  3676.                 x1+=iptr[2*col]+iptr[2*col+1];
  3677.                 fprintf(rtf,"\\cellx%ld",((x1*scaling-rounderr)*l1)/32767);
  3678.             }
  3679.             }
  3680.             else
  3681.             {
  3682.             fprintf(rtf,"\\trleft%ld \\cellx%ld ",((iptr[1]*scaling-rounderr)*l1-32767)/32767,((iptr[0]*scaling-rounderr)*l1)/32767);
  3683.             }
  3684.             ptr=(char *)(iptr+2*cols);
  3685.         }
  3686.         lastcol=-1;
  3687.         str=LinkData2;
  3688.         for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
  3689.         {
  3690.             fputs("\\pard",rtf);
  3691.             if(TopicPos<nonscroll) fputs("\\keepn",rtf);
  3692.             if(TopicLink.RecordType==TL_TABLE)
  3693.             {
  3694.             fputs("\\intbl",rtf);
  3695.             lastcol=*(short *)ptr;
  3696.             ptr+=5;
  3697.             }
  3698.             ptr+=4;
  3699.             x2=*(unsigned short *)ptr;
  3700.             ptr+=2;
  3701.             if(x2&0x1000) fputs("\\keep",rtf);
  3702.             if(x2&0x0400) fputs("\\qr",rtf);
  3703.             if(x2&0x0800) fputs("\\qc",rtf);
  3704.             if(x2&0x0001) scanlong(&ptr);
  3705.             if(x2&0x0002) fprintf(rtf,"\\sb%ld",scanint(&ptr)*scaling-rounderr);
  3706.             if(x2&0x0004) fprintf(rtf,"\\sa%ld",scanint(&ptr)*scaling-rounderr);
  3707.             if(x2&0x0008) fprintf(rtf,"\\sl%ld",scanint(&ptr)*scaling-rounderr);
  3708.             if(x2&0x0010) fprintf(rtf,"\\li%ld",scanint(&ptr)*scaling-rounderr);
  3709.             if(x2&0x0020) fprintf(rtf,"\\ri%ld",scanint(&ptr)*scaling-rounderr);
  3710.             if(x2&0x0040) fprintf(rtf,"\\fi%ld",scanint(&ptr)*scaling-rounderr);
  3711.             if(x2&0x0100)
  3712.             {
  3713.             x1=(unsigned char)*ptr++;
  3714.             if(x1&1) fputs("\\box",rtf);
  3715.             if(x1&2) fputs("\\brdrt",rtf);
  3716.             if(x1&4) fputs("\\brdrl",rtf);
  3717.             if(x1&8) fputs("\\brdrb",rtf);
  3718.             if(x1&0x10) fputs("\\brdrr",rtf);
  3719.             if(x1&0x20) fputs("\\brdrth",rtf); else fputs("\\brdrs",rtf);
  3720.             if(x1&0x40) fputs("\\brdrdb",rtf);
  3721.             ptr+=2;
  3722.             }
  3723.             if(x2&0x0200)
  3724.             {
  3725.             y1=scanint(&ptr);
  3726.             while(y1-->0)
  3727.             {
  3728.                 x1=scanword(&ptr);
  3729.                 if(x1&0x4000)
  3730.                 {
  3731.                 switch(scanword(&ptr))
  3732.                 {
  3733.                 case 1:
  3734.                     fputs("\\tqr",rtf);
  3735.                     break;
  3736.                 case 2:
  3737.                     fputs("\\tqc",rtf);
  3738.                     break;
  3739.                 }
  3740.                 }
  3741.                 fprintf(rtf,"\\tx%d",(x1&0x3FFF)*scaling-rounderr);
  3742.             }
  3743.             }
  3744.             putc(' ',rtf);
  3745.             while(1) /* ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK)&&str<end) */
  3746.             {
  3747.             if(*str&&fontset>=0&&fontset<fonts&&font&&font[fontset].SmallCaps) strlwr(str);
  3748.             do
  3749.             {
  3750.                 if(!makertf)
  3751.                 {
  3752.                 while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<=ActualTopicOffset&&ContextRec[NextContextRec].TopicOffset<MaxTopicOffset)
  3753.                 {
  3754.                     fputs("{\\up #}{\\footnote\\pard\\plain{\\up #} ",rtf);
  3755.                                     putrtf(rtf,unhash(ContextRec[NextContextRec].HashValue));
  3756.                                     fputs("}\n",rtf);
  3757.                     if(!mvp) while(NextContextRec+1<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
  3758.                     {
  3759.                     NextContextRec++;
  3760.                     }
  3761.                     NextContextRec++;
  3762.                 }
  3763.                 if(!before31) ListKeywords(HelpFile,rtf,ActualTopicOffset<MaxTopicOffset?ActualTopicOffset:MaxTopicOffset-1);
  3764.                 }
  3765.                 if(*str)
  3766.                 {
  3767.                 if(*str!='{'&&*str!='}'&&*str!='\\'&&isprint((unsigned char)*str))
  3768.                 {
  3769.                     putc(*str,rtf);
  3770.                 }
  3771.                 else if(!makertf&&*str=='{')
  3772.                 {
  3773.                     fputs("\\{\\-",rtf); /* emit invisible dash after { brace */
  3774.                     /* because bmc or another legal command may follow, but this */
  3775.                     /* command was not parsed the help file was build, so it was */
  3776.                     /* used just as an example. The dash will be eaten up by the */
  3777.                     /* help compiler on recompile. */
  3778.                 }
  3779.                 else
  3780.                 {
  3781.                     fprintf(rtf,"\\'%02x",(unsigned char)*str);
  3782.                 }
  3783.                 }
  3784.                 if(ActualTopicOffset<MaxTopicOffset) ActualTopicOffset++;
  3785.             }
  3786.             while(*str++);
  3787.             if((unsigned char)ptr[0]==0xFF)
  3788.             {
  3789.                 ptr++;
  3790.                 break;
  3791.             }
  3792.             else switch((unsigned char)ptr[0])
  3793.             {
  3794.             case 0x20: /* vfld MVB */
  3795.                 if(*(long *)(ptr+1))
  3796.                 {
  3797.                 fprintf(rtf,"\\{vfld%ld\\}",*(long *)(ptr+1));
  3798.                 }
  3799.                 else
  3800.                 {
  3801.                 fputs("\\{vfld\\}",rtf);
  3802.                 }
  3803.                 ptr+=5;
  3804.                 break;
  3805.             case 0x21: /* dtype MVB */
  3806.                 if(*(short *)(ptr+1))
  3807.                 {
  3808.                 fprintf(rtf,"\\{dtype%d\\}",*(short *)(ptr+1));
  3809.                 }
  3810.                 else
  3811.                 {
  3812.                 fputs("\\{dtype\\}",rtf);
  3813.                 }
  3814.                 ptr+=3;
  3815.                 break;
  3816.             case 0x80: /* font change */
  3817.                 ChangeFont(rtf,fontset=*(short *)(ptr+1),ul,uldb);
  3818.                 ptr+=3;
  3819.                 break;
  3820.             case 0x81:
  3821.                 fputs("\\line\n",rtf);
  3822.                 ptr++;
  3823.                 break;
  3824.             case 0x82:
  3825.                 if(TopicLink.RecordType==TL_TABLE)
  3826.                 {
  3827.                 if((unsigned char)ptr[1]!=0xFF)
  3828.                 {
  3829.                     fputs("\n\\par\\intbl ",rtf);
  3830.                 }
  3831.                 else if(*(short *)(ptr+2)==-1)
  3832.                 {
  3833.                     fputs("\\cell\\intbl\\row\n",rtf);
  3834.                 }
  3835.                 else if(*(short *)(ptr+2)==lastcol)
  3836.                 {
  3837.                     fputs("\\par\\pard ",rtf);
  3838.                 }
  3839.                 else
  3840.                 {
  3841.                     fputs("\\cell\\pard ",rtf);
  3842.                 }
  3843.                 }
  3844.                 else
  3845.                 {
  3846.                 fputs("\n\\par ",rtf);
  3847.                 }
  3848.                 ptr++;
  3849.                 break;
  3850.             case 0x83:
  3851.                 fputs("\\tab ",rtf);
  3852.                 ptr++;
  3853.                 break;
  3854.             case 0x86:
  3855.                 x3=(unsigned char)*ptr++;
  3856.                 x1=*ptr++;
  3857.                 if(x1==0x05) cmd="ewc"; else cmd="bmc";
  3858.                 goto picture;
  3859.             case 0x87:
  3860.                 x3=(unsigned char)*ptr++;
  3861.                 x1=*ptr++;
  3862.                 if(x1==0x05) cmd="ewl"; else cmd="bml";
  3863.                 goto picture;
  3864.             case 0x88:
  3865.                 x3=(unsigned char)*ptr++;
  3866.                 x1=*ptr++;
  3867.                 if(x1==0x05) cmd="ewr"; else cmd="bmr";
  3868.                 goto picture;
  3869.             picture:
  3870.                 l1=scanlong(&ptr);
  3871.                 switch(x1)
  3872.                 {
  3873.                 case 0x22: /* HC31 */
  3874.                 ActualTopicOffset+=scanword(&ptr); /* number of hotspots in picture */
  3875.                 if(ActualTopicOffset>MaxTopicOffset) ActualTopicOffset=MaxTopicOffset;
  3876.                 /* fall thru */
  3877.                 case 0x03: /* HC30 */
  3878.                 x1=((unsigned short *)ptr)[0];
  3879.                 switch(x1)
  3880.                 {
  3881.                 case 1:
  3882.                     while(nextbitmap<extensions&&extension[nextbitmap]<0x10) nextbitmap++;
  3883.                     if(nextbitmap>=extensions)
  3884.                     {
  3885.                     error("Bitmap never saved");
  3886.                     break;
  3887.                     }
  3888.                     x2=nextbitmap++;
  3889.                     goto other;
  3890.                 case 0:
  3891.                     x2=((unsigned short *)ptr)[1];
  3892.                 other:
  3893.                     if(makertf)
  3894.                     {
  3895.                     switch(x3)
  3896.                     {
  3897.                     case 0x86:
  3898.                         fprintf(rtf,"{\\field {\\*\\fldinst import %s}}",getbitmapname(x2));
  3899.                         break;
  3900.                     case 0x87:
  3901.                         fprintf(rtf,"{\\pvpara {\\field {\\*\\fldinst import %s}}\\par}\n",getbitmapname(x2));
  3902.                         break;
  3903.                     case 0x88:
  3904.                         fprintf(rtf,"{\\pvpara\\posxr{\\field {\\*\\fldinst import %s}}\\par}\n",getbitmapname(x2));
  3905.                         break;
  3906.                     }
  3907.                     }
  3908.                     else
  3909.                     {
  3910.                     if(x2<extensions&&(extension[x2]&0x20))
  3911.                     {
  3912.                         if(strcmp(cmd,"bmc")==0) cmd="bmct";
  3913.                         else if(strcmp(cmd,"bml")==0) cmd="bmlt";
  3914.                         else if(strcmp(cmd,"bmr")==0) cmd="bmrt";
  3915.                     }
  3916.                     fprintf(rtf,"\\{%s %s\\}",cmd,getbitmapname(x2));
  3917.                     }
  3918.                     break;
  3919.                 }
  3920.                 break;
  3921.                 case 0x05: /* ewc,ewl,ewr */
  3922.                 if(ptr[6]=='!')
  3923.                 {
  3924.                     fprintf(rtf,"\\{button %s\\}",ptr+7);
  3925.                 }
  3926.                 else if(ptr[6]=='*')
  3927.                 {
  3928.                     char *plus;
  3929.                     int n,c1,c2;
  3930.  
  3931.                     sscanf(ptr+7,"%d,%d,%n",&c1,&c2,&n);
  3932.                     plus=strchr(ptr+7+n,'+');
  3933.                     if((c1&0xFFF5)!=0x8400) fprintf(stderr,"mci c1=%04x\n",c1);
  3934.                     fputs("\\{mci",rtf);
  3935.                     if(cmd[2]=='r') fputs("_right",rtf);
  3936.                     if(cmd[2]=='l') fputs("_left",rtf);
  3937.                     if(c2==1) fputs(" REPEAT",rtf);
  3938.                     if(c2==2) fputs(" PLAY",rtf);
  3939.                     if(!plus) fputs(" EXTERNAL",rtf);
  3940.                     if(c1&8) fputs(" NOMENU",rtf);
  3941.                     if(c1&2) fputs(" NOPLAYBAR",rtf);
  3942.                     fprintf(rtf,",%s\\}\n",plus?plus+1:ptr+7+n);
  3943.                 }
  3944.                 else
  3945.                 {
  3946.                     fprintf(rtf,"\\{%s %s\\}",cmd,ptr+6);
  3947.                 }
  3948.                 break;
  3949.                 }
  3950.                 ptr+=l1;
  3951.                 break;
  3952.             case 0x89: /* end of hotspot */
  3953.                 if(!makertf)
  3954.                 {
  3955.                 if(hotspot[0]=='%'&&fontset>=0&&fontset<fonts&&font[fontset].Underline)
  3956.                 {
  3957.                     hotspot[0]='*';
  3958.                 }
  3959.                 }
  3960.                 ChangeFont(rtf,fontset,ul=FALSE,uldb=FALSE);
  3961.                 if(!makertf)
  3962.                 {
  3963.                 if(!after31&&strlen(hotspot)>255)
  3964.                 {
  3965.                     puts("Help compiler will issue Warning 4072: Context string exceeds limit of 255 characters");
  3966.                 }
  3967.                 fputs("{\\v ",rtf);
  3968.                                 putrtf(rtf,multi&&(hotspot[0]=='%'||hotspot[0]=='*')?hotspot+1:hotspot);
  3969.                                 fputc('}',rtf);
  3970.                 }
  3971.                 ptr++;
  3972.                 break;
  3973.             case 0xC8: /* macro */
  3974.                 ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  3975.                 if(!makertf)
  3976.                 {
  3977.                 hotspot=my_realloc(hotspot,strlen(ptr+3)+2);
  3978.                 sprintf(hotspot,"!%s",ptr+3);
  3979.                 }
  3980.                 ptr+=*(short *)(ptr+1)+3;
  3981.                 break;
  3982.             case 0xCC: /* macro without font change */
  3983.                 ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  3984.                 if(!makertf)
  3985.                 {
  3986.                 hotspot=my_realloc(hotspot,strlen(ptr+3)+3);
  3987.                 sprintf(hotspot,"%%!%s",ptr+3);
  3988.                 }
  3989.                 ptr+=*(short *)(ptr+1)+3;
  3990.                 break;
  3991.             case 0xE0: /* popup jump HC30 */
  3992.                 ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  3993.                 goto label0;
  3994.             case 0xE1: /* topic jump HC30 */
  3995.                 ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  3996.             label0:
  3997.                 if(!makertf)
  3998.                 {
  3999.                 hotspot=my_realloc(hotspot,128);
  4000.                 sprintf(hotspot,"TOPIC%ld",*(long *)(ptr+1));
  4001.                 }
  4002.                 ptr+=5;
  4003.                 break;
  4004.             case 0xE2: /* popup jump HC31 */
  4005.                 ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  4006.                 goto label1;
  4007.             case 0xE3: /* topic jump HC31 */
  4008.                 ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4009.             label1:
  4010.                 if(!makertf)
  4011.                 {
  4012.                 arg=ContextId(*(long *)(ptr+1));
  4013.                 hotspot=my_realloc(hotspot,strlen(arg)+1);
  4014.                 sprintf(hotspot,"%s",arg);
  4015.                 }
  4016.                 ptr+=5;
  4017.                 break;
  4018.             case 0xE6: /* popup jump without font change */
  4019.                 ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  4020.                 goto label2;
  4021.             case 0xE7: /* topic jump without font change */
  4022.                 ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4023.             label2:
  4024.                 if(!makertf)
  4025.                 {
  4026.                 arg=ContextId(*(long *)(ptr+1));
  4027.                 hotspot=my_realloc(hotspot,strlen(arg)+2);
  4028.                 sprintf(hotspot,"%%%s",arg);
  4029.                 }
  4030.                 ptr+=5;
  4031.                 break;
  4032.             case 0xEA: /* popup jump into external file */
  4033.             case 0xEE:
  4034.                 ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  4035.                 goto label3;
  4036.             case 0xEB: /* topic jump into external file / secondary window */
  4037.             case 0xEF:
  4038.                 ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4039.             label3:
  4040.                 if(!makertf)
  4041.                 {
  4042.                 if((unsigned char)ptr[0]==0xEE||(unsigned char)ptr[0]==0xEF)
  4043.                 {
  4044.                     cmd="%";
  4045.                 }
  4046.                 else
  4047.                 {
  4048.                     cmd="";
  4049.                 }
  4050.                 arg=unhash(*(long *)(ptr+4)); // no ContextId, it may jump into external file
  4051.                 switch((unsigned char)ptr[3])
  4052.                 {
  4053.                 case 0:
  4054.                     hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1);
  4055.                     sprintf(hotspot,"%s%s",cmd,arg);
  4056.                     break;
  4057.                 case 1:
  4058.                     hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(GetWindowName(ptr[8]))+1);
  4059.                     sprintf(hotspot,"%s%s>%s",cmd,arg,GetWindowName(ptr[8]));
  4060.                     break;
  4061.                 case 4:
  4062.                     hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1);
  4063.                     sprintf(hotspot,"%s%s@%s",cmd,arg,ptr+8);
  4064.                     break;
  4065.                 case 6:
  4066.                     hotspot=my_realloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1+strlen(strchr(ptr+8,'\0')+1)+1);
  4067.                     sprintf(hotspot,"%s%s>%s@%s",cmd,arg,ptr+8,strchr(ptr+8,'\0')+1);
  4068.                     break;
  4069.                 }
  4070.                 }
  4071.                 ptr+=*(short *)(ptr+1)+3;
  4072.                 break;
  4073.             case 0x8B:
  4074.                 fputs("\\~",rtf);
  4075.                 ptr++;
  4076.                 break;
  4077.             case 0x8C:
  4078.                 fputs("\\-",rtf);
  4079.                 ptr++;
  4080.                 break;
  4081.             default:
  4082.                 ptr++;
  4083.             }
  4084.             }
  4085.         }
  4086.         }
  4087.         if(LinkData1) free(LinkData1);
  4088.         if(LinkData2) free(LinkData2);
  4089.         if(before31)
  4090.         {
  4091.         TopicPos+=TopicLink.NextBlock;
  4092.         }
  4093.         else
  4094.         {
  4095.         TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
  4096.         TopicPos=TopicLink.NextBlock;
  4097.         }
  4098.     }
  4099.     }
  4100.     return rtf;
  4101. }
  4102.  
  4103. int _cdecl ContextRecCmp(const void *a,const void *b)
  4104. {
  4105.     if(((const CONTEXTREC *)a)->TopicOffset<((const CONTEXTREC *)b)->TopicOffset) return -1;
  4106.     if(((const CONTEXTREC *)a)->TopicOffset>((const CONTEXTREC *)b)->TopicOffset) return 1;
  4107.     return 0;
  4108. }
  4109.  
  4110. void ContextLoad(FILE *HelpFile)
  4111. {
  4112.     BUFFER buf;
  4113.     int n;
  4114.     long entries;
  4115.  
  4116.     if(SearchFile(HelpFile,"|CONTEXT",NULL))
  4117.     {
  4118.     n=GetFirstPage(HelpFile,&buf,&entries);
  4119.     if(entries)
  4120.     {
  4121.         ContextRec=my_malloc(entries*sizeof(CONTEXTREC));
  4122.         ContextRecs=0;
  4123.         while(n)
  4124.         {
  4125.         my_fread(ContextRec+ContextRecs,n*sizeof(CONTEXTREC),HelpFile);
  4126.         ContextRecs+=n;
  4127.         n=GetNextPage(HelpFile,&buf);
  4128.         }
  4129.         fprintf(stderr,"%d topic offsets and hash values loaded\n",ContextRecs);
  4130.         qsort(ContextRec,ContextRecs,sizeof(CONTEXTREC),ContextRecCmp);
  4131.     }
  4132.     }
  4133.     else if(SearchFile(HelpFile,"|TOMAP",&entries))
  4134.     {
  4135.     Topic=my_malloc(entries);
  4136.     my_fread(Topic,entries,HelpFile);
  4137.     Topics=(int)(entries/sizeof(long));
  4138.     }
  4139. }
  4140.  
  4141. void GenerateContent(FILE *HelpFile,FILE *ContentFile) /* create a simple Win95 contents file */
  4142. {
  4143.     VIOLAREC *WindowRec;
  4144.     long FileLength,offset;
  4145.     int n,i,j,WindowRecs;
  4146.     BUFFER buf;
  4147.     char *ptr;
  4148.  
  4149.     fprintf(ContentFile,":Base %s%s>main\n",name,ext);
  4150.     if(HelpFileTitle[0]) fprintf(ContentFile,":Title %s\n",HelpFileTitle);
  4151.     WindowRecs=0;
  4152.     if(SearchFile(HelpFile,"|VIOLA",NULL))
  4153.     {
  4154.     n=GetFirstPage(HelpFile,&buf,&FileLength);
  4155.     if(FileLength)
  4156.     {
  4157.         WindowRec=my_malloc(FileLength*sizeof(VIOLAREC));
  4158.         while(n)
  4159.         {
  4160.         my_fread(WindowRec+WindowRecs,n*sizeof(VIOLAREC),HelpFile);
  4161.         WindowRecs+=n;
  4162.         n=GetNextPage(HelpFile,&buf);
  4163.         }
  4164.     }
  4165.     }
  4166.     if(SearchFile(HelpFile,"|TTLBTREE",NULL))
  4167.     {
  4168.     for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
  4169.     {
  4170.         for(i=0;i<n;i++)
  4171.         {
  4172.         offset=getdw(HelpFile);
  4173.         if(my_gets(buffer,sizeof(buffer),HelpFile))
  4174.         {
  4175.             ptr=TopicName(offset);
  4176.             if(ptr)
  4177.             {
  4178.             fprintf(ContentFile,"1 %s=%s",buffer,ptr);
  4179.             for(j=0;j<WindowRecs;j++)
  4180.             {
  4181.                 if(WindowRec[j].TopicOffset==offset)
  4182.                 {
  4183.                 fprintf(ContentFile,">%s",GetWindowName(WindowRec[j].WindowNumber));
  4184.                 break;
  4185.                 }
  4186.             }
  4187.             putc('\n',ContentFile);
  4188.             }
  4189.         }
  4190.         }
  4191.     }
  4192.     }
  4193. }
  4194.  
  4195. void ListRose(FILE *HelpFile,FILE *hpj)
  4196. {
  4197.     long FileLength,offset,hash,h,pos,savepos;
  4198.     unsigned char *ptr;
  4199.     long *keytopic;
  4200.     int n,i,l,e;
  4201.     unsigned short j,count;
  4202.     BUFFER buf,buf2;
  4203.  
  4204.     if(SearchFile(HelpFile,"|Rose",NULL))
  4205.     {
  4206.     savepos=ftell(HelpFile);
  4207.     if(SearchFile(HelpFile,"|KWDATA",&FileLength))
  4208.     {
  4209.         keytopic=my_malloc(FileLength);
  4210.         my_fread(keytopic,FileLength,HelpFile);
  4211.         if(SearchFile(HelpFile,"|KWBTREE",NULL))
  4212.         {
  4213.         fputs("[MACROS]\n",hpj);
  4214.         for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
  4215.         {
  4216.             for(i=0;i<n;i++)
  4217.             {
  4218.             my_gets(keyword,sizeof(keyword),HelpFile);
  4219.             for(hash=0L,ptr=(unsigned char *)keyword;*ptr;ptr++)
  4220.             {
  4221.                 hash=hash*43L+table[*ptr];
  4222.             }
  4223.             count=my_getw(HelpFile);
  4224.             offset=getdw(HelpFile);
  4225.             for(j=0;j<count;j++)
  4226.             {
  4227.                 if(keytopic[offset/4+j]==-1L)
  4228.                 {
  4229.                 pos=ftell(HelpFile);
  4230.                 fseek(HelpFile,savepos,SEEK_SET);
  4231.                 for(l=GetFirstPage(HelpFile,&buf2,NULL);l;l=GetNextPage(HelpFile,&buf2))
  4232.                 {
  4233.                     for(e=0;e<l;e++)
  4234.                     {
  4235.                     h=getdw(HelpFile);
  4236.                     my_gets(buffer,sizeof(buffer),HelpFile);
  4237.                     if(h==hash)
  4238.                     {
  4239.                         fprintf(hpj,"%s\n%s\n",keyword,buffer);
  4240.                         my_gets(buffer,sizeof(buffer),HelpFile);
  4241.                         fprintf(hpj,"%s\n",buffer);
  4242.                     }
  4243.                     else
  4244.                     {
  4245.                         my_gets(buffer,sizeof(buffer),HelpFile);
  4246.                     }
  4247.                     }
  4248.                 }
  4249.                 fseek(HelpFile,pos,SEEK_SET);
  4250.                 break;
  4251.                 }
  4252.             }
  4253.             }
  4254.         }
  4255.         putc('\n',hpj);
  4256.         }
  4257.         free(keytopic);
  4258.     }
  4259.     }
  4260. }
  4261.  
  4262. /* dump section: all the dump-routines are used to display internal files
  4263. // of the help file with known format of contents for debugging reasons */
  4264. void PrintNewFont(int i,NEWFONT *newfont)
  4265. {
  4266.     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]);
  4267.     if(newfont->Weight>500) putchar('b');
  4268.     if(newfont->Italic) putchar('i');
  4269.     if(newfont->Underline) putchar('u');
  4270.     if(newfont->StrikeOut) putchar('s');
  4271.     if(newfont->DoubleUnderline) putchar('d');
  4272.     if(newfont->SmallCaps) putchar('c');
  4273.     putchar('\n');
  4274. }
  4275.  
  4276. void PrintMvbFont(int i,MVBFONT *mvbfont)
  4277. {
  4278.     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]);
  4279.     if(mvbfont->Weight>500) putchar('b');
  4280.     if(mvbfont->Italic) putchar('i');
  4281.     if(mvbfont->Underline) putchar('u');
  4282.     if(mvbfont->StrikeOut) putchar('s');
  4283.     if(mvbfont->DoubleUnderline) putchar('d');
  4284.     if(mvbfont->SmallCaps) putchar('c');
  4285.     putchar('\n');
  4286. }
  4287.  
  4288. void FontDump(FILE *HelpFile)
  4289. {
  4290.     FONTHEADER FontHdr;
  4291.     long FileStart;
  4292.     OLDFONT oldfont;
  4293.     NEWFONT newfont;
  4294.     NEWSTYLE newstyle;
  4295.     MVBFONT mvbfont;
  4296.     MVBSTYLE mvbstyle;
  4297.     int i,n;
  4298.  
  4299.     /* Go to the FONT file and get the headers */
  4300.     FileStart=ftell(HelpFile);
  4301.     my_fread(&FontHdr,sizeof(FontHdr),HelpFile);
  4302.     n=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/FontHdr.NumFacenames;
  4303.     fontname=my_malloc(FontHdr.NumFacenames*sizeof(char *));
  4304.     fseek(HelpFile,FileStart+FontHdr.FacenamesOffset,SEEK_SET);
  4305.     for(i=0;i<FontHdr.NumFacenames;i++)
  4306.     {
  4307.     my_fread(buffer,n,HelpFile);
  4308.     buffer[n]='\0';
  4309.     printf("Font name %d: %s\n",i,buffer);
  4310.     fontname[i]=my_strdup(buffer);
  4311.     }
  4312.     puts("Font Facename             Height Family Foregr Backgr Style");
  4313.     fseek(HelpFile,FileStart+FontHdr.DescriptorsOffset,SEEK_SET);
  4314.     if(FontHdr.FacenamesOffset>=16)
  4315.     {
  4316.     for(i=0;i<FontHdr.NumDescriptors;i++)
  4317.     {
  4318.         my_fread(&mvbfont,sizeof(mvbfont),HelpFile);
  4319.         PrintMvbFont(i,&mvbfont);
  4320.     }
  4321.     fseek(HelpFile,FileStart+FontHdr.FormatsOffset,SEEK_SET);
  4322.     for(i=0;i<FontHdr.NumFormats;i++)
  4323.     {
  4324.         my_fread(&mvbstyle,sizeof(mvbstyle),HelpFile);
  4325.         printf("Style %d",mvbstyle.StyleNum);
  4326.         if(mvbstyle.BasedOn) printf(" based on %d",mvbstyle.BasedOn);
  4327.         printf(" named '%s':\n",mvbstyle.StyleName);
  4328.         PrintMvbFont(i,&mvbstyle.font);
  4329.     }
  4330.     }
  4331.     else if(FontHdr.FacenamesOffset>=12)
  4332.     {
  4333.     for(i=0;i<FontHdr.NumDescriptors;i++)
  4334.     {
  4335.         my_fread(&newfont,sizeof(newfont),HelpFile);
  4336.         PrintNewFont(i,&newfont);
  4337.     }
  4338.     fseek(HelpFile,FileStart+FontHdr.FormatsOffset,SEEK_SET);
  4339.     for(i=0;i<FontHdr.NumFormats;i++)
  4340.     {
  4341.         my_fread(&newstyle,sizeof(newstyle),HelpFile);
  4342.         printf("Style %d",newstyle.StyleNum);
  4343.         if(newstyle.BasedOn) printf(" based on %d",newstyle.BasedOn);
  4344.         printf(" named '%s':\n",newstyle.StyleName);
  4345.         PrintNewFont(i,&newstyle.font);
  4346.     }
  4347.     }
  4348.     else
  4349.     {
  4350.     for(i=0;i<FontHdr.NumDescriptors;i++)
  4351.     {
  4352.         my_fread(&oldfont,sizeof(oldfont),HelpFile);
  4353.         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]);
  4354.         if(oldfont.Attributes&FONT_BOLD) putchar('b');
  4355.         if(oldfont.Attributes&FONT_ITAL) putchar('i');
  4356.         if(oldfont.Attributes&FONT_UNDR) putchar('u');
  4357.         if(oldfont.Attributes&FONT_STRK) putchar('s');
  4358.         if(oldfont.Attributes&FONT_DBUN) putchar('d');
  4359.         if(oldfont.Attributes&FONT_SMCP) putchar('c');
  4360.         putchar('\n');
  4361.     }
  4362.     }
  4363. }
  4364.  
  4365. void PhrImageDump(FILE *HelpFile)
  4366. {
  4367.     long FileLength;
  4368.     unsigned int bytes;
  4369.     PHRINDEXHDR PhrIndexHdr;
  4370.     unsigned char *ptr;
  4371.  
  4372.     if(SearchFile(HelpFile,"|PhrIndex",NULL))
  4373.     {
  4374.     my_fread(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile);
  4375.     if(SearchFile(HelpFile,"|PhrImage",&FileLength))
  4376.     {
  4377.         if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize)
  4378.         {
  4379.         HexDump(HelpFile,FileLength,0L);
  4380.         }
  4381.         else
  4382.         {
  4383.         if(FileLength!=PhrIndexHdr.phrimagecompressedsize)
  4384.         {
  4385.             fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileLength);
  4386.         }
  4387.         ptr=my_malloc(PhrIndexHdr.phrimagesize);
  4388.         bytes=DecompressIntoBuffer(2,HelpFile,FileLength,ptr,PhrIndexHdr.phrimagesize);
  4389.         HexDumpMemory(ptr,bytes);
  4390.         free(ptr);
  4391.         }
  4392.     }
  4393.     }
  4394. }
  4395.  
  4396. void BTreeDump(FILE *HelpFile,char text[])
  4397. {
  4398.     int n,i,j;
  4399.     long count;
  4400.     BUFFER buf;
  4401.     char format[10];
  4402.     char *ptr;
  4403.  
  4404.     n=GetFirstPage(HelpFile,&buf,NULL);
  4405.     while(n)
  4406.     {
  4407.     for(i=0;i<n;i++)
  4408.     {
  4409.         for(ptr=text;*ptr;ptr++)
  4410.         {
  4411.         if(*ptr=='%')
  4412.         {
  4413.             j=strcspn(ptr,"hsdiouxX!");
  4414.             memcpy(format,ptr,j+1);
  4415.             format[j+1]='\0';
  4416.             if(format[j]=='!')
  4417.             {
  4418.             count=getdw(HelpFile);
  4419.             while(count>=8)
  4420.             {
  4421.                 printf(" (%ld)",getdw(HelpFile));
  4422.                 printf("%08lx",getdw(HelpFile));
  4423.                 count-=8;
  4424.             }
  4425.             }
  4426.             else if(format[j]=='h')
  4427.             {
  4428.             format[j]='s';
  4429.             printf(format,unhash(getdw(HelpFile)));
  4430.             }
  4431.             else if(format[j]=='s')
  4432.             {
  4433.             my_gets(buffer,sizeof(buffer),HelpFile);
  4434.             printf(format,buffer);
  4435.             }
  4436.             else if(strchr(format,'l'))
  4437.             {
  4438.             printf(format,getdw(HelpFile));
  4439.             }
  4440.             else
  4441.             {
  4442.             printf(format,my_getw(HelpFile));
  4443.             }
  4444.             ptr+=j;
  4445.         }
  4446.         else
  4447.         {
  4448.             putchar(*ptr);
  4449.         }
  4450.         }
  4451.     }
  4452.     n=GetNextPage(HelpFile,&buf);
  4453.     }
  4454. }
  4455.  
  4456. void PhraseDump(void)
  4457. {
  4458.     unsigned int n;
  4459.  
  4460.     for(n=0;n<PhraseCount;n++)
  4461.     {
  4462.     printf("%-5d - ",n);
  4463.     PrintPhrase(n,NULL,NULL);
  4464.     putchar('\n');
  4465.     }
  4466. }
  4467.  
  4468. void SysDump(FILE *HelpFile)
  4469. {
  4470.     SYSTEMHEADER SysHdr;
  4471.     SYSTEMRECORD *SysRec;
  4472.     struct tm *TimeRec;
  4473.     char *ptr;
  4474.  
  4475.     my_fread(&SysHdr,sizeof(SysHdr),HelpFile);
  4476.     if(SysHdr.Minor==15)
  4477.     {
  4478.     ptr="HC30";
  4479.     }
  4480.     else if(SysHdr.Minor==21)
  4481.     {
  4482.     ptr="HC31/HCP";
  4483.     }
  4484.     else if(SysHdr.Minor==27)
  4485.     {
  4486.     ptr="WMVC/MVCC";
  4487.     }
  4488.     else if(SysHdr.Minor==33)
  4489.     {
  4490.     if(mvp)
  4491.     {
  4492.         ptr="MVC";
  4493.     }
  4494.     else
  4495.     {
  4496.         ptr="HCRTF";
  4497.     }
  4498.     }
  4499.     else ptr="Unknown";
  4500.     printf("%s Help Compiler used.\n",ptr);
  4501.     printf("System Flags & Compression Method=0x%04x\n",SysHdr.Flags);
  4502.     if(SysHdr.GenDate)
  4503.     {
  4504.     TimeRec=localtime(&SysHdr.GenDate);
  4505.     printf("Help File Generated: %s",asctime(TimeRec));
  4506.     }
  4507.     if(SysHdr.Minor<16)
  4508.     {
  4509.     my_gets(HelpFileTitle,33,HelpFile);
  4510.     printf("TITLE=%s\n",HelpFileTitle);
  4511.     }
  4512.     else for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  4513.     {
  4514.     switch(SysRec->RecordType)
  4515.     {
  4516.     case 0x0001:
  4517.         printf("TITLE=%s\n",SysRec->Data);
  4518.         break;
  4519.     case 0x0002:
  4520.         printf("COPYRIGHT=%s\n",SysRec->Data);
  4521.         break;
  4522.     case 0x0003:
  4523.         printf("CONTENTS=0x%08lX\n",*(long *)SysRec->Data);
  4524.         break;
  4525.     case 0x0004:
  4526.         printf("[MACRO] %s\n",SysRec->Data);
  4527.         break;
  4528.     case 0x0005:
  4529.         puts("Icon in System record");
  4530.         break;
  4531.     case 0x0006:
  4532.         printf("[WINDOW] ");
  4533.         if(SysRec->DataSize==sizeof(SECWINDOW))
  4534.         {
  4535.         PrintWindow(stdout,(SECWINDOW *)SysRec->Data);
  4536.         }
  4537.         else if(SysRec->DataSize==sizeof(MVBWINDOW))
  4538.         {
  4539.         PrintMVBWindow(stdout,(MVBWINDOW *)SysRec->Data);
  4540.         }
  4541.         else
  4542.         {
  4543.         HexDumpMemory(SysRec->Data,SysRec->DataSize);
  4544.         error("[WINDOW] data size does not match");
  4545.         }
  4546.         break;
  4547.     case 0x0008:
  4548.         printf("CITATION=%s\n",SysRec->Data);
  4549.         break;
  4550.     case 0x0009:
  4551.         if(!mvp) printf("LCID=0x%X 0x%X 0x%X\n",*(short *)(SysRec->Data+8),*(short *)SysRec->Data,*(short *)(SysRec->Data+2));
  4552.         break;
  4553.     case 0x000A:
  4554.         if(!mvp) printf("CNT=%s\n",SysRec->Data);
  4555.         break;
  4556.     case 0x000B:
  4557. //        if(!mvp) printf("CHARSET=%d\n",*(unsigned char *)(SysRec->Data+1));
  4558.         break;
  4559.     case 0x000C:
  4560.         if(mvp)
  4561.         {
  4562.         printf("[FTINDEX] dtype %s\n",SysRec->Data);
  4563.         }
  4564.         else
  4565.         {
  4566.         printf("DEFFONT=%s,%d,%d\n",SysRec->Data+2,*(unsigned char *)SysRec->Data,*(unsigned char *)(SysRec->Data+1));
  4567.         }
  4568.         break;
  4569.     case 0x000D:
  4570.         if(mvp) printf("[GROUPS] %s\n",SysRec->Data);
  4571.         break;
  4572.     case 0x000E:
  4573.         if(mvp)
  4574.         {
  4575.         printf("[KEYINDEX] keyword=%c, \"%s\"\n",SysRec->Data[1],SysRec->Data+30);
  4576.         }
  4577.         else
  4578.         {
  4579.         printf("INDEX_SEPARATORS=\"%s\"\n",SysRec->Data);
  4580.         }
  4581.         break;
  4582.     case 0x0012:
  4583.         if(SysRec->Data[0]) printf("LANGUAGE=%s\n",SysRec->Data);
  4584.         break;
  4585.     case 0x0013:
  4586.         ptr=SysRec->Data+strlen(SysRec->Data)+1;
  4587.         printf("[DLLMAPS] %s=%s,",SysRec->Data,ptr);
  4588.         ptr+=strlen(ptr)+1;
  4589.         printf("%s,",ptr);
  4590.         ptr+=strlen(ptr)+1;
  4591.         printf("%s,",ptr);
  4592.         ptr+=strlen(ptr)+1;
  4593.         printf("%s\n",ptr);
  4594.         break;
  4595.     default:
  4596.         fprintf(stderr,"Unknown record type: 0x%04X\n",SysRec->RecordType);
  4597.         HexDumpMemory(SysRec->Data,SysRec->DataSize);
  4598.     }
  4599.     }
  4600. }
  4601.  
  4602. /* dump the contents of |TOPIC for debugging */
  4603. void DumpTopic(FILE *HelpFile,long TopicPos)
  4604. {
  4605.     TOPICLINK TopicLink;
  4606.     TOPICHEADER30 *TopicHdr30;
  4607.     TOPICHEADER *TopicHdr;
  4608.     char *ptr;
  4609.     char *str;
  4610.     char *cmd;
  4611.     long l;
  4612.     unsigned short x1,x2;
  4613.     unsigned char b;
  4614.     int cols,col;
  4615.     short i;
  4616.     char *LinkData1;
  4617.     char *LinkData2;
  4618.     long TopicNum;
  4619.     long TopicOffset;
  4620.  
  4621.     if(!SearchFile(HelpFile,"|TOPIC",&TopicFileLength)) return;
  4622.     TopicOffset=0L;
  4623.     if(TopicPos<12) TopicPos=12L;
  4624.     TopicNum=16;
  4625.     while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
  4626.     {
  4627.     puts("----------------------------------------------------------------------------");
  4628.     printf("TopicLink Type %02x: BlockSize=%08lx DataLen1=%08lx DataLen2=%08lx\n",TopicLink.RecordType,TopicLink.BlockSize,TopicLink.DataLen1,TopicLink.DataLen2);
  4629.     printf("TopicPos=%08lx TopicOffset=%08lx PrevBlock=%08lx NextBlock=%08lx\n",TopicPos,TopicOffset,TopicLink.PrevBlock,TopicLink.NextBlock);
  4630.     if(TopicLink.DataLen1>sizeof(TOPICLINK))
  4631.     {
  4632.         LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK));
  4633.         if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
  4634.     }
  4635.     else LinkData1=NULL;
  4636.     if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
  4637.     {
  4638.         LinkData2=my_malloc(TopicLink.DataLen2+1);
  4639.         if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
  4640.     }
  4641.     else LinkData2=NULL;
  4642.     if(LinkData1) HexDumpMemory(LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK));
  4643.     if(TopicLink.RecordType==TL_TOPICHDR)
  4644.     {
  4645.         if(before31)
  4646.         {
  4647.         TopicHdr30=(TOPICHEADER30 *)LinkData1;
  4648.         puts("============================================================================");
  4649.         printf("TopicHeader TopicNum=%ld BlockSize=%ld PrevTopicNum=%d NextTopicNum=%d\n",TopicNum,TopicHdr30->BlockSize,TopicHdr30->PrevTopicNum,TopicHdr30->NextTopicNum);
  4650.         TopicNum++;
  4651.         }
  4652.         else
  4653.         {
  4654.         TopicHdr=(TOPICHEADER *)LinkData1;
  4655.         puts("============================================================================");
  4656.         printf("TopicHeader TopicNum=%ld BlockSize=%ld NextTopicOffset=%08lx\n",TopicHdr->TopicNum,TopicHdr->BlockSize,TopicHdr->NextTopic);
  4657.         printf("NonScroll=%08lx Scroll=%08lx BrowseBck=%08lx BrowseFor=%08lx\n",TopicHdr->NonScroll,TopicHdr->Scroll,TopicHdr->BrowseBck,TopicHdr->BrowseFor);
  4658.         }
  4659.     }
  4660.     else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  4661.     {
  4662.         switch(TopicLink.RecordType)
  4663.         {
  4664.         case TL_DISPLAY30:
  4665.         fputs("Text ",stdout);
  4666.         break;
  4667.         case TL_DISPLAY:
  4668.         fputs("Display ",stdout);
  4669.         break;
  4670.         case TL_TABLE:
  4671.         fputs("Table ",stdout);
  4672.         break;
  4673.         }
  4674.         ptr=LinkData1;
  4675.         printf("expandedsize=%ld ",scanlong(&ptr));
  4676.         if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  4677.         {
  4678.         x1=scanword(&ptr);
  4679.         TopicOffset+=x1;
  4680.         printf("topicoffsetincrement=%u ",x1);
  4681.         }
  4682.         if(TopicLink.RecordType==TL_TABLE)
  4683.         {
  4684.         cols=(unsigned char)*ptr++;
  4685.         x1=*ptr++;
  4686.         printf("columns=%d type=%d ",cols,x1);
  4687.         switch(x1)
  4688.         {
  4689.         case 0:
  4690.         case 2:
  4691.             printf("minwidth=%d ",*(short *)ptr);
  4692.             ptr+=2;
  4693.         case 1:
  4694.         case 3:
  4695.             break;
  4696.         default:
  4697.             error("Unknown TableType %d",x1);
  4698.         }
  4699.         for(col=0;col<cols;col++)
  4700.         {
  4701.             printf("width=%d gap=%d ",*(short *)ptr,*(short *)(ptr+2));
  4702.             ptr+=4;
  4703.         }
  4704.         }
  4705.         putchar('\n');
  4706.         str=LinkData2;
  4707.         for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
  4708.         {
  4709.         if(TopicLink.RecordType==TL_TABLE)
  4710.         {
  4711.             printf("column=%d %04x %d ",*(short *)ptr,*(unsigned short *)(ptr+2),*(unsigned char *)(ptr+4)-0x80);
  4712.             ptr+=5;
  4713.         }
  4714.         printf("%02x %d id=%04x ",*(unsigned char *)ptr,*(unsigned char *)(ptr+1)-0x80,*(unsigned short *)(ptr+2));
  4715.         ptr+=4;
  4716.         x2=*((unsigned short *)ptr)++;
  4717.         if(x2&0x0001) printf("unknownbit01=%ld ",scanlong(&ptr)); /* found in MVBs, purpose unknown, may mean that x2 is compressed long */
  4718.         if(x2&0x0002) printf("topspacing=%d ",scanint(&ptr));
  4719.         if(x2&0x0004) printf("bottomspacing=%d ",scanint(&ptr));
  4720.         if(x2&0x0008) printf("linespacing=%d ",scanint(&ptr));
  4721.         if(x2&0x0010) printf("leftindent=%d ",scanint(&ptr));
  4722.         if(x2&0x0020) printf("rightindent=%d ",scanint(&ptr));
  4723.         if(x2&0x0040) printf("firstlineindent=%d ",scanint(&ptr));
  4724.         if(x2&0x0080) fputs("unknownbit80set",stdout);
  4725.         if(x2&0x0100)
  4726.         {
  4727.             x1=(unsigned char)*ptr++;
  4728.             if(x1&1) fputs("box ",stdout);
  4729.             if(x1&2) fputs("topborder ",stdout);
  4730.             if(x1&4) fputs("leftborder ",stdout);
  4731.             if(x1&8) fputs("bottomborder ",stdout);
  4732.             if(x1&0x10) fputs("rightborder ",stdout);
  4733.             if(x1&0x20) fputs("thickborder ",stdout);
  4734.             if(x1&0x40) fputs("doubleborder ",stdout);
  4735.             if(x1&0x80) fputs("unknownborder",stdout);
  4736.             printf("%04x ",*((unsigned short *)ptr)++);
  4737.         }
  4738.         if(x2&0x0200)
  4739.         {
  4740.             i=scanint(&ptr);
  4741.             printf("tabs=%d ",i);
  4742.             while(i-->0)
  4743.             {
  4744.             x1=scanword(&ptr);
  4745.             printf("stop=%d ",x1&0x3FFF);
  4746.             if(x1&0x4000)
  4747.             {
  4748.                 x1=scanword(&ptr);
  4749.                 if(x1==1)
  4750.                 {
  4751.                 fputs("right ",stdout);
  4752.                 }
  4753.                 else if(x1==2)
  4754.                 {
  4755.                 fputs("center ",stdout);
  4756.                 }
  4757.                 else
  4758.                 {
  4759.                 error("unknowntabmodifier=%02x",x1);
  4760.                 }
  4761.             }
  4762.             }
  4763.         }
  4764.         if(x2&0x0400) fputs("rightalign ",stdout);
  4765.         if(x2&0x0800) fputs("centeralign ",stdout);
  4766.         if(x2&0x1000) fputs("keeplinestogether ",stdout);
  4767.         if(x2&0x2000) fputs("unknownbit2000set ",stdout); /* found in PRINTMAN.HLP */
  4768.         if(x2&0x4000) fputs("unknownbit4000set ",stdout); /* found in PRINTMAN.HLP, RATTLER.HLP */
  4769.         if(x2&0x8000) fputs("unknownbit8000set",stdout);
  4770.         putchar('\n');
  4771.         while(1)
  4772.         {
  4773.             str=PrintString(str,strlen(str))+1;
  4774.             if((unsigned char)ptr[0]==0xFF)
  4775.             {
  4776.             ptr++;
  4777.             break;
  4778.             }
  4779.             else switch((unsigned char)ptr[0])
  4780.             {
  4781.             case 0x20:
  4782.             printf("{vfld%ld}",*(long *)(ptr+1));
  4783.             ptr+=5;
  4784.             break;
  4785.             case 0x21:
  4786.             printf("{dtype%d}",*(short *)(ptr+1));
  4787.             ptr+=3;
  4788.             break;
  4789.             case 0x80: /* font change */
  4790.             printf("[font=%u]",*(short *)(ptr+1));
  4791.             ptr+=3;
  4792.             break;
  4793.             case 0x81:
  4794.             puts("[LF]");
  4795.             ptr++;
  4796.             break;
  4797.             case 0x82:
  4798.             puts("[CR]");
  4799.             ptr++;
  4800.             break;
  4801.             case 0x83:
  4802.             fputs("[TAB]",stdout);
  4803.             ptr++;
  4804.             break;
  4805.             case 0x86:
  4806.             ptr++;
  4807.             b=*ptr++;
  4808.             if(b==0x05) cmd="ewc"; else cmd="bmc";
  4809.             goto picture;
  4810.             case 0x87:
  4811.             ptr++;
  4812.             b=*ptr++;
  4813.             if(b==0x05) cmd="ewl"; else cmd="bml";
  4814.             goto picture;
  4815.             case 0x88:
  4816.             ptr++;
  4817.             b=*ptr++;
  4818.             if(b==0x05) cmd="ewr"; else cmd="bmr";
  4819.             picture:
  4820.             printf("[%s %02x ",cmd,b);
  4821.             l=scanlong(&ptr);
  4822.             switch(b)
  4823.             {
  4824.             case 0x22: /* HC31 */
  4825.                 x1=scanword(&ptr);
  4826.                 printf("hotspots=%u ",x1);
  4827.             case 0x03: /* HC30 */
  4828.                 switch(*(unsigned short *)ptr)
  4829.                 {
  4830.                 case 0:
  4831.                 fputs("baggage ",stdout);
  4832.                 break;
  4833.                 case 1:
  4834.                 fputs("embedded ",stdout);
  4835.                 break;
  4836.                 default:
  4837.                 error("Unknown %04x",((unsigned short *)ptr)[0]);
  4838.                 }
  4839.                 printf("bm%u]",((unsigned short *)ptr)[1]);
  4840.                 break;
  4841.             case 0x05:
  4842.                 printf("%04x ",((unsigned short *)ptr)[0]);
  4843.                 printf("%04x ",((unsigned short *)ptr)[1]);
  4844.                 printf("%04x ",((unsigned short *)ptr)[2]);
  4845.                 printf("%s]",ptr+6);
  4846.                 break;
  4847.             default:
  4848.                 error("Unknown picture flag %02x",b);
  4849.             }
  4850.             ptr+=l;
  4851.             break;
  4852.             case 0x89: /* end of hot spot */
  4853.             fputs("[U]",stdout);
  4854.             ptr++;
  4855.             break;
  4856.             case 0x8B: /* non-break-space */
  4857.             fputs("[~]",stdout);
  4858.             ptr++;
  4859.             break;
  4860.             case 0x8C: /* non-break-hyphen */
  4861.             fputs("[-]",stdout);
  4862.             ptr++;
  4863.             break;
  4864.             case 0xC8: /* macro */
  4865.             printf("[!%s]",ptr+3);
  4866.             ptr+=*(short *)(ptr+1)+3;
  4867.             break;
  4868.             case 0xCC: /* macro without font change */
  4869.             printf("[*!%s]",ptr+3);
  4870.             ptr+=*(short *)(ptr+1)+3;
  4871.             break;
  4872.             case 0xE0: /* Popup HC30 */
  4873.             printf("[^TOPIC%ld]",*(long *)(ptr+1));
  4874.             ptr+=5;
  4875.             break;
  4876.             case 0xE1: /* Jump HC30 */
  4877.             printf("[TOPIC%ld]",*(long *)(ptr+1));
  4878.             ptr+=5;
  4879.             break;
  4880.             case 0xE2: /* Popup HC31 */
  4881.             printf("[^%08lx]",*(long *)(ptr+1));
  4882.             ptr+=5;
  4883.             break;
  4884.             case 0xE3: /* Jump HC31 */
  4885.             printf("[%08lx]",*(long *)(ptr+1));
  4886.             ptr+=5;
  4887.             break;
  4888.             case 0xE6: /* Popup without font change */
  4889.             printf("[*^%08lx]",*(long *)(ptr+1));
  4890.             ptr+=5;
  4891.             break;
  4892.             case 0xE7: /* Jump without font change */
  4893.             printf("[*%08lx]",*(long *)(ptr+1));
  4894.             ptr+=5;
  4895.             break;
  4896.             case 0xEA: /* Popup into external file / secondary window */
  4897.             cmd="^";
  4898.             goto jump;
  4899.             case 0xEB: /* Jump into external file / secondary window */
  4900.             cmd="";
  4901.             goto jump;
  4902.             case 0xEE: /* Popup into external file / secondary window without font change */
  4903.             cmd="^*";
  4904.             goto jump;
  4905.             case 0xEF: /* Jump into external file / secondary window without font change */
  4906.             cmd="*";
  4907.             jump:
  4908.             switch(ptr[3])
  4909.             {
  4910.             case 0:
  4911.                 printf("[%s%08lx] ",cmd,*(long *)(ptr+4));
  4912.                 break;
  4913.             case 1: /* Popup into secondary window (silly) */
  4914.                 printf("[%s%08lx>%d]",cmd,*(long *)(ptr+4),(unsigned char)ptr[8]);
  4915.                 break;
  4916.             case 4:
  4917.                 printf("[%s%08lx@%s] ",cmd,*(long *)(ptr+4),ptr+8);
  4918.                 break;
  4919.             case 6: /* Popup into external file / secondary window (silly) */
  4920.                 printf("[%s%08lx>%s@%s] ",cmd,*(long *)(ptr+4),ptr+8,strchr(ptr+8,'\0')+1);
  4921.                 break;
  4922.             default:
  4923.                 putchar('[');
  4924.                 for(i=0;i<*(short *)(ptr+1);i++) printf("%02x",(unsigned char)ptr[i]);
  4925.                 putchar(']');
  4926.             }
  4927.             ptr+=*(short *)(ptr+1)+3;
  4928.             break;
  4929.             default:
  4930.             printf("[%02x]",(unsigned char)*ptr++);
  4931.             }
  4932.         }
  4933.         putchar('\n');
  4934.         }
  4935.     }
  4936.     if(LinkData2&&(TopicLink.RecordType!=TL_DISPLAY30&&TopicLink.RecordType!=TL_DISPLAY&&TopicLink.RecordType!=TL_TABLE))
  4937.     {
  4938.         PrintString(LinkData2,TopicLink.DataLen2);
  4939.         putchar('\n');
  4940.     }
  4941.     if(LinkData1) free(LinkData1);
  4942.     if(LinkData2) free(LinkData2);
  4943.     if(before31)
  4944.     {
  4945.         TopicPos+=TopicLink.NextBlock;
  4946.         if(TopicPos>=TopicFileLength) break;
  4947.     }
  4948.     else
  4949.     {
  4950.         if(TopicLink.NextBlock<=0) break;
  4951.         TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
  4952.         TopicPos=TopicLink.NextBlock;
  4953.     }
  4954.     }
  4955. }
  4956.  
  4957. void AliasList(FILE *hpj) /* write [ALIAS] section to HPJ file */
  4958. {
  4959.     int i,n;
  4960.     BOOL headerwritten;
  4961.  
  4962.     headerwritten=FALSE;
  4963.     for(i=0;i<ContextRecs;i=n)
  4964.     {
  4965.     for(n=i+1;n<ContextRecs&&ContextRec[i].TopicOffset==ContextRec[n].TopicOffset;n++)
  4966.     {
  4967.         if(!headerwritten)
  4968.         {
  4969.         fputs("Creating [ALIAS] section...\n",stderr);
  4970.         fputs("[ALIAS]\n",hpj);
  4971.         headerwritten=TRUE;
  4972.         }
  4973.         fprintf(hpj,"%s=",unhash(ContextRec[n].HashValue));
  4974.         fprintf(hpj,"%s\n",unhash(ContextRec[i].HashValue));
  4975.     }
  4976.     }
  4977.     if(headerwritten) putc('\n',hpj);
  4978. }
  4979.  
  4980. void CTXOMAPList(FILE *HelpFile,FILE *hpj) /* write [MAP] section to HPJ file */
  4981. {
  4982.     CTXOMAPREC CTXORec;
  4983.     unsigned short n,i;
  4984.     char *ptr;
  4985.  
  4986.     if(SearchFile(HelpFile,"|CTXOMAP",NULL))
  4987.     {
  4988.     n=my_getw(HelpFile);
  4989.     if(n)
  4990.     {
  4991.         fputs("Creating [MAP] section...\n",stderr);
  4992.         fputs("[MAP]\n",hpj);
  4993.         for(i=0;i<n;i++)
  4994.         {
  4995.         my_fread(&CTXORec,sizeof(CTXORec),HelpFile);
  4996.         ptr=TopicName(CTXORec.TopicOffset);
  4997.         if(ptr)
  4998.         {
  4999.             fprintf(hpj,"%s %ld\n",ptr,CTXORec.MapID);
  5000.         }
  5001.         else
  5002.         {
  5003.             fprintf(hpj,"TOPIC%08lx %ld\n",CTXORec.TopicOffset,CTXORec.MapID);
  5004.         }
  5005.         }
  5006.         putc('\n',hpj);
  5007.     }
  5008.     }
  5009. }
  5010.  
  5011. void GuessFromKeywords(FILE *HelpFile)
  5012. {
  5013.     long *keytopic;
  5014.     char kwdata[10];
  5015.     char kwbtree[10];
  5016.     int m,i,n,k,l,j,map;
  5017.     long FileLength,KWDataOffset,TopicOffset;
  5018.     BUFFER buf;
  5019.  
  5020.     fputs("Guessing...",stderr);
  5021.     for(k=0;k<2;k++) for(map='0';map<='z';map++)
  5022.     {
  5023.     if(k)
  5024.     {
  5025.         if(!keyindex[map-'0']) continue;
  5026.         sprintf(kwdata,"|%cKWDATA",map);
  5027.         sprintf(kwbtree,"|%cKWBTREE",map);
  5028.     }
  5029.     else
  5030.     {
  5031.         if(!lists[map-'0']) continue;
  5032.         sprintf(kwdata,"|%cWDATA",map);
  5033.         sprintf(kwbtree,"|%cWBTREE",map);
  5034.     }
  5035.     if(SearchFile(HelpFile,kwdata,&FileLength))
  5036.     {
  5037.         keytopic=my_malloc(FileLength);
  5038.         my_fread(keytopic,FileLength,HelpFile);
  5039.         if(SearchFile(HelpFile,kwbtree,NULL))
  5040.         {
  5041.         for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
  5042.         {
  5043.             for(i=0;i<n;i++)
  5044.             {
  5045.             my_gets(keyword,sizeof(keyword),HelpFile);
  5046.             m=my_getw(HelpFile);
  5047.             KWDataOffset=getdw(HelpFile);
  5048.             for(j=0;j<m;j++)
  5049.             {
  5050.                 TopicOffset=keytopic[KWDataOffset/4+j];
  5051.                 Guess(keyword,TopicOffset);
  5052.                 for(l=0;l<alternatives;l++)
  5053.                 {
  5054.                 if(alternative[l].OtherTopicOffset==TopicOffset)
  5055.                 {
  5056.                     Guess(keyword,alternative[l].TopicOffset);
  5057.                 }
  5058.                 }
  5059.             }
  5060.             }
  5061.             fputc('.',stderr);
  5062.         }
  5063.         free(keytopic);
  5064.         }
  5065.     }
  5066.     }
  5067.     if(guessed>0)
  5068.     {
  5069.     fprintf(stderr,"%ld context ids found\n",guessed);
  5070.     }
  5071.     else
  5072.     {
  5073.     fputs("no context ids found\n(you may use option /g to turn off guessing on this help file)\n",stderr);
  5074.     }
  5075. }
  5076.  
  5077. /* 1. extract topic names from topic macros, embedded pictures, and hotspot macros */
  5078. /* 2. build browse sequence start list */
  5079. /* 3. extract embedded pictures */
  5080. void FirstPass(FILE *HelpFile)
  5081. {
  5082.     SYSTEMRECORD *SysRec;
  5083.     TOPICLINK TopicLink;
  5084.     char *LinkData1;
  5085.     char *LinkData2;
  5086.     char *ptr;
  5087.     long l1,TopicNum,TopicPos,TopicOffset,BogusTopicOffset;
  5088.     int n,i,col,cols;
  5089.     BUFFER buf;
  5090.     TOPICHEADER30 *TopicHdr30;
  5091.     TOPICHEADER *TopicHdr;
  5092.     char filename[20];
  5093.     unsigned short x1,x2;
  5094.     short y1;
  5095.     MFILE *f;
  5096.  
  5097.     if(extractmacros)
  5098.     {
  5099.     for(SysRec=GetFirstSystemRecord(HelpFile);SysRec;SysRec=GetNextSystemRecord(SysRec))
  5100.     {
  5101.         if(SysRec->RecordType==0x0004)
  5102.         {
  5103.         strcpy(TopicTitle,"[CONFIG] section");
  5104.         CheckMacro(SysRec->Data);
  5105.         }
  5106.     }
  5107.     if(SearchFile(HelpFile,"|TopicId",NULL))
  5108.     {
  5109.         for(n=GetFirstPage(HelpFile,&buf,NULL);n;n=GetNextPage(HelpFile,&buf))
  5110.         {
  5111.         for(i=0;i<n;i++)
  5112.         {
  5113.             getdw(HelpFile);
  5114.             my_gets(buffer,sizeof(buffer),HelpFile);
  5115.             AddTopic(buffer,FALSE);
  5116.         }
  5117.         }
  5118.         guessing=FALSE; /* it's not neccessary to guess context ids if you know them */
  5119.     }
  5120.     }
  5121.     browses=0;
  5122.     browsenums=1;
  5123.     if(!SearchFile(HelpFile,"|TOPIC",&TopicFileLength)) return;
  5124.     TopicOffset=0L;
  5125.     TopicPos=12L;
  5126.     TopicNum=16;
  5127.     while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
  5128.     {
  5129.     if(before31)
  5130.     {
  5131.         if(TopicPos+TopicLink.NextBlock>=TopicFileLength) break;
  5132.     }
  5133.     else
  5134.     {
  5135.         if(TopicLink.NextBlock<=0) break;
  5136.     }
  5137.     if(TopicLink.DataLen1>sizeof(TOPICLINK))
  5138.     {
  5139.         LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
  5140.         if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
  5141.     }
  5142.     else LinkData1=NULL;
  5143.     if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
  5144.     {
  5145.         LinkData2=my_malloc(TopicLink.DataLen2+1);
  5146.         if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
  5147.     }
  5148.     else LinkData2=NULL;
  5149.     if(TopicLink.RecordType==TL_TOPICHDR) /* display a topic header record */
  5150.     {
  5151.         fprintf(stderr,"\rTopic %ld...",TopicNum-15);
  5152.         if(before31)
  5153.         {
  5154.         TopicHdr30=(TOPICHEADER30 *)LinkData1;
  5155.         if(resolvebrowse)
  5156.         {
  5157.             if(TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum>TopicNum
  5158.             || TopicHdr30->NextTopicNum==-1&&TopicHdr30->PrevTopicNum>TopicNum
  5159.             || TopicHdr30->NextTopicNum>TopicNum&&TopicHdr30->PrevTopicNum==-1)
  5160.             {
  5161.             AddBrowse(TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  5162.             }
  5163.             else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum)
  5164.             {
  5165.             MergeBrowse(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  5166.             }
  5167.             else if(TopicHdr30->NextTopicNum!=-1&&TopicHdr30->NextTopicNum<TopicNum&&(TopicHdr30->PrevTopicNum==-1||TopicHdr30->PrevTopicNum>TopicNum))
  5168.             {
  5169.             BackLinkBrowse(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  5170.             }
  5171.             else if(TopicHdr30->PrevTopicNum!=-1&&TopicHdr30->PrevTopicNum<TopicNum&&(TopicHdr30->NextTopicNum==-1||TopicHdr30->NextTopicNum>TopicNum))
  5172.             {
  5173.             LinkBrowse(TopicNum,TopicNum,TopicHdr30->NextTopicNum,TopicHdr30->PrevTopicNum);
  5174.             }
  5175.         }
  5176.         }
  5177.         else
  5178.         {
  5179.         BogusTopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
  5180.         if(BogusTopicOffset!=TopicOffset)
  5181.         {
  5182.             alternative=my_realloc(alternative,(alternatives+1)*sizeof(ALTERNATIVE));
  5183.             alternative[alternatives].TopicOffset=TopicOffset;
  5184.             alternative[alternatives].OtherTopicOffset=BogusTopicOffset;
  5185.             alternatives++;
  5186.         }
  5187.         TopicHdr=(TOPICHEADER *)LinkData1;
  5188.         if(resolvebrowse)
  5189.         {
  5190.             if(TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck>TopicOffset
  5191.             || TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>TopicOffset
  5192.             || TopicHdr->BrowseFor>TopicOffset&&TopicHdr->BrowseBck==-1L)
  5193.             {
  5194.             AddBrowse(TopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5195.             }
  5196.             else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset)
  5197.             {
  5198.             MergeBrowse(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5199.             }
  5200.             else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<TopicOffset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>TopicOffset))
  5201.             {
  5202.             BackLinkBrowse(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5203.             }
  5204.             else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<TopicOffset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>TopicOffset))
  5205.             {
  5206.             LinkBrowse(TopicOffset,BogusTopicOffset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5207.             }
  5208.         }
  5209.         if(extractmacros)
  5210.         {
  5211.             if(TopicLink.DataLen2&&*LinkData2)
  5212.             {
  5213.             strlcpy(TopicTitle,LinkData2,sizeof(TopicTitle));
  5214.             if(guessing)
  5215.             {
  5216.                 Guess(LinkData2,TopicOffset);
  5217.                 if(BogusTopicOffset!=TopicOffset)
  5218.                 {
  5219.                 Guess(LinkData2,BogusTopicOffset);
  5220.                 }
  5221.             }
  5222.             }
  5223.             else
  5224.             {
  5225.             strcpy(TopicTitle,"<< untitled topic >>");
  5226.             }
  5227.             if(TopicLink.DataLen2)
  5228.             {
  5229.             for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=strlen(LinkData2+i)+1)
  5230.             {
  5231.                 CheckMacro(LinkData2+i);
  5232.             }
  5233.             }
  5234.         }
  5235.         }
  5236.         TopicNum++;
  5237.     }
  5238.     else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  5239.     {
  5240.         ptr=LinkData1;
  5241.         scanlong(&ptr);
  5242.         if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  5243.         {
  5244.         TopicOffset+=scanword(&ptr);
  5245.         }
  5246.         if(TopicLink.RecordType==TL_TABLE)
  5247.         {
  5248.         cols=(unsigned char)*ptr++;
  5249.         x1=(unsigned char)*ptr++;
  5250.         switch(x1)
  5251.         {
  5252.         case 0: /* found in CALC.HLP and TERMINAL.HLP */
  5253.         case 2:
  5254.             ptr+=2;
  5255.         case 1:
  5256.         case 3:
  5257.             break;
  5258.         default:
  5259.             error("Unknown TableType %d",x1);
  5260.         }
  5261.         ptr+=4*cols;
  5262.         }
  5263.         for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
  5264.         {
  5265.         if(TopicLink.RecordType==TL_TABLE) ptr+=5;
  5266.         ptr+=4;
  5267.         x2=*(unsigned short *)ptr;
  5268.         ptr+=2;
  5269.         if(x2&0x0001) scanlong(&ptr); /* found in MVBs, purpose */
  5270.         /* unknown, may mean that x2 is really compressed long */
  5271.         if(x2&0x0002) scanint(&ptr);
  5272.         if(x2&0x0004) scanint(&ptr);
  5273.         if(x2&0x0008) scanint(&ptr);
  5274.         if(x2&0x0010) scanint(&ptr);
  5275.         if(x2&0x0020) scanint(&ptr);
  5276.         if(x2&0x0040) scanint(&ptr);
  5277.         if(x2&0x0100) ptr+=3;
  5278.         if(x2&0x0200)
  5279.         {
  5280.             y1=scanint(&ptr);
  5281.             while(y1-->0) if(scanword(&ptr)&0x4000) scanword(&ptr);
  5282.         }
  5283.         while(ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK))
  5284.         {
  5285.             if((unsigned char)ptr[0]==0xFF)
  5286.             {
  5287.             ptr++;
  5288.             break;
  5289.             }
  5290.             else switch((unsigned char)ptr[0])
  5291.             {
  5292.             case 0x21: /* dtype (MVB) */
  5293.             case 0x80: /* font change */
  5294.             ptr+=3;
  5295.             break;
  5296.             case 0x81:
  5297.             case 0x82:
  5298.             case 0x83:
  5299.             case 0x89: /* end of hotspot */
  5300.             case 0x8B: /* non-break-space */
  5301.             case 0x8C: /* non-break-hyphen */
  5302.             ptr++;
  5303.             break;
  5304.             case 0x86:
  5305.             case 0x87:
  5306.             case 0x88:
  5307.             ptr++;
  5308.             x1=*ptr++;
  5309.             l1=scanlong(&ptr);
  5310.             switch(x1)
  5311.             {
  5312.             case 0x22: /* HC31 */
  5313.                 x1=scanword(&ptr);
  5314.                 /* fall thru */
  5315.             case 0x03: /* HC30 */
  5316.                 switch(((unsigned short *)ptr)[0])
  5317.                 {
  5318.                 case 1:
  5319.                 for(x2=1;x2<extensions;x2++) if(!extension[x2]) break;
  5320.                 if(x2>=extensions)
  5321.                 {
  5322.                     extension=my_realloc(extension,(x2+1)*sizeof(char));
  5323.                     while(extensions<=x2) extension[extensions++]=0;
  5324.                 }
  5325.                 sprintf(filename,"bm%u",x2);
  5326.                 f=CreateMap(ptr+2,l1-2);
  5327.                 x1=ExtractBitmap(filename,f);
  5328.                 CloseMap(f);
  5329.                 extension[x2]=x1|0x10;
  5330.                 break;
  5331.                 }
  5332.                 break;
  5333.             case 0x05:
  5334.                 if(ptr[6]=='!'&&strchr(ptr+7,','))
  5335.                 {
  5336.                 CheckMacro(strchr(ptr+7,',')+1);
  5337.                 }
  5338.                 break;
  5339.             }
  5340.             ptr+=l1;
  5341.             break;
  5342.             case 0xC8: /* macro */
  5343.             case 0xCC: /* macro without font change */
  5344.             CheckMacro(ptr+3);
  5345.             ptr+=*(short *)(ptr+1)+3;
  5346.             break;
  5347.             case 0x20: /* vfld (MVC) */
  5348.             case 0xE0: /* popup jump HC30 */
  5349.             case 0xE1: /* topic jump HC30 */
  5350.             case 0xE2: /* popup jump HC31 */
  5351.             case 0xE3: /* topic jump HC31 */
  5352.             case 0xE6: /* popup jump without font change */
  5353.             case 0xE7: /* topic jump without font change */
  5354.             ptr+=5;
  5355.             break;
  5356.             case 0xEA: /* popup jump into external file */
  5357.             case 0xEB: /* topic jump into external file / secondary window */
  5358.             case 0xEE: /* popup jump into external file without font change */
  5359.             case 0xEF: /* topic jump into external file / secondary window without font change */
  5360.             switch((unsigned char)ptr[3])
  5361.             {
  5362.             case 0:
  5363.             case 1:
  5364.                 break;
  5365.             case 4:
  5366.                 StoreReference(ptr+8,TOPIC,NULL,*(long *)(ptr+4));
  5367.                 break;
  5368.             case 6:
  5369.                 StoreReference(strchr(ptr+8,'\0')+1,TOPIC,NULL,*(long *)(ptr+4));
  5370.                 break;
  5371.             default:
  5372.                 error("Unknown modifier %02x in tag %02x",(unsigned char)ptr[3],(unsigned char)ptr[0]);
  5373.             }
  5374.             ptr+=*(short *)(ptr+1)+3;
  5375.             break;
  5376.             default:
  5377.             error("Unknown %02x",*(unsigned char *)ptr);
  5378.             ptr++;
  5379.             }
  5380.         }
  5381.         }
  5382.     }
  5383.     if(LinkData1) free(LinkData1);
  5384.     if(LinkData2) free(LinkData2);
  5385.     if(before31)
  5386.     {
  5387.         TopicPos+=TopicLink.NextBlock;
  5388.     }
  5389.     else
  5390.     {
  5391.         TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
  5392.         TopicPos=TopicLink.NextBlock;
  5393.     }
  5394.     }
  5395. }
  5396.  
  5397. int _cdecl CTXOMAPRecCmp(const void *a,const void *b)
  5398. {
  5399.     if(((CTXOMAPREC *)a)->TopicOffset<((CTXOMAPREC *)b)->TopicOffset) return -1;
  5400.     if(((CTXOMAPREC *)a)->TopicOffset>((CTXOMAPREC *)b)->TopicOffset) return 1;
  5401.     return 0;
  5402. }
  5403.  
  5404. void ContextList(FILE *HelpFile)
  5405. {
  5406.     unsigned short maprecs,m;
  5407.     int j,window,len;
  5408.     BOOL morekeywords;
  5409.     CTXOMAPREC *map;
  5410.     char filename[13];
  5411.     TOPICLINK TopicLink;
  5412.     char *LinkData1;
  5413.     char *LinkData2;
  5414.     char *ptr;
  5415.     long TopicPos,TopicNum,TopicOffset;
  5416.  
  5417.     if(SearchFile(HelpFile,"|CTXOMAP",NULL))
  5418.     {
  5419.     maprecs=my_getw(HelpFile);
  5420.     if(maprecs)
  5421.     {
  5422.         map=my_malloc((long)maprecs*sizeof(CTXOMAPREC));
  5423.         my_fread(map,(long)maprecs*sizeof(CTXOMAPREC),HelpFile);
  5424.         qsort(map,maprecs,sizeof(CTXOMAPREC),CTXOMAPRecCmp);
  5425.     }
  5426.     }
  5427.     else
  5428.     {
  5429.      maprecs=0;
  5430.     }
  5431.     strcpy(filename,name);
  5432.     strcat(filename,ext);
  5433.     if(!SearchFile(HelpFile,"|TOPIC",&TopicFileLength)) return;
  5434.     TopicOffset=0L;
  5435.     TopicPos=12L;
  5436.     TopicNum=1;
  5437.     j=0;
  5438.     len=80;
  5439.     window=-1;
  5440.     morekeywords=TRUE;
  5441.     m=0;
  5442.     while(TopicRead(HelpFile,TopicPos,&TopicLink,sizeof(TopicLink))==sizeof(TOPICLINK))
  5443.     {
  5444.     if(TopicLink.DataLen1>sizeof(TOPICLINK))
  5445.     {
  5446.         LinkData1=my_malloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
  5447.         if(TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))!=TopicLink.DataLen1-sizeof(TOPICLINK)) break;
  5448.     }
  5449.     else LinkData1=NULL;
  5450.     if(TopicLink.DataLen1<TopicLink.BlockSize) /* read LinkData2 using phrase replacement */
  5451.     {
  5452.         LinkData2=my_malloc(TopicLink.DataLen2+1);
  5453.         if(TopicPhraseRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,TopicLink.DataLen2)!=TopicLink.BlockSize-TopicLink.DataLen1) break;
  5454.     }
  5455.     else LinkData2=NULL;
  5456.     if(TopicLink.NextBlock<=0||TopicLink.RecordType==TL_TOPICHDR) /* display a topic header record */
  5457.     {
  5458.         if(TopicNum>1) putchar('\n');
  5459.         if(before31) TopicOffset=TopicPos;
  5460.         while(m<maprecs&&map[m].TopicOffset<TopicOffset)
  5461.         {
  5462.         printf("  WinHelp(wnd,\"%s\",HELP_CONTEXT,%lu)\n",filename,map[m].MapID);
  5463.         m++;
  5464.         }
  5465.         if(!before31)
  5466.         {
  5467.         while(j<ContextRecs&&ContextRec[j].TopicOffset<TopicOffset)
  5468.         {
  5469.             if(len==80)
  5470.             {
  5471.             fputs("  Jump",stdout);
  5472.             }
  5473.             else
  5474.             {
  5475.             fputs("  Popup",stdout);
  5476.             }
  5477.             printf("Id(`%s",filename);
  5478.             if(window!=-1) printf(">%s",GetWindowName(window));
  5479.             printf("',`%s')\n",unhash(ContextRec[j].HashValue));
  5480.             j++;
  5481.         }
  5482.         if(morekeywords)
  5483.         {
  5484.             if(NextKeywordRec>=KeywordRecs)
  5485.             {
  5486.             if(NextKeywordOffset<0x7FFFFFFFL)
  5487.             {
  5488.                 CollectKeywords(HelpFile);
  5489.             }
  5490.             else
  5491.             {
  5492.                 morekeywords=FALSE;
  5493.             }
  5494.             }
  5495.             while(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset<TopicOffset)
  5496.             {
  5497.             if(KeywordRec[NextKeywordRec].Footnote=='K')
  5498.             {
  5499.                 printf("  JumpKeyword(`%s',`%s')\n",filename,KeywordRec[NextKeywordRec].Keyword);
  5500.             }
  5501.             else if(KeywordRec[NextKeywordRec].Footnote=='A')
  5502.             {
  5503.                 printf("  ALink(`%s@%s')\n",KeywordRec[NextKeywordRec].Keyword,filename);
  5504.             }
  5505.             NextKeywordRec++;
  5506.             }
  5507.         }
  5508.         window=ListWindows(HelpFile,TopicOffset);
  5509.         }
  5510.         if(TopicLink.NextBlock<=0) break;
  5511.         if(LinkData2&&*LinkData2)
  5512.         {
  5513.         printf("Topic %ld: %s",TopicNum,LinkData2);
  5514.         len=80;
  5515.         }
  5516.         else
  5517.         {
  5518.         len=printf("Topic %ld: untitled: ",TopicNum);
  5519.         }
  5520.         TopicNum++;
  5521.     }
  5522.     else if(LinkData2&&len<79)
  5523.     {
  5524.         for(ptr=LinkData2;len<79&&ptr<LinkData2+TopicLink.DataLen2;ptr++) if(*ptr)
  5525.         {
  5526.         putchar(*ptr);
  5527.         len++;
  5528.         }
  5529.     }
  5530.     if(TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  5531.     {
  5532.         ptr=LinkData1;
  5533.         scanlong(&ptr);
  5534.         TopicOffset+=scanword(&ptr);
  5535.     }
  5536.     if(LinkData1) free(LinkData1);
  5537.     if(LinkData2) free(LinkData2);
  5538.     if(before31)
  5539.     {
  5540.         if(TopicPos+TopicLink.NextBlock>=TopicFileLength) break;
  5541.         TopicPos+=TopicLink.NextBlock;
  5542.     }
  5543.     else
  5544.     {
  5545.         if(TopicLink.NextBlock<=0) break;
  5546.         TopicOffset=NextTopicOffset(TopicOffset,TopicLink.NextBlock,TopicPos);
  5547.         TopicPos=TopicLink.NextBlock;
  5548.     }
  5549.     }
  5550. }
  5551.  
  5552. BOOL HelpDeCompile(FILE *HelpFile,char *dumpfile,int mode,char *exportname,long offset)
  5553. {
  5554.     char filename[81];
  5555.     char hpjfilename[81];
  5556.     long FileLength;
  5557.     FILE *rtf;
  5558.     FILE *hpj;
  5559.     int d;
  5560.     long topic;
  5561.  
  5562.     if(!SearchFile(HelpFile,NULL,&FileLength)) return FALSE;
  5563.     if(!dumpfile)
  5564.     {
  5565.     switch(mode)
  5566.     {
  5567.     case 0:
  5568.         SysLoad(HelpFile);
  5569.         fprintf(stderr,"Decompiling %s...\n",HelpFileTitle);
  5570.         ContextLoad(HelpFile);
  5571.         PhraseLoad(HelpFile);
  5572.         ExportBitmaps(HelpFile);
  5573.         fputs("Pass 1...\n",stderr);
  5574.         FirstPass(HelpFile); /* valid only after ExportBitmaps */
  5575.         putc('\n',stderr);
  5576.         if(!before31&&guessing) GuessFromKeywords(HelpFile); /* after FirstPass, before SysList */
  5577.         strcpy(hpjfilename,name);
  5578.         if(mvp)
  5579.         {
  5580.         strcat(hpjfilename,".MVP");
  5581.         }
  5582.         else
  5583.         {
  5584.         strcat(hpjfilename,".HPJ");
  5585.         }
  5586.         hpj=my_fopen(hpjfilename,"wt");
  5587.         if(hpj)
  5588.         {
  5589.         strcpy(filename,name);
  5590.         strcat(filename,".ICO");
  5591.         SysList(HelpFile,hpj,filename); /* after ContextLoad */
  5592.         ListBaggage(HelpFile,hpj,before31);
  5593.         if(!mvp) AliasList(hpj); /* after ContextLoad, before TopicDump */
  5594.         strcpy(filename,name);
  5595.         strcat(filename,".PH");
  5596.         PhraseList(filename); /* after PhraseLoad */
  5597.         BuildName(filename,TopicsPerRTF>0);
  5598.         rtf=my_fopen(filename,"wt");
  5599.         if(rtf)
  5600.         {
  5601.             FontLoad(HelpFile,rtf,hpj);
  5602.             fputs("Pass 2...\n",stderr);
  5603.             fprintf(hpj,"[FILES]\n%s\n\n",filename);
  5604.             rtf=TopicDump(HelpFile,rtf,hpj,FALSE);
  5605.             putc('}',rtf);
  5606.             putc('\n',stderr);
  5607.             my_fclose(rtf);
  5608.         }
  5609.         NotInAnyTopic=FALSE;
  5610.         CTXOMAPList(HelpFile,hpj);
  5611.         if(extensions&&before31) ListBitmaps(hpj);
  5612.         if(win95) ListRose(HelpFile,hpj);
  5613.         my_fclose(hpj);
  5614.         }
  5615.         if(PhraseOffsets)
  5616.         {
  5617.         if(win95)
  5618.         {
  5619.             puts("Help Compiler will issue Note HC1002: Using existing phrase table");
  5620.         }
  5621.         else
  5622.         {
  5623.             puts("Help Compiler will issue Warning 5098: Using old key-phrase table");
  5624.         }
  5625.         }
  5626.         if(missing) puts("Help Compiler will issue Error 1230: File 'missing.bmp' not found");
  5627.         if(NotInAnyTopic) puts("Help Compiler will issue Warning 4098: Context string(s) in [MAP] section not defined in any topic");
  5628.         if(!extractmacros) puts("Help Compiler may issue Warning 4131: Hash conflict between 'x' and 'y'.");
  5629.         if(warnings)
  5630.         {
  5631.         printf("HELPDECO had problems with %s. Rebuilt helpfile may behave bad.\n",HelpFileName);
  5632.         }
  5633.         if(helpcomp[0])
  5634.         {
  5635.         if(win95&&SearchFile(HelpFile,"|Petra",NULL)) strcat(helpcomp," /a");
  5636.         printf("Use %s %s to recompile ",helpcomp,hpjfilename);
  5637.         if(AnnoFile) fputs("annotated ",stdout);
  5638.         puts("helpfile.");
  5639.         }
  5640.         break;
  5641.     case 1:
  5642.         HexDump(HelpFile,FileLength,offset);
  5643.         break;
  5644.     case 2:
  5645.         ListFiles(HelpFile);
  5646.         break;
  5647.     case 3: /* create lookalike RTF */
  5648.         SysLoad(HelpFile);
  5649.         fprintf(stderr,"Writing %s...\n",HelpFileTitle);
  5650.         exportplain=TRUE;
  5651.         ExportBitmaps(HelpFile);
  5652.         PhraseLoad(HelpFile);
  5653.         BuildName(filename,TopicsPerRTF>0);
  5654.         rtf=my_fopen(filename,"wt");
  5655.         if(rtf)
  5656.         {
  5657.         FontLoad(HelpFile,rtf,NULL);
  5658.         rtf=TopicDump(HelpFile,rtf,NULL,TRUE);
  5659.         putc('}',rtf);
  5660.         putc('\n',stderr);
  5661.         my_fclose(rtf);
  5662.         }
  5663.         break;
  5664.     case 4: /* generate contents file */
  5665.         SysLoad(HelpFile);
  5666.         fprintf(stderr,"Scanning %s...\n",HelpFileTitle);
  5667.         ContextLoad(HelpFile);
  5668.         PhraseLoad(HelpFile);
  5669.         checkexternal=TRUE;
  5670.         ExportBitmaps(HelpFile);
  5671.         FirstPass(HelpFile);
  5672.         putc('\n',stderr);
  5673.         if(!before31&&guessing) GuessFromKeywords(HelpFile); /* after FirstPass, before SysList */
  5674.         strcpy(filename,name);
  5675.         strcat(filename,".CNT");
  5676.         rtf=my_fopen(filename,"wt");
  5677.         if(rtf)
  5678.         {
  5679.         GenerateContent(HelpFile,rtf);
  5680.         my_fclose(rtf);
  5681.         }
  5682.         break;
  5683.     case 5: /* create entry point list  */
  5684.         resolvebrowse=FALSE;
  5685.         checkexternal=TRUE;
  5686.         SysLoad(HelpFile);
  5687.         fprintf(stderr,"Parsing %s...\n",HelpFileTitle);
  5688.         ContextLoad(HelpFile);
  5689.         PhraseLoad(HelpFile);
  5690.         ExportBitmaps(HelpFile);
  5691.         FirstPass(HelpFile);
  5692.         putc('\n',stderr);
  5693.         if(!before31&&guessing) GuessFromKeywords(HelpFile); /* after FirstPass, before SysList */
  5694.         ContextList(HelpFile);
  5695.         break;
  5696.     case 6: /* check external references */
  5697.     case 7:
  5698.         resolvebrowse=FALSE;
  5699.         checkexternal=TRUE;
  5700.         SysLoad(HelpFile);
  5701.         fprintf(stderr,"Checking %s...\n",HelpFileTitle);
  5702.         PhraseLoad(HelpFile);
  5703.         FirstPass(HelpFile);
  5704.         putc('\n',stderr);
  5705.         if(!external)
  5706.         {
  5707.         printf("No references to external files found in %s.\n",HelpFileName);
  5708.         }
  5709.         else if(mode==6)
  5710.         {
  5711.         CheckReferences();
  5712.         }
  5713.         else
  5714.         {
  5715.         ListReferences();
  5716.         }
  5717.         break;
  5718.     }
  5719.     }
  5720.     else
  5721.     {
  5722.     if(!SearchFile(HelpFile,dumpfile,&FileLength))
  5723.     {
  5724.         filename[0]='|';
  5725.         strlcpy(filename+1,dumpfile,sizeof(filename)-1);
  5726.         if(!SearchFile(HelpFile,filename,&FileLength))
  5727.         {
  5728.         fprintf(stderr,"Internal file %s not found.\n",dumpfile);
  5729.         return TRUE;
  5730.         }
  5731.         dumpfile=filename;
  5732.     }
  5733.     printf("FileName: %s FileSize: %ld\n",dumpfile,FileLength);
  5734.     if(exportname) /* export internal file */
  5735.     {
  5736.         FILE *f;
  5737.  
  5738.         f=my_fopen(exportname,"wb");
  5739.         if(f)
  5740.         {
  5741.         copy(HelpFile,FileLength,f);
  5742.         my_fclose(f);
  5743.         }
  5744.     }
  5745.     else if(mode==1)
  5746.     {
  5747.         HexDump(HelpFile,FileLength,offset);
  5748.     }
  5749.     else if(strcmp(dumpfile,"|TOPIC")==0)
  5750.     {
  5751.         SysLoad(HelpFile);
  5752.         PhraseLoad(HelpFile);
  5753.         DumpTopic(HelpFile,offset);
  5754.     }
  5755.     else if(strcmp(dumpfile+strlen(dumpfile)-4,".GRP")==0)
  5756.     {
  5757.         GroupDump(HelpFile);
  5758.     }
  5759.     else if(strcmp(dumpfile,"@LINK")==0)
  5760.     {
  5761.         LinkDump(HelpFile);
  5762.     }
  5763.     else if(sscanf(dumpfile,"%ld!%d",&topic,&d)==2&&topic!=0L&&d==0)
  5764.     {
  5765.         AnnotationDump(HelpFile,FileLength,dumpfile);
  5766.     }
  5767.     else if(strcmp(dumpfile,"|Phrases")==0||strcmp(dumpfile,"|PhrIndex")==0)
  5768.     {
  5769.         SysLoad(HelpFile);
  5770.         PhraseLoad(HelpFile);
  5771.         PhraseDump();
  5772.     }
  5773.     else if(strcmp(dumpfile,"|SYSTEM")==0)
  5774.     {
  5775.         SysDump(HelpFile);
  5776.     }
  5777.     else if(strcmp(dumpfile,"|TOMAP")==0)
  5778.     {
  5779.         ToMapDump(HelpFile,FileLength);
  5780.     }
  5781.     else if(strcmp(dumpfile,"|CONTEXT")==0)
  5782.     {
  5783.         BTreeDump(HelpFile,"ContextId: %h TopicOffset: 0x%08lx\n");
  5784.     }
  5785.     else if(dumpfile[0]=='|'&&(strcmp(dumpfile+2,"WBTREE")==0||strcmp(dumpfile+2,"KWBTREE")==0))
  5786.     {
  5787.         fseek(HelpFile,7,SEEK_CUR);
  5788.         d=getc(HelpFile);
  5789.         fseek(HelpFile,-8,SEEK_CUR);
  5790.         if(d=='!')
  5791.         {
  5792.         BTreeDump(HelpFile,"Keyword: '%s' Occurrance: %!\n");
  5793.         }
  5794.         else
  5795.         {
  5796.         BTreeDump(HelpFile,"Keyword: '%s' Count: %u KWDataAddress: 0x%08lx\n");
  5797.         }
  5798.     }
  5799.     else if(dumpfile[0]=='|'&&(strcmp(dumpfile+2,"WMAP")==0||strcmp(dumpfile+2,"KWMAP")==0))
  5800.     {
  5801.         KWMapDump(HelpFile);
  5802.     }
  5803.     else if(dumpfile[0]=='|'&&(strcmp(dumpfile+2,"WDATA")==0||strcmp(dumpfile+2,"KWDATA")==0))
  5804.     {
  5805.         KWDataDump(HelpFile,FileLength);
  5806.     }
  5807.     else if(strcmp(dumpfile,"|VIOLA")==0)
  5808.     {
  5809.         BTreeDump(HelpFile,"TopicOffset: 0x%08lx WindowNumber: %ld\n");
  5810.     }
  5811.     else if(strcmp(dumpfile,"|CTXOMAP")==0)
  5812.     {
  5813.         CTXOMAPDump(HelpFile);
  5814.     }
  5815.     else if(strcmp(dumpfile,"|CATALOG")==0)
  5816.     {
  5817.         CatalogDump(HelpFile);
  5818.     }
  5819.     else if(strcmp(dumpfile,"|Petra")==0)
  5820.     {
  5821.         BTreeDump(HelpFile,"TopicOffset: 0x%08lx SourceFileName: %s\n");
  5822.     }
  5823.     else if(strcmp(dumpfile,"|TopicId")==0)
  5824.     {
  5825.         BTreeDump(HelpFile,"TopicOffset: 0x%08lx ContextId: %s\n");
  5826.     }
  5827.     else if(strcmp(dumpfile,"|Rose")==0)
  5828.     {
  5829.         BTreeDump(HelpFile,"KeywordHashValue: 0x%08lx\nMacro: %s\nTitle: %s\n");
  5830.     }
  5831.     else if(strcmp(dumpfile,"|TTLBTREE")==0)
  5832.     {
  5833.         BTreeDump(HelpFile,"TopicOffset: 0x%08lx TopicTitle: %s\n");
  5834.     }
  5835.     else if(strcmp(dumpfile,"|FONT")==0)
  5836.     {
  5837.         FontDump(HelpFile);
  5838.     }
  5839.     else /* generic  */
  5840.     {
  5841.         topic=ftell(HelpFile);
  5842.         if(my_getw(HelpFile)==0x293B) /* if it's a B+ tree */
  5843.         {
  5844.         my_getw(HelpFile);
  5845.         my_getw(HelpFile);
  5846.         filename[0]='\0';
  5847.         while((d=getc(HelpFile))>0) /* format according to Structure */
  5848.         {
  5849.             switch(d)
  5850.             {
  5851.             case 'L':
  5852.             case '4':
  5853.             case 'a':
  5854.             strcat(filename,"0x%08lx ");
  5855.             break;
  5856.             case '2':
  5857.             strcat(filename,"%5u ");
  5858.             break;
  5859.             case 'F':
  5860.             case 'i':
  5861.             case 'z':
  5862.             strcat(filename,"'%s' ");
  5863.             break;
  5864.             case '!':
  5865.             strcat(filename,"%!");
  5866.             break;
  5867.             default:
  5868.             error("Unknown Btree field type '%c'",d);
  5869.             }
  5870.         }
  5871.         strcat(filename,"\n");
  5872.         fseek(HelpFile,topic,SEEK_SET);
  5873.         BTreeDump(HelpFile,filename);
  5874.         }
  5875.         else
  5876.         {
  5877.         fseek(HelpFile,topic,SEEK_SET);
  5878.         HexDump(HelpFile,FileLength,offset);
  5879.         }
  5880.     }
  5881.     }
  5882.     return TRUE;
  5883. }
  5884.  
  5885. int _cdecl main(int argc,char *argv[])
  5886. {
  5887.     char AnnoFileName[81];
  5888.     char drive[_MAX_DRIVE];
  5889.     char dir[_MAX_DIR];
  5890.     FILE *f;
  5891.     int mode;
  5892.     BOOL annotate;
  5893.     char *filename;
  5894.     char *dumpfile;
  5895.     char *exportname;
  5896.     long offset;
  5897.     int i,j;
  5898.  
  5899.     /* initialize hash value coding oldtable */
  5900.     memset(oldtable,0,sizeof(oldtable));
  5901.     for(i=0;i<9;i++) oldtable['1'+i]=i+1;
  5902.     oldtable['0']=10;
  5903.     oldtable['.']=12;
  5904.     oldtable['_']=13;
  5905.     for(i=0;i<26;i++) oldtable['A'+i]=oldtable['a'+i]=17+i;
  5906.     exportname=dumpfile=filename=NULL;
  5907.     AnnoFileName[0]='\0';
  5908.     mode=0;
  5909.     offset=0;
  5910.     annotate=FALSE;
  5911.     /* scan arguments */
  5912.     for(i=1;i<argc;i++)
  5913.     {
  5914.     if(argv[i][0]=='/'||argv[i][0]=='-')
  5915.     {
  5916.         switch(tolower((unsigned char)argv[i][1]))
  5917.         {
  5918.         case 'a':
  5919.         if(argv[i][2])
  5920.         {
  5921.             strlcpy(AnnoFileName,argv[i]+2,sizeof(AnnoFileName));
  5922.         }
  5923.         else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
  5924.         {
  5925.             strlcpy(AnnoFileName,argv[i+1],sizeof(AnnoFileName));
  5926.             i++;
  5927.         }
  5928.         annotate=TRUE;
  5929.         break;
  5930.         case 'b':
  5931.         resolvebrowse=FALSE;
  5932.         break;
  5933.         case 'c':
  5934.         mode=4;
  5935.         break;
  5936.         case 'd':
  5937.         mode=2;
  5938.         break;
  5939.         case 'e':
  5940.         mode=7;
  5941.         break;
  5942.         case 'f':
  5943.         listtopic=TRUE;
  5944.         break;
  5945.         case 'g':
  5946.         guessing=FALSE;
  5947.         break;
  5948.         case 'h': // add entry to prefix table
  5949.         for(j=0;j<sizeof(prefix)/sizeof(prefix[0])&&prefix[j];j++) ;
  5950.         if(j<sizeof(prefix)/sizeof(prefix[0]))
  5951.         {
  5952.             if(argv[i][2])
  5953.             {
  5954.             prefix[j]=argv[i]+2;
  5955.             }
  5956.             else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
  5957.             {
  5958.             prefix[j]=argv[i+1];
  5959.             i++;
  5960.             }
  5961.         }
  5962.         else
  5963.         {
  5964.             fprintf(stderr,"Prefix table full.\n");
  5965.         }
  5966.         break;
  5967.         case 'i':
  5968.         reportderived=TRUE;
  5969.         break;
  5970.         case 'l':
  5971.         mode=5;
  5972.         break;
  5973.         case 'm':
  5974.         extractmacros=FALSE;
  5975.         break;
  5976.         case 'n':
  5977.         nopagebreak=TRUE;
  5978.         break;
  5979.         case 'p':
  5980.         mode=6;
  5981.         break;
  5982.         case 'r':
  5983.         mode=3;
  5984.         break;
  5985.         case 's':
  5986.         if(argv[i][2])
  5987.         {
  5988.             TopicsPerRTF=atoi(argv[i]+2);
  5989.         }
  5990.         else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
  5991.         {
  5992.             TopicsPerRTF=atoi(argv[i+1]);
  5993.             i++;
  5994.         }
  5995.         break;
  5996.         case 't':
  5997.         if(argv[i][2])
  5998.         {
  5999.             offset=strtoul(argv[i]+2,NULL,0);
  6000.         }
  6001.         else if(argv[i+1]&&argv[i+1][0]!='/'&&argv[i+1][0]!='-')
  6002.         {
  6003.             offset=strtoul(argv[i+1],NULL,0);
  6004.             i++;
  6005.         }
  6006.         break;
  6007.         case 'x':
  6008.         mode=1;
  6009.         break;
  6010.         case 'y':
  6011.         overwrite=TRUE;
  6012.         break;
  6013.         case 'z':
  6014.         exportLZ77=TRUE;
  6015.         break;
  6016.         default:
  6017.         fprintf(stderr,"unknown option '%s' ignored\n",argv[i]);
  6018.         }
  6019.     }
  6020.     else if(exportname)
  6021.     {
  6022.         fprintf(stderr,"additional parameter '%s' ignored\n",argv[i]);
  6023.     }
  6024.     else if(dumpfile)
  6025.     {
  6026.         exportname=argv[i];
  6027.     }
  6028.     else if(filename)
  6029.     {
  6030.         dumpfile=argv[i];
  6031.     }
  6032.     else
  6033.     {
  6034.         filename=argv[i];
  6035.     }
  6036.     }
  6037.     if(filename)
  6038.     {
  6039.     strupr(filename);
  6040.     _splitpath(filename,drive,dir,name,ext);
  6041.     if(ext[0]=='\0') strcpy(ext,".HLP");
  6042.     mvp=ext[1]=='M';
  6043.     _makepath(HelpFileName,drive,dir,name,ext);
  6044.     f=fopen(HelpFileName,"rb");
  6045.     if(f)
  6046.     {
  6047.         if(annotate)
  6048.         {
  6049.         if(AnnoFileName[0]=='\0') _makepath(AnnoFileName,drive,dir,name,".ANN");
  6050.         AnnoFile=fopen(AnnoFileName,"rb");
  6051.         if(!AnnoFile)
  6052.         {
  6053.             fprintf(stderr,"Couldn't find annotation file '%s'\n",AnnoFileName);
  6054.         }
  6055.         }
  6056.         prefixhash[0]=0L;
  6057.         for(i=1;prefix[i];i++)
  6058.         {
  6059.         prefixhash[i]=hash(prefix[i]);
  6060.         }
  6061.         if(!HelpDeCompile(f,dumpfile,mode,exportname,offset))
  6062.         {
  6063.         fprintf(stderr,"%s isn't a valid WinHelp file !\n",HelpFileName);
  6064.         }
  6065.         if(annotate&&AnnoFile) fclose(AnnoFile);
  6066.         my_fclose(f);
  6067.     }
  6068.     else
  6069.     {
  6070.         fprintf(stderr,"Can not open '%s'\n",HelpFileName);
  6071.     }
  6072.     }
  6073.     else
  6074.     {
  6075.     fprintf(stderr,"HELPDECO - decompile *.HLP/*.MVB files of Windows 3.x / 95 - %d bit Version 2.1\n"
  6076.                "M.Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany, CIS 100326,2776\n"
  6077.                "\n"
  6078.                "usage:   HELPDECO helpfile[.hlp]    [/y]  - decompile helpfile into all sources\n"
  6079.                "         HELPDECO helpfile[.hlp]    [/y] /a[annfile.ANN]  - and add annotations\n"
  6080.                "         HELPDECO helpfile[.hlp] /r [/y] [/n]    - decompile into lookalike RTF\n"
  6081.                "         HELPDECO helpfile[.hlp] /c [/y]  - generate Win95 .CNT content file\n"
  6082.                "         HELPDECO helpfile[.hlp] /l       - list entry points of this helpfile\n"
  6083.                "         HELPDECO helpfile[.hlp] /e [/f]  - list references to other helpfiles\n"
  6084.                "         HELPDECO helpfile[.hlp] /p [/f]  - check references to other helpfiles\n"
  6085.                "         HELPDECO helpfile[.hlp] /d [/x]  - display internal directory\n"
  6086.                "         HELPDECO helpfile[.hlp] \"internalfile\" [/x]    - display internal file\n"
  6087.                "         HELPDECO helpfile[.hlp] \"internalfile\" filename - export internal file\n"
  6088.                "options: /y overwrite without warning, /f list referencing topics, /x hex dump\n"
  6089.                "         /g no guessing, /hprefix add known contextid prefix, /n no page breaks\n"
  6090.                "To recreate all source files neccessary to rebuild a Windows helpfile, create\n"
  6091.                "a directory, change to this directory and call HELPDECO with the path and name\n"
  6092.                "of the helpfile to dissect. HELPDECO will extract all files contained in the\n"
  6093.                "helpfile in two passes and deposit them in the current directory. You may then\n"
  6094.                "rebuild the helpfile using the appropriate help compiler HC30, HC31, HCP, HCW,\n"
  6095.                "HCRTF, MVC, WMVC or MVCC. The file will not be identical, but should look and\n"
  6096.                "work like the original. This program is Freeware. Use at your own risk. No\n"
  6097.                "part of it may be used commercially. No fees may be charged on distributing.\n"
  6098. #ifndef _WIN32
  6099.                "Launch from Windows 95/Windows NT command line to handle larger helpfiles."
  6100. #endif
  6101.                ,sizeof(int)*8);
  6102.     }
  6103.     return 0;
  6104. }
  6105.