wwinhelp.pas 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683
  1. {
  2. This file is part of the Free Pascal Integrated Development Environment
  3. Copyright (c) 2000 by Berczi Gabor
  4. Help support for Windows Help (.HLP) files
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$R-}
  12. unit WWinHelp;
  13. {$ifdef cpullvm}
  14. {$modeswitch nestedprocvars}
  15. {$endif}
  16. interface
  17. uses Objects,
  18. WUtils,WHelp;
  19. const
  20. WinHelpMagicNo = $00035F3F;
  21. WinHelpBTreeHeaderMagicNo = $293B;
  22. WinHelpSystemHeaderMagicNo = $036c;
  23. { WinHelp Btree dataformat descriptors }
  24. wh_bdf_Long = 'L';
  25. wh_bdf_ASCIIZ = 'F';
  26. wh_bdf_ASCIIZ2 = 'i';
  27. wh_bdf_Short = '2';
  28. wh_bdf_Long2 = '4';
  29. wh_bdf_ASCIIZ3 = 'z';
  30. { WinHelp system record type }
  31. wh_srt_Title = 1;
  32. wh_srt_Copyright = 2;
  33. wh_srt_Contents = 3;
  34. wh_srt_Config = 4;
  35. wh_srt_Icon = 5;
  36. wh_srt_Window = 6;
  37. wh_srt_Citation = 8;
  38. wh_srt_LangID = 9;
  39. wh_srt_Content = 10;
  40. wh_srt_DefFont = 11;
  41. wh_srt_FtIndex = 12;
  42. wh_srt_Groups = 13;
  43. wh_srt_KeyIndex = 14;
  44. wh_srt_Language = 18;
  45. wh_srt_DLLMaps = 19;
  46. type
  47. PInteger = ^integer;
  48. TWinHelpHeader = packed record
  49. MagicNo : longint;
  50. DirectoryStart : longint;
  51. NonDirectoryStart: longint;
  52. EntireFileSize : longint;
  53. end;
  54. TWinHelpFileEntryHeader = packed record
  55. ReservedSpace : longint;
  56. UsedSpace : longint;
  57. FileFlags : byte;
  58. end;
  59. TWinHelpRecordHeader = packed record
  60. RecordType : word;
  61. DataSize : word;
  62. end;
  63. TWinHelpBTreeHeader = packed record
  64. Magic : word;
  65. Flags : word; { 0x0002 always set, 0x0400 set if directory }
  66. PageSize : word;
  67. DataFormat : array[1..16] of char;
  68. MustBeZero : word; { $0000 }
  69. PageSplits : word;
  70. RootPage : word;
  71. MustBeNegOne : integer; { $ffff }
  72. TotalPages : word;
  73. NumLevels : word;
  74. TotalEntries : word;
  75. Pages : record end;
  76. end;
  77. TWinHelpBTreeIndexHeader = packed record
  78. Unknown : longint;
  79. NumEntries : word; { no of index-entries }
  80. PrevPage : integer;
  81. end;
  82. {
  83. TWinHelpBTreeIndexEntry = packed record
  84. FileName : STRINGZ;
  85. PageNumber : word;
  86. end;
  87. }
  88. TWinHelpBTreeNodeHeader = packed record
  89. Unknown : longint;
  90. NumEntries : word; { no of index-entries }
  91. PrevPage : integer;
  92. NextPage : integer;
  93. end;
  94. TWinHelpSystemHeader = packed record
  95. Magic : word;
  96. Version : word;
  97. Always1 : word;
  98. GenDate : longint;
  99. Flags : word;
  100. end;
  101. TWinHelpPhrIndexHeader = packed record
  102. Magic : longint;
  103. NumEntries : longint;
  104. CompressedSize : longint;
  105. PhrImageSize : longint;
  106. PhrImageCompressedSize: longint;
  107. Always0 : longint;
  108. BitCount_Unk : word; { BitCount = lower 4 bits }
  109. Dunno : word;
  110. end;
  111. TWinHelpTopicBlockHeader = packed record
  112. LastTopicLink : longint;
  113. FirstTopicLink : longint;
  114. LastTopicHeader : longint;
  115. end;
  116. TWinHelpTopicBlock = packed record
  117. Header : TWinHelpTopicBlockHeader;
  118. Data : record end;
  119. end;
  120. TWinHelpTopicHeader = packed record
  121. BlockSize : longint;
  122. PrevOffset : longint; { prev topic }
  123. NextOffset : longint; { next topic }
  124. TopicNumber : longint;
  125. NonScrollRgnOfs : longint; { start of non-scrolling region (topic ofs) }
  126. ScrollRgnOfs : longint; { topic ofs }
  127. NextTopic : longint; { next type 2 record }
  128. end;
  129. TWinHelpTopicLink = packed record
  130. BlockSize : longint;
  131. DataLen2 : longint;
  132. PrevBlock : longint;
  133. NextBlock : longint;
  134. DataLen1 : longint;
  135. RecordType : byte;
  136. end;
  137. TTopicBlock = object
  138. Header : TWinHelpTopicBlockHeader;
  139. DataSize : longint;
  140. DataPtr : PByteArray;
  141. private
  142. CurOfs: longint;
  143. procedure Seek(Pos: longint);
  144. function GetPos: longint;
  145. function GetSize: longint;
  146. procedure Read(var Buf; Count: longint);
  147. end;
  148. PTopicEnumData = ^TTopicEnumData;
  149. TTopicEnumData = record
  150. TB : TTopicBlock;
  151. BlockNo : longint;
  152. TopicPos: longint;
  153. TopicOfs: longint;
  154. TL : TWinHelpTopicLink;
  155. LinkData1Size: longint;
  156. LinkData1: PByteArray;
  157. LinkData2Size: longint;
  158. LinkData2: PByteArray;
  159. end;
  160. PWinHelpFile = ^TWinHelpFile;
  161. TWinHelpFile = object(THelpFile)
  162. constructor Init(AFileName: string; AID: word);
  163. destructor Done; virtual;
  164. public
  165. function LoadIndex: boolean; virtual;
  166. function ReadTopic(T: PTopic): boolean; virtual;
  167. private
  168. F: PStream;
  169. Header: TWinHelpHeader;
  170. SysHeader: TWinHelpSystemHeader;
  171. Title: string;
  172. CNTFileName: string;
  173. PhrasesStart: longint;
  174. TTLBTreeStart: longint;
  175. TopicFileStart: longint;
  176. PhrIndexStart: longint;
  177. PhrImageStart: longint;
  178. Phrases: PUnsortedStringCollection;
  179. TreeDone: boolean;
  180. IndexLoaded: boolean;
  181. function ReadHeader: boolean;
  182. function ReadInternalDirectory: boolean;
  183. function ProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word;
  184. ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
  185. function ProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
  186. TotalPages: word; ReadIndexEntryMethod, ReadLeafEntryMethod: pointer;
  187. ScannedPages: PIntCollection): boolean;
  188. function ProcessTree(ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; NoDuplicates: boolean): boolean;
  189. function IDIRReadIndexEntry(SubPageNo: PInteger): boolean;
  190. function IDIRReadLeafEntry(P: pointer): boolean;
  191. function IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
  192. function TTLBReadIndexEntry(SubPageNo: PInteger): boolean;
  193. function TTLBReadLeafEntry(P: pointer): boolean;
  194. function TTLBProcessTopicEntry(const TopicTitle: string; FileOfs: longint): boolean;
  195. function ReadSystemFile: boolean;
  196. function ReadPhraseFile: boolean;
  197. function ReadTTLBTree: boolean;
  198. function ReadPhrIndexFile(PhraseOfs: PIntCollection; var IH: TWinHelpPhrIndexHeader): boolean;
  199. function ReadPhrImageFile(PhraseOfs: PIntCollection; const IH: TWinHelpPhrIndexHeader): boolean;
  200. function TopicBlockSize: word;
  201. function LZ77Compressed: boolean;
  202. function UsesHallCompression: boolean;
  203. procedure ExtractTopicOffset(TopicOffset: longint; var TopicBlockNo, TopicBlockOffset: word);
  204. function ReadTopicBlock(BlockNo: word; var T: TTopicBlock; ReadData: boolean): boolean;
  205. function ProcessTopicBlock(BlockNo: longint; EnumProc: TCallbackFunBoolParam): boolean;
  206. procedure PhraseDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
  207. procedure HallDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
  208. end;
  209. procedure RegisterHelpType;
  210. implementation
  211. uses Strings;
  212. function ReadString(F: PStream): string;
  213. var S: string;
  214. C: char;
  215. begin
  216. S:='';
  217. if Assigned(F) then
  218. repeat
  219. F^.Read(C,sizeof(C));
  220. if (F^.Status=stOK) and (C<>#0) then
  221. S:=S+C;
  222. until (C=#0) or (F^.Status<>stOK);
  223. ReadString:=S;
  224. end;
  225. function TTopicBlock.GetPos: longint;
  226. begin
  227. GetPos:=CurOfs;
  228. end;
  229. function TTopicBlock.GetSize: longint;
  230. begin
  231. GetSize:=DataSize;
  232. end;
  233. procedure TTopicBlock.Seek(Pos: longint);
  234. begin
  235. CurOfs:=Pos;
  236. end;
  237. procedure TTopicBlock.Read(var Buf; Count: longint);
  238. begin
  239. FillChar(Buf,Count,0);
  240. if Count>(DataSize-CurOfs) then
  241. begin
  242. Count:=Max(0,DataSize-CurOfs);
  243. end;
  244. Move(DataPtr^[CurOfs],Buf,Count);
  245. Inc(CurOfs,Count);
  246. end;
  247. constructor TWinHelpFile.Init(AFileName: string; AID: word);
  248. var OK: boolean;
  249. begin
  250. if inherited Init(AID)=false then Fail;
  251. New(Phrases, Init(1000,1000));
  252. F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  253. OK:=F<>nil;
  254. if OK then OK:=(F^.Status=stOK);
  255. if OK then
  256. begin
  257. OK:=ReadHeader;
  258. end;
  259. if OK=false then
  260. begin
  261. Done;
  262. Fail;
  263. end;
  264. end;
  265. function TWinHelpFile.ReadHeader: boolean;
  266. var OK: boolean;
  267. begin
  268. F^.Read(Header,sizeof(Header));
  269. OK:=(F^.Status=stOK);
  270. OK:=OK and (Header.MagicNo=WinHelpMagicNo);
  271. if OK then
  272. begin
  273. F^.Seek(Header.DirectoryStart);
  274. OK:=(F^.Status=stOK);
  275. end;
  276. if OK then
  277. OK:=ReadInternalDirectory;
  278. ReadHeader:=OK;
  279. end;
  280. function TWinHelpFile.TopicBlockSize: word;
  281. var Size: word;
  282. begin
  283. if (SysHeader.Version<=16) then
  284. Size:=2048 else
  285. if (SysHeader.Flags in[0,4]) then
  286. Size:=4096
  287. else
  288. Size:=2048;
  289. TopicBlockSize:=Size;
  290. end;
  291. function TWinHelpFile.LZ77Compressed: boolean;
  292. begin
  293. LZ77Compressed:=(SysHeader.Version>16) and (SysHeader.Flags<>0);
  294. end;
  295. function TWinHelpFile.UsesHallCompression: boolean;
  296. begin
  297. UsesHallCompression:=(PhrIndexStart<>0) and (PhrImageStart<>0);
  298. end;
  299. function TWinHelpFile.ReadSystemFile: boolean;
  300. var OK: boolean;
  301. FH: TWinHelpFileEntryHeader;
  302. RH: TWinHelpRecordHeader;
  303. StartOfs,RecStartOfs: longint;
  304. begin
  305. F^.Read(FH,sizeof(FH));
  306. OK:=(F^.Status=stOK);
  307. StartOfs:=F^.GetPos;
  308. F^.Read(SysHeader,sizeof(SysHeader));
  309. OK:=OK and (F^.Status=stOK);
  310. OK:=OK and (SysHeader.Magic=WinHelpSystemHeaderMagicNo);
  311. if OK then
  312. if SysHeader.Version>16 then
  313. begin
  314. repeat
  315. F^.Read(RH,sizeof(RH));
  316. OK:=(F^.Status=stOK);
  317. RecStartOfs:=F^.GetPos;
  318. if OK then
  319. begin
  320. case RH.RecordType of
  321. wh_srt_Title : Title:=ReadString(F);
  322. wh_srt_Content : CNTFileName:=ReadString(F);
  323. end;
  324. if F^.GetPos<>RecStartOfs+RH.DataSize then
  325. F^.Seek(RecStartOfs+RH.DataSize);
  326. OK:=(F^.Status=stOK);
  327. end;
  328. until (OK=false) or (F^.GetPos>=StartOfs+FH.UsedSpace);
  329. end
  330. else
  331. Title:=ReadString(F);
  332. OK:=OK and (F^.Status=stOK);
  333. ReadSystemFile:=OK;
  334. end;
  335. function LZ77Decompress(SrcBufP: pointer; SrcSize: longint; DestBufP: pointer; DestSize: longint): longint;
  336. var SrcBuf: PByteArray absolute SrcBufP;
  337. DestBuf: PByteArray absolute DestBufP;
  338. var SrcOfs: longint;
  339. function GetByte: byte;
  340. begin
  341. GetByte:=PByteArray(SrcBuf)^[SrcOfs];
  342. Inc(SrcOfs);
  343. end;
  344. var DestOfs: longint;
  345. procedure PutByte(B: byte);
  346. begin
  347. PByteArray(DestBuf)^[DestOfs]:=B;
  348. Inc(DestOfs);
  349. end;
  350. var B,Mask: byte;
  351. Len,J: word;
  352. I: integer;
  353. const N = 4096; F=16;
  354. begin
  355. SrcOfs:=0; DestOfs:=0; I:=N-F;
  356. while (SrcOfs<SrcSize) do
  357. begin
  358. B:=GetByte;
  359. for Mask:=0 to 7 do
  360. begin
  361. if SrcOfs=SrcSize then Break;
  362. if (B and (1 shl Mask))<>0 then
  363. begin
  364. J:=GetByte;
  365. Len:=GetByte;
  366. J:=J+(Len and $0f) shl 8;
  367. Len:=(Len and $f0) shr 4+3;
  368. while (Len>0) do
  369. begin
  370. PutByte(PByteArray(DestBuf)^[DestOfs-J-1]);
  371. {Inc(J); }Inc(I);
  372. Dec(Len);
  373. end;
  374. end
  375. else
  376. begin
  377. PutByte(GetByte);
  378. I:=(I+1) and (N-1);
  379. end;
  380. end;
  381. end;
  382. LZ77Decompress:=DestOfs;
  383. end;
  384. function TWinHelpFile.ReadPhraseFile: boolean;
  385. var OK: boolean;
  386. FH: TWinHelpFileEntryHeader;
  387. NumPhrases: word;
  388. DecompSize: longint;
  389. W: word;
  390. PhraseOfss: PWordArray;
  391. PhraseOfssSize: word;
  392. PhraseBuf: PByteArray;
  393. TempBuf: pointer;
  394. TempSize: longint;
  395. I,PhraseBufSize,PhraseOfs,PhraseSize: longint;
  396. S: string;
  397. begin
  398. F^.Read(FH,sizeof(FH));
  399. OK:=(F^.Status=stOK);
  400. F^.Read(NumPhrases,sizeof(NumPhrases));
  401. F^.Read(W,sizeof(W));
  402. OK:=(F^.Status=stOK) and (W=$0100);
  403. if OK then
  404. begin
  405. PhraseOfssSize:=(NumPhrases+1)*sizeof(word);
  406. GetMem(PhraseOfss,PhraseOfssSize);
  407. F^.Read(W,sizeof(W));
  408. if W=2*(NumPhrases+1) then
  409. begin
  410. { uncompressed data }
  411. PhraseOfss^[0]:=W;
  412. F^.Read(PhraseOfss^[1],PhraseOfssSize-sizeof(word)*1);
  413. DecompSize:=FH.UsedSpace-(PhraseOfssSize+2+2+2);
  414. PhraseBufSize:=DecompSize;
  415. GetMem(PhraseBuf,PhraseBufSize);
  416. F^.Read(PhraseBuf^,DecompSize);
  417. end
  418. else
  419. begin
  420. DecompSize:=W;
  421. F^.Read(W,sizeof(W));
  422. DecompSize:=DecompSize+longint(W) shl 16;
  423. Inc(DecompSize);
  424. PhraseOfss^[0]:=DecompSize;
  425. F^.Read(PhraseOfss^[1],PhraseOfssSize);
  426. PhraseBufSize:=DecompSize+10;
  427. GetMem(PhraseBuf,PhraseBufSize); FillChar(PhraseBuf^,DecompSize,0);
  428. TempSize:=FH.UsedSpace-(PhraseOfssSize+2+2+2);
  429. GetMem(TempBuf,TempSize);
  430. F^.Read(TempBuf^,TempSize);
  431. LZ77Decompress(TempBuf,TempSize,PhraseBuf,DecompSize);
  432. FreeMem(TempBuf,TempSize);
  433. end;
  434. for I:=1 to NumPhrases do
  435. begin
  436. PhraseOfs:=PhraseOfss^[I]-PhraseOfss^[1];
  437. if I=NumPhrases then
  438. PhraseSize:=DecompSize-PhraseOfs
  439. else
  440. PhraseSize:=PhraseOfss^[I+1]-PhraseOfss^[I];
  441. S:=MemToStr(PhraseBuf^[PhraseOfs],PhraseSize);
  442. Phrases^.InsertStr(S);
  443. end;
  444. FreeMem(PhraseOfss,PhraseOfssSize);
  445. FreeMem(PhraseBuf,PhraseBufSize);
  446. end;
  447. ReadPhraseFile:=OK;
  448. end;
  449. function TWinHelpFile.ProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word;
  450. ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
  451. var OK: boolean;
  452. BNH: TWinHelpBTreeNodeHeader;
  453. Count: longint;
  454. PageOfs: longint;
  455. CurPage: longint;
  456. begin
  457. CurPage:=PageNo;
  458. repeat
  459. PageOfs:=PagesBase+longint(PageSize)*CurPage;
  460. F^.Seek(PageOfs);
  461. OK:=(F^.Status=stOK);
  462. if OK then
  463. begin
  464. F^.Read(BNH,sizeof(BNH));
  465. OK:=(F^.Status=stOK);
  466. end;
  467. if OK then
  468. if (ScannedPages<>nil) and ScannedPages^.Contains(CurPage) then
  469. Break
  470. else
  471. begin
  472. if Assigned(ScannedPages) then ScannedPages^.Add(CurPage);
  473. Count:=0;
  474. repeat
  475. TreeDone:=not (longint(CallPointerMethod(ReadLeafEntryMethod,@Self,nil))<>0);
  476. Inc(Count);
  477. until (OK=false) or (F^.Status<>stOK) or TreeDone or (Count>=BNH.NumEntries){ or
  478. (F^.GetPos>=PageOfs+PageSize-BNH.NumEntries)};
  479. end;
  480. if (BNH.PrevPage<>-1) and (TreeDone=false) then
  481. CurPage:=BNH.PrevPage;
  482. until (OK=false) or TreeDone or (BNH.PrevPage=-1);
  483. ProcessLeafPage:=OK;
  484. end;
  485. function TWinHelpFile.ProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
  486. TotalPages: word; ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
  487. var BIH: TWinHelpBTreeIndexHeader;
  488. I: integer;
  489. SubPageNo: integer;
  490. OK: boolean;
  491. OldPos: longint;
  492. CurPage: longint;
  493. begin
  494. CurPage:=PageNo;
  495. repeat
  496. F^.Seek(PagesBase+longint(PageSize)*CurPage);
  497. OK:=(F^.Status=stOK);
  498. if OK then
  499. begin
  500. F^.Read(BIH,sizeof(BIH));
  501. OK:=(F^.Status=stOK);
  502. end;
  503. if OK then
  504. if (ScannedPages<>nil) and ScannedPages^.Contains(CurPage) then
  505. Break
  506. else
  507. begin
  508. if Assigned(ScannedPages) then ScannedPages^.Add(CurPage);
  509. for I:=1 to BIH.NumEntries do
  510. begin
  511. SubPageNo:=-1;
  512. OK:=(longint(CallPointerMethod(ReadIndexEntryMethod,@Self,@SubPageNo))<>0);
  513. OK:=OK and (F^.Status=stOK);
  514. if OK then
  515. if CurLevel<MaxLevel-1 then
  516. begin
  517. if (0<=SubPageNo) then
  518. if (ScannedPages=nil) or (ScannedPages^.Contains(SubPageNo)=false) then
  519. begin
  520. OldPos:=F^.GetPos;
  521. OK:=ProcessIndexPage(CurLevel+1,MaxLevel,PagesBase,PageSize,SubPageNo,TotalPages,
  522. ReadIndexEntryMethod,ReadLeafEntryMethod,ScannedPages);
  523. if F^.GetPos<>OldPos then
  524. F^.Seek(OldPos);
  525. end
  526. end
  527. else
  528. { process leaf page }
  529. if (0<=SubPageNo) then
  530. if (ScannedPages=nil) or (ScannedPages^.Contains(SubPageNo)=false) then
  531. begin
  532. OldPos:=F^.GetPos;
  533. OK:=ProcessLeafPage(PagesBase,PageSize,SubPageNo,TotalPages,ReadLeafEntryMethod,ScannedPages);
  534. if F^.GetPos<>OldPos then
  535. F^.Seek(OldPos);
  536. end;
  537. if TreeDone then
  538. Break;
  539. end;
  540. end;
  541. if (BIH.PrevPage>0) and (TreeDone=false) then
  542. CurPage:=BIH.PrevPage
  543. else
  544. Break;
  545. until (OK=false) or TreeDone;
  546. ProcessIndexPage:=OK;
  547. end;
  548. function TWinHelpFile.ProcessTree(ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; NoDuplicates: boolean): boolean;
  549. var BTH: TWinHelpBTreeHeader;
  550. OK: boolean;
  551. PagesBase: longint;
  552. ScannedPages: PIntCollection;
  553. begin
  554. ScannedPages:=nil;
  555. TreeDone:=false;
  556. F^.Read(BTH,sizeof(BTH));
  557. OK:=(F^.Status=stOK);
  558. PagesBase:=F^.GetPos;
  559. if OK then
  560. begin
  561. OK:=(BTH.Magic=WinHelpBTreeHeaderMagicNo) and
  562. (BTH.MustBeZero=0) and
  563. (BTH.MustBeNegOne=-1);
  564. end;
  565. if OK then
  566. begin
  567. if NoDuplicates then
  568. New(ScannedPages, Init(500,100));
  569. if BTH.NumLevels>1 then
  570. begin
  571. OK:=ProcessIndexPage(1,BTH.NumLevels,PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages,
  572. ReadIndexEntryMethod,ReadLeafEntryMethod,ScannedPages);
  573. end
  574. else
  575. OK:=ProcessLeafPage(PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages,ReadLeafEntryMethod,ScannedPAges);
  576. if Assigned(ScannedPages) then
  577. Dispose(ScannedPages, Done);
  578. end;
  579. ProcessTree:=OK;
  580. end;
  581. (*function TWinHelpFile.IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
  582. var OK: boolean;
  583. begin
  584. OK:=true;
  585. if FileName='|SYSTEM' then
  586. begin
  587. F^.Seek(FileOfs); OK:=(F^.Status=stOK);
  588. if OK then OK:=ReadSystemFile;
  589. end else
  590. if (FileName='|Phrases') then
  591. begin
  592. PhrasesStart:=FileOfs;
  593. end else
  594. ;
  595. IDIRProcessFile:=OK;
  596. end;
  597. function TWinHelpFile.IDIRProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word): boolean;
  598. var OK: boolean;
  599. BNH: TWinHelpBTreeNodeHeader;
  600. FileOfs: longint;
  601. Count: integer;
  602. S: string;
  603. CurOfs,PageOfs,OldPos: longint;
  604. begin
  605. PageOfs:=PagesBase+PageSize*PageNo;
  606. F^.Seek(PageOfs);
  607. OK:=(F^.Status=stOK);
  608. repeat
  609. if OK then
  610. begin
  611. F^.Read(BNH,sizeof(BNH));
  612. OK:=(F^.Status=stOK);
  613. end;
  614. if OK then
  615. begin
  616. Count:=0;
  617. repeat
  618. S:=ReadString(F);
  619. F^.Read(FileOfs,sizeof(FileOfs)); { longint }
  620. OK:=OK and (F^.Status=stOK);
  621. if OK then
  622. begin
  623. OldPos:=F^.GetPos;
  624. OK:=IDIRProcessFile(S,FileOfs);
  625. if F^.GetPos<>OldPos then
  626. F^.Seek(OldPos);
  627. end;
  628. Inc(Count);
  629. until (OK=false) or (Count=BNH.NumEntries){ or (F^.GetPos>=PageOfs+PageSize-BNH.NumEntries)};
  630. end;
  631. if BNH.PrevPage<>-1 then
  632. begin
  633. F^.Seek(PagesBase+PageSize*BNH.PrevPage);
  634. OK:=(F^.Status=stOK);
  635. end;
  636. until (OK=false) or (BNH.PrevPage=-1);
  637. IDIRProcessLeafPage:=OK;
  638. end;
  639. function TWinHelpFile.IDIRProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
  640. TotalPages: word): boolean;
  641. var BIH: TWinHelpBTreeIndexHeader;
  642. I: integer;
  643. SubPageNo: integer;
  644. OK: boolean;
  645. S: string;
  646. OldPos: longint;
  647. begin
  648. F^.Seek(PagesBase+PageSize*PageNo);
  649. OK:=(F^.Status=stOK);
  650. repeat
  651. if OK then
  652. begin
  653. F^.Read(BIH,sizeof(BIH));
  654. OK:=(F^.Status=stOK);
  655. end;
  656. if OK then
  657. for I:=1 to BIH.NumEntries do
  658. begin
  659. S:=ReadString(F);
  660. F^.Read(SubPageNo,sizeof(SubPageNo)); { word }
  661. OK:=OK and (F^.Status=stOK);
  662. if OK then
  663. if CurLevel<MaxLevel-1 then
  664. begin
  665. if (0<=SubPageNo) then
  666. begin
  667. OldPos:=F^.GetPos;
  668. OK:=IDIRProcessIndexPage(CurLevel+1,MaxLevel,PagesBase,PageSize,SubPageNo,TotalPages);
  669. if F^.GetPos<>OldPos then
  670. F^.Seek(OldPos);
  671. end
  672. end
  673. else
  674. { process leaf page }
  675. if (0<=SubPageNo) then
  676. OK:=IDIRProcessLeafPage(PagesBase,PageSize,SubPageNo,TotalPages);
  677. end;
  678. if (BIH.PrevPage>0) then
  679. begin
  680. F^.Seek(PagesBase+PageSize*BIH.PrevPage);
  681. OK:=(F^.Status=stOK);
  682. end
  683. else
  684. Break;
  685. until (OK=false);
  686. IDIRProcessIndexPage:=OK;
  687. end;
  688. function TWinHelpFile.ReadInternalDirectory: boolean;
  689. var BTH: TWinHelpBTreeHeader;
  690. OK: boolean;
  691. PagesBase: longint;
  692. begin
  693. F^.Read(BTH,sizeof(BTH));
  694. OK:=(F^.Status=stOK);
  695. PagesBase:=F^.GetPos;
  696. if OK then
  697. begin
  698. OK:=(BTH.Magic=WinHelpBTreeHeaderMagicNo) and
  699. (BTH.MustBeZero=0) and
  700. (BTH.MustBeNegOne=-1);
  701. end;
  702. if BTH.NumLevels>1 then
  703. OK:=IDIRProcessIndexPage(1,BTH.NumLevels,PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages)
  704. else
  705. OK:=IDIRProcessLeafPage(PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages);
  706. ReadInternalDirectory:=OK;
  707. end;
  708. *)
  709. function TWinHelpFile.IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
  710. var OK: boolean;
  711. begin
  712. OK:=true;
  713. if FileName='|SYSTEM' then
  714. begin
  715. F^.Seek(FileOfs); OK:=(F^.Status=stOK);
  716. if OK then OK:=ReadSystemFile;
  717. end else
  718. if (FileName='|Phrases') then begin PhrasesStart:=FileOfs; end else
  719. if (FileName='|TOPIC') then begin TopicFileStart:=FileOfs; end else
  720. if (FileName='|TTLBTREE') then begin TTLBTreeStart:=FileOfs; end else
  721. if (FileName='|PhrIndex') then begin PhrIndexStart:=FileOfs; end else
  722. if (FileName='|PhrImage') then begin PhrImageStart:=FileOfs; end else
  723. ;
  724. IDIRProcessFile:=OK;
  725. end;
  726. function TWinHelpFile.IDIRReadIndexEntry(SubPageNo: PInteger): boolean;
  727. var {S: string;}
  728. OK: boolean;
  729. begin
  730. {S:=}ReadString(F);
  731. F^.Read(SubPageNo^,sizeof(SubPageNo^));
  732. OK:=(F^.Status=stOK);
  733. IDIRReadIndexEntry:=OK;
  734. end;
  735. function TWinHelpFile.IDIRReadLeafEntry(P: pointer): boolean;
  736. var OK: boolean;
  737. S: string;
  738. FileOfs,OldPos: longint;
  739. begin
  740. S:=ReadString(F);
  741. F^.Read(FileOfs,sizeof(FileOfs)); { longint }
  742. OK:=(F^.Status=stOK);
  743. if OK then
  744. begin
  745. OldPos:=F^.GetPos;
  746. OK:=IDIRProcessFile(S,FileOfs);
  747. if F^.GetPos<>OldPos then
  748. F^.Seek(OldPos);
  749. OK:=OK and (F^.Status=stOK);
  750. end;
  751. IDIRReadLeafEntry:=OK;
  752. end;
  753. function TWinHelpFile.ReadInternalDirectory: boolean;
  754. var OK: boolean;
  755. FH: TWinHelpFileEntryHeader;
  756. begin
  757. F^.Read(FH,sizeof(FH));
  758. OK:=(F^.Status=stOK);
  759. if OK then
  760. OK:=ProcessTree(@TWinHelpFile.IDIRReadIndexEntry,@TWinHelpFile.IDIRReadLeafEntry,true);
  761. ReadInternalDirectory:=OK;
  762. end;
  763. function TWinHelpFile.TTLBReadIndexEntry(SubPageNo: PInteger): boolean;
  764. var TopicOffset: longint;
  765. OK: boolean;
  766. begin
  767. F^.Read(TopicOffset,sizeof(TopicOffset));
  768. F^.Read(SubPageNo^,sizeof(SubPageNo^));
  769. OK:=(F^.Status=stOK);
  770. TTLBReadIndexEntry:=OK;
  771. end;
  772. function TWinHelpFile.TTLBReadLeafEntry(P: pointer): boolean;
  773. var OK: boolean;
  774. S: string;
  775. TopicOfs,OldPos: longint;
  776. begin
  777. F^.Read(TopicOfs,sizeof(TopicOfs)); { longint }
  778. S:=ReadString(F);
  779. OK:=(F^.Status=stOK);
  780. if OK then
  781. begin
  782. OldPos:=F^.GetPos;
  783. OK:=TTLBProcessTopicEntry(S,TopicOfs);
  784. if F^.GetPos<>OldPos then
  785. F^.Seek(OldPos);
  786. OK:=OK and (F^.Status=stOK);
  787. end;
  788. TTLBReadLeafEntry:=OK;
  789. end;
  790. function TWinHelpFile.TTLBProcessTopicEntry(const TopicTitle: string; FileOfs: longint): boolean;
  791. var OK: boolean;
  792. {const Count: longint = 0;}
  793. begin
  794. { Inc(Count);
  795. if (Count mod 100)=1 then
  796. begin
  797. gotoxy(1,1); write(Count,' - ',IndexEntries^.Count,' - ',Topics^.Count);
  798. end;}
  799. OK:=(IndexEntries^.Count<MaxCollectionSize-10);
  800. if OK then
  801. begin
  802. if (TopicTitle<>'') and (FileOfs>=0) then
  803. begin
  804. AddIndexEntry(TopicTitle,FileOfs);
  805. AddTopic(FileOfs,FileOfs,'',nil,0);
  806. end;
  807. end;
  808. TTLBProcessTopicEntry:=OK;
  809. end;
  810. function TWinHelpFile.ReadTTLBTree: boolean;
  811. var OK: boolean;
  812. FH: TWinHelpFileEntryHeader;
  813. begin
  814. F^.Read(FH,sizeof(FH));
  815. OK:=(F^.Status=stOK);
  816. if OK then
  817. OK:=ProcessTree(@TWinHelpFile.TTLBReadIndexEntry,@TWinHelpFile.TTLBReadLeafEntry,true);
  818. ReadTTLBTree:=OK;
  819. end;
  820. function TWinHelpFile.ReadPhrIndexFile(PhraseOfs: PIntCollection; var IH: TWinHelpPhrIndexHeader): boolean;
  821. var OK: boolean;
  822. FH: TWinHelpFileEntryHeader;
  823. TotalBitPos,BufBitPos: longint;
  824. BitBuf: array[0..1023] of byte;
  825. CurFrag: word;
  826. function GetBit: integer;
  827. begin
  828. BufBitPos:=(TotalBitPos mod ((High(BitBuf)-Low(BitBuf)+1)*8));
  829. if (BufBitPos=0) then
  830. begin
  831. CurFrag:=Min(sizeof(BitBuf),FH.UsedSpace-(TotalBitPos div 8));
  832. F^.Read(BitBuf,CurFrag);
  833. OK:=OK and (F^.Status=stOK);
  834. end;
  835. if (BitBuf[Low(BitBuf)+BufBitPos div 8] and (1 shl (BufBitPos mod 8)))<>0 then
  836. GetBit:=1
  837. else
  838. GetBit:=0;
  839. Inc(TotalBitPos);
  840. end;
  841. var Delta: longint;
  842. I,J,LastOfs: longint;
  843. BitCount: integer;
  844. begin
  845. F^.Read(FH,sizeof(FH));
  846. OK:=(F^.Status=stOK);
  847. if OK then
  848. begin
  849. F^.Read(IH,sizeof(IH));
  850. OK:=(F^.Status=stOK) and (IH.Magic=1);
  851. end;
  852. if OK then
  853. begin
  854. PhraseOfs^.Add(0);
  855. TotalBitPos:=0; LastOfs:=0; BitCount:=(IH.BitCount_Unk and $0f);
  856. for I:=1 to IH.NumEntries do
  857. begin
  858. Delta:=1;
  859. while GetBit=1 do
  860. Delta:=Delta+(1 shl BitCount);
  861. for J:=0 to BitCount-1 do
  862. Delta:=Delta+(1 shl J)*GetBit;
  863. Inc(LastOfs,Delta);
  864. PhraseOfs^.Add(LastOfs);
  865. end;
  866. end;
  867. ReadPhrIndexFile:=OK;
  868. end;
  869. function TWinHelpFile.ReadPhrImageFile(PhraseOfs: PIntCollection; const IH: TWinHelpPhrIndexHeader): boolean;
  870. var OK: boolean;
  871. FH: TWinHelpFileEntryHeader;
  872. PhraseBufSize: longint;
  873. PhraseBuf: PByteArray;
  874. TempBufSize: longint;
  875. TempBuf: pointer;
  876. CurOfs,NextOfs: longint;
  877. I: longint;
  878. begin
  879. F^.Read(FH,sizeof(FH));
  880. OK:=(F^.Status=stOK);
  881. OK:=OK and (IH.PhrImageCompressedSize=FH.UsedSpace);
  882. if OK then
  883. begin
  884. PhraseBufSize:=IH.PhrImageSize;
  885. GetMem(PhraseBuf,PhraseBufSize);
  886. if IH.PhrImageSize=IH.PhrImageCompressedSize then
  887. begin
  888. F^.Read(PhraseBuf^,PhraseBufSize);
  889. end
  890. else
  891. begin
  892. TempBufSize:=IH.PhrImageCompressedSize;
  893. GetMem(TempBuf,TempBufSize);
  894. F^.Read(TempBuf^,TempBufSize);
  895. OK:=(F^.Status=stOK);
  896. if OK then LZ77Decompress(TempBuf,TempBufSize,PhraseBuf,PhraseBufSize);
  897. FreeMem(TempBuf,TempBufSize);
  898. end;
  899. if OK then
  900. begin
  901. for I:=1 to IH.NumEntries do
  902. begin
  903. CurOfs:=PhraseOfs^.AtInt(I-1);
  904. NextOfs:=PhraseOfs^.AtInt(I);
  905. Phrases^.InsertStr(MemToStr(PhraseBuf^[CurOfs],NextOfs-CurOfs));
  906. end;
  907. end;
  908. FreeMem(PhraseBuf,PhraseBufSize);
  909. end;
  910. ReadPhrImageFile:=OK;
  911. end;
  912. function TWinHelpFile.LoadIndex: boolean;
  913. var OK: boolean;
  914. PO: PIntCollection;
  915. IH: TWinHelpPhrIndexHeader;
  916. begin
  917. if IndexLoaded then OK:=true else
  918. begin
  919. if PhrasesStart<>0 then
  920. begin
  921. F^.Seek(PhrasesStart); OK:=(F^.Status=stOK);
  922. if OK then OK:=ReadPhraseFile;
  923. end else
  924. if (PhrIndexStart<>0) and (PhrImageStart<>0) then
  925. begin
  926. New(PO, Init(1000,1000));
  927. F^.Seek(PhrIndexStart); OK:=(F^.Status=stOK);
  928. if OK then OK:=ReadPhrIndexFile(PO,IH);
  929. if OK then begin F^.Seek(PhrImageStart); OK:=(F^.Status=stOK); end;
  930. if OK then OK:=ReadPhrImageFile(PO,IH);
  931. Dispose(PO, Done);
  932. end;
  933. if TTLBTreeStart<>0 then
  934. begin
  935. F^.Seek(TTLBTreeStart); OK:=(F^.Status=stOK);
  936. if OK then OK:=ReadTTLBTree;
  937. end;
  938. IndexLoaded:=OK;
  939. end;
  940. LoadIndex:=OK;
  941. end;
  942. procedure TWinHelpFile.ExtractTopicOffset(TopicOffset: longint; var TopicBlockNo, TopicBlockOffset: word);
  943. var {OfsBitCount: longint;
  944. OfsMask: longint;}
  945. BS: longint;
  946. begin
  947. { if LZ77Compressed then
  948. BS:=32768
  949. else
  950. BS:=TopicBlockSize;}
  951. BS:=32768;
  952. { for OfsBitCount:=0 to 31 do
  953. if (1 shl OfsBitCount)=BS then
  954. Break;
  955. OfsMask:=(1 shl OfsBitCount)-1;
  956. TopicBlockNo:=(TopicOffset and not OfsMask) shr OfsBitCount;
  957. TopicBlockOffset:=(TopicOffset and OfsMask);}
  958. TopicBlockNo:=TopicOffset div BS;
  959. TopicBlockOffset:=TopicOffset mod BS;
  960. end;
  961. function TWinHelpFile.ReadTopicBlock(BlockNo: word; var T: TTopicBlock; ReadData: boolean): boolean;
  962. var TempBuf: pointer;
  963. BlockBuf: ^TWinHelpTopicBlock;
  964. OK: boolean;
  965. RS,DecompSize: longint;
  966. const TempBufSize = 16384;
  967. begin
  968. F^.Reset;
  969. FillChar(T,sizeof(T),0);
  970. F^.Seek(TopicFileStart+sizeof(TWinHelpFileEntryHeader)+longint(BlockNo)*TopicBlockSize);
  971. OK:=(F^.Status=stOK);
  972. if OK then
  973. if ReadData=false then
  974. begin
  975. F^.Read(T.Header,sizeof(T.Header));
  976. OK:=(F^.Status=stOK);
  977. end
  978. else
  979. begin
  980. GetMem(BlockBuf, TopicBlockSize);
  981. F^.Read(BlockBuf^,TopicBlockSize);
  982. OK:=(F^.Status=stOK);
  983. if OK then
  984. begin
  985. Move(BlockBuf^.Header,T.Header,sizeof(T.Header));
  986. if LZ77Compressed then
  987. begin
  988. GetMem(TempBuf,TempBufSize);
  989. DecompSize:=LZ77Decompress(@BlockBuf^.Data,TopicBlockSize-sizeof(BlockBuf^.Header),TempBuf,TempBufSize);
  990. T.DataSize:=DecompSize;
  991. GetMem(T.DataPtr,T.DataSize);
  992. Move(TempBuf^,T.DataPtr^,T.DataSize);
  993. FreeMem(TempBuf,TempBufSize);
  994. end
  995. else
  996. begin
  997. T.DataSize:=TopicBlockSize-sizeof(BlockBuf^.Header);
  998. GetMem(T.DataPtr,T.DataSize);
  999. Move(BlockBuf^.Data,T.DataPtr^,T.DataSize);
  1000. end;
  1001. end;
  1002. FreeMem(BlockBuf,TopicBlockSize);
  1003. end;
  1004. ReadTopicBlock:=OK;
  1005. end;
  1006. procedure FreeTopicBlock(T: TTopicBlock);
  1007. begin
  1008. if (T.DataSize>0) and (T.DataPtr<>nil) then
  1009. begin
  1010. FreeMem(T.DataPtr,T.DataSize);
  1011. T.DataPtr:=nil;
  1012. end;
  1013. end;
  1014. procedure TWinHelpFile.PhraseDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
  1015. var SrcBuf: PByteArray absolute SrcBufP;
  1016. DestBuf: PByteArray absolute DestBufP;
  1017. var SrcOfs: longint;
  1018. function GetByte: byte;
  1019. begin
  1020. GetByte:=SrcBuf^[SrcOfs];
  1021. Inc(SrcOfs);
  1022. end;
  1023. var DestOfs: longint;
  1024. procedure PutByte(B: byte);
  1025. begin
  1026. if DestOfs<DestBufSize then
  1027. DestBuf^[DestOfs]:=B;
  1028. Inc(DestOfs);
  1029. end;
  1030. var B: byte;
  1031. I,Index: longint;
  1032. S: string;
  1033. begin
  1034. SrcOfs:=0; DestOfs:=0;
  1035. while (SrcOfs<SrcBufSize) do
  1036. begin
  1037. B:=GetByte;
  1038. if (B=0) or (B>15) then
  1039. PutByte(B)
  1040. else
  1041. begin
  1042. Index:=longint(B)*256-256+GetByte;
  1043. S:=GetStr(Phrases^.At(Index div 2));
  1044. if (Index mod 2)=1 then S:=S+' ';
  1045. for I:=1 to length(S) do
  1046. PutByte(ord(S[I]));
  1047. end;
  1048. end;
  1049. end;
  1050. procedure TWinHelpFile.HallDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
  1051. var SrcBuf: PByteArray absolute SrcBufP;
  1052. DestBuf: PByteArray absolute DestBufP;
  1053. var SrcOfs: longint;
  1054. function GetByte: byte;
  1055. begin
  1056. GetByte:=SrcBuf^[SrcOfs];
  1057. Inc(SrcOfs);
  1058. end;
  1059. var DestOfs: longint;
  1060. procedure PutByte(B: byte);
  1061. begin
  1062. if DestOfs<DestBufSize then
  1063. DestBuf^[DestOfs]:=B;
  1064. Inc(DestOfs);
  1065. end;
  1066. procedure EmitStr(const S: string);
  1067. var I: longint;
  1068. begin
  1069. for I:=1 to length(S) do
  1070. PutByte(ord(S[I]));
  1071. end;
  1072. procedure EmitStrIndex(Index: longint);
  1073. begin
  1074. EmitStr(GetStr(Phrases^.At(Index)));
  1075. end;
  1076. var B: longint;
  1077. I,Index: longint;
  1078. S: string;
  1079. begin
  1080. SrcOfs:=0; DestOfs:=0;
  1081. while (SrcOfs<SrcBufSize) do
  1082. begin
  1083. B:=GetByte;
  1084. if (B and 1)=0 then
  1085. EmitStrIndex(B div 2)
  1086. else
  1087. if (B and 3)=1 then
  1088. EmitStrIndex(B*64+64+GetByte)
  1089. else
  1090. if (B and 7)=3 then
  1091. for I:=1 to (B div 8)+1 do
  1092. PutByte(GetByte)
  1093. else
  1094. if (B and 15)=7 then
  1095. EmitStr(CharStr(' ',B div 16+1))
  1096. else
  1097. EmitStr(CharStr(#0,B div 16+1));
  1098. end;
  1099. end;
  1100. function TWinHelpFile.ProcessTopicBlock(BlockNo: longint; EnumProc: TCallbackFunBoolParam): boolean;
  1101. var TB: TTopicBlock;
  1102. TL: TWinHelpTopicLink;
  1103. BlockFileOfs: longint;
  1104. LinkData1Size: longint;
  1105. LinkData1: PByteArray;
  1106. LinkData2Size: longint;
  1107. LinkData2: PByteArray;
  1108. TempBufSize: longint;
  1109. TempBuf: PByteArray;
  1110. CurBlockOfs,LastLinkOfs: longint;
  1111. TopicPos,TopicOfs: longint;
  1112. OK: boolean;
  1113. TEN: TTopicEnumData;
  1114. DoCont: boolean;
  1115. begin
  1116. OK:=ReadTopicBlock(BlockNo,TB,true);
  1117. if OK then
  1118. begin
  1119. TopicOfs:=0; DoCont:=true;
  1120. BlockFileOfs:=longint(BlockNo)*TopicBlockSize;
  1121. if TB.Header.FirstTopicLink>0 then
  1122. TB.Seek((TB.Header.FirstTopicLink and $3fff)-sizeof(TB.Header));
  1123. if TB.Header.LastTopicLink=-1 then
  1124. LastLinkOfs:=TB.DataSize-1-sizeof(TL)
  1125. else
  1126. LastLinkOfs:={(TB.Header.LastTopicLink-BlockFileOfs-sizeof(TB.Header))}TB.GetSize-1;
  1127. while (DoCont) and OK and (TB.GetPos<=LastLinkOfs) do
  1128. begin
  1129. CurBlockOfs:=TB.GetPos;
  1130. TopicPos:=TB.GetPos+sizeof(TB.Header);
  1131. TB.Read(TL,sizeof(TL));
  1132. if (TL.BlockSize=0) or (TL.DataLen1=0) or (TB.GetPos>LastLinkOfs) or
  1133. (TB.GetSize-TB.GetPos<TL.BlockSize) then
  1134. Break;
  1135. LinkData1Size:=TL.DataLen1-sizeof(TL);
  1136. GetMem(LinkData1,LinkData1Size);
  1137. TB.Read(LinkData1^,LinkData1Size);
  1138. LinkData2Size:=TL.DataLen2;
  1139. GetMem(LinkData2,LinkData2Size);
  1140. if TL.DataLen2>TL.BlockSize-TL.DataLen1 then
  1141. begin
  1142. TempBufSize:=TL.BlockSize-TL.DataLen1;
  1143. GetMem(TempBuf,TempBufSize);
  1144. TB.Read(TempBuf^,TempBufSize);
  1145. if UsesHallCompression then
  1146. HallDecompress(TempBuf,TempBufSize,LinkData2,LinkData2Size)
  1147. else
  1148. PhraseDecompress(TempBuf,TempBufSize,LinkData2,LinkData2Size);
  1149. FreeMem(TempBuf,TempBufSize);
  1150. end
  1151. else
  1152. TB.Read(LinkData2^,TL.DataLen2);
  1153. FillChar(TEN,sizeof(TEN),0);
  1154. TEN.TB:=TB;
  1155. TEN.BlockNo:=BlockNo;
  1156. TEN.TopicPos:=TopicPos;
  1157. TEN.TopicOfs:=TopicOfs;
  1158. TEN.TL:=TL;
  1159. TEN.LinkData1Size:=LinkData1Size;
  1160. TEN.LinkData1:=LinkData1;
  1161. TEN.LinkData2Size:=LinkData2Size;
  1162. TEN.LinkData2:=LinkData2;
  1163. DoCont:=(longint(CallPointerLocal(EnumProc,
  1164. get_caller_frame(get_frame,get_pc_addr),@TEN)) and $ff)<>0;
  1165. case TL.RecordType of
  1166. $02: ;
  1167. $20,$23:
  1168. begin
  1169. Inc(TopicOfs,TL.DataLen2);
  1170. end;
  1171. end;
  1172. FreeMem(LinkData1,LinkData1Size);
  1173. FreeMem(LinkData2,LinkData2Size);
  1174. end;
  1175. FreeTopicBlock(TB);
  1176. end;
  1177. ProcessTopicBlock:=OK;
  1178. end;
  1179. function TWinHelpFile.ReadTopic(T: PTopic): boolean;
  1180. var OK: boolean;
  1181. BlockNo,BlockOfs: word;
  1182. TopicStartPos: longint;
  1183. GotIt: boolean;
  1184. TH: TWinHelpTopicHeader;
  1185. CurLine: string;
  1186. Lines: PUnsortedStringCollection;
  1187. EmitSize: longint;
  1188. LastEmittedChar: integer;
  1189. procedure FlushLine;
  1190. begin
  1191. Lines^.InsertStr(CurLine); CurLine:='';
  1192. end;
  1193. procedure EmitText(const S: string);
  1194. begin
  1195. Inc(EmitSize,length(S));
  1196. if length(CurLine)+length(S)>High(S) then
  1197. FlushLine;
  1198. CurLine:=CurLine+S;
  1199. if length(S)>0 then
  1200. LastEmittedChar:=ord(S[length(S)]);
  1201. end;
  1202. procedure EmitTextC(C: PChar);
  1203. var RemSize,CurOfs,CurFrag: longint;
  1204. S: string;
  1205. begin
  1206. if C=nil then Exit;
  1207. RemSize:=StrLen(C); CurOfs:=0;
  1208. while (RemSize>0) do
  1209. begin
  1210. CurFrag:=Min(RemSize,255);
  1211. S[0]:=chr(CurFrag);
  1212. Move(PByteArray(C)^[CurOfs],S[1],CurFrag);
  1213. EmitText(S);
  1214. Dec(RemSize,CurFrag); Inc(CurOfs,CurFrag);
  1215. end;
  1216. end;
  1217. function SearchTopicStart(P: PTopicEnumData): boolean;
  1218. begin
  1219. case P^.TL.RecordType of
  1220. $02 : TopicStartPos:=P^.TopicPos;
  1221. end;
  1222. GotIt:=(P^.TL.RecordType in [$20,$23]) and (P^.TopicOfs<=BlockOfs) and (BlockOfs<P^.TopicOfs+P^.LinkData2Size);
  1223. SearchTopicStart:=not GotIt;
  1224. end;
  1225. function RenderTopicProc(P: PTopicEnumData): boolean;
  1226. var LinkData1Ofs: longint;
  1227. LinkData2Ofs: longint;
  1228. function ReadUCHAR: byte;
  1229. begin
  1230. ReadUCHAR:=P^.LinkData1^[LinkData1Ofs];
  1231. Inc(LinkData1Ofs);
  1232. end;
  1233. function ReadCHAR: shortint;
  1234. var B: byte;
  1235. U: shortint absolute B;
  1236. begin
  1237. B:=ReadUCHAR;
  1238. ReadCHAR:=U;
  1239. end;
  1240. function ReadUSHORT: word;
  1241. begin
  1242. ReadUSHORT:=ReadUCHAR+longint(ReadUCHAR)*256;
  1243. end;
  1244. function ReadSHORT: integer;
  1245. var W: word;
  1246. I: integer absolute W;
  1247. begin
  1248. W:=ReadUSHORT;
  1249. ReadSHORT:=I;
  1250. end;
  1251. function ReadComprUSHORT: word;
  1252. var B: byte;
  1253. begin
  1254. B:=ReadUCHAR;
  1255. if (B mod 2)=0 then
  1256. ReadComprUSHORT:=(B div 2)
  1257. else
  1258. ReadComprUSHORT:=(B div 2)+longint(ReadUCHAR)*128;
  1259. end;
  1260. {$Q-}
  1261. function ReadLONG: longint;
  1262. begin
  1263. ReadLONG:=ReadUSHORT+longint(ReadUSHORT)*65536;
  1264. end;
  1265. function ReadULONG: longint;
  1266. begin
  1267. ReadULONG:=ReadLONG;
  1268. end;
  1269. {$Q+}
  1270. function ReadComprSHORT: integer;
  1271. var B: byte;
  1272. begin
  1273. B:=ReadUCHAR;
  1274. if (B mod 2)=0 then
  1275. ReadComprSHORT:=longint(B div 2)-64
  1276. else
  1277. ReadComprSHORT:=((B div 2)+longint(ReadUCHAR)*128)-16384;
  1278. end;
  1279. function ReadComprULONG: longint;
  1280. var W: word;
  1281. begin
  1282. W:=ReadUSHORT;
  1283. if (W mod 2)=0 then
  1284. ReadComprULONG:=(W div 2)
  1285. else
  1286. ReadComprULONG:=(W div 2)+longint(ReadUSHORT)*32768;
  1287. end;
  1288. function ReadComprLONG: longint;
  1289. var W: word;
  1290. begin
  1291. W:=ReadUSHORT;
  1292. if (W mod 2)=0 then
  1293. ReadComprLONG:=longint(W div 2)-16384
  1294. else
  1295. ReadComprLONG:=(W div 2)+longint(ReadUSHORT)*32768-67108864;
  1296. end;
  1297. function ReadString: string;
  1298. var S: string;
  1299. B: byte;
  1300. begin
  1301. S:='';
  1302. repeat
  1303. B:=ReadUCHAR;
  1304. if B<>0 then
  1305. S:=S+chr(B);
  1306. until B=0;
  1307. ReadString:=S;
  1308. end;
  1309. procedure EmitDebugText(const S: string);
  1310. begin
  1311. {$ifdef DEBUGMSG}
  1312. EmitText(S);
  1313. {$endif}
  1314. end;
  1315. var Finished: boolean;
  1316. S: string;
  1317. { ---- }
  1318. Cmd: integer;
  1319. I,TopicSize: longint;
  1320. NumberOfCols,TableType: byte;
  1321. MinTableWidth: integer;
  1322. Flags: longint;
  1323. NumberOfTabStops: integer;
  1324. TabStop: longint;
  1325. PType: integer;
  1326. Len: word;
  1327. SLen,LinkOfs: longint;
  1328. SPtr: pointer;
  1329. SBuf: PChar;
  1330. PictureSize,PictureStartOfs: longint;
  1331. FontNumber: integer;
  1332. begin
  1333. Finished:=((P^.TopicPos>TopicStartPos) or (P^.BlockNo>BlockNo)) and
  1334. (P^.TL.RecordType=$02); { next topic header found }
  1335. if (Finished=false) and (P^.TopicPos>=TopicStartPos) then
  1336. case P^.TL.RecordType of
  1337. $02 :
  1338. begin
  1339. S[0]:=chr(Min(StrLen(pointer(P^.LinkData2)),P^.LinkData2Size));
  1340. Move(P^.LinkData2^,S[1],ord(S[0]));
  1341. if S<>'' then
  1342. begin
  1343. EmitText(' '+S+' Ü'+hscLineBreak);
  1344. EmitText(' '+CharStr('ß',length(S)+3)+hscLineBreak);
  1345. end;
  1346. end;
  1347. $20,$23 :
  1348. begin
  1349. EmitDebugText(hscLineBreak+'<------ new record ------>'+hscLineBreak);
  1350. LinkData1Ofs:=0; LinkData2Ofs:=0; EmitSize:=0;
  1351. { ---- }
  1352. MinTableWidth:=0;
  1353. TopicSize:=ReadComprULONG;
  1354. if P^.TL.RecordType in[$20,$23] then
  1355. {TopicLen:=}ReadComprUSHORT;
  1356. if P^.TL.RecordType=$23 then
  1357. begin
  1358. NumberOfCols:=ReadUCHAR; TableType:=ReadUCHAR;
  1359. if TableType in[0,2] then
  1360. MinTableWidth:=ReadSHORT;
  1361. for I:=1 to NumberOfCols do
  1362. begin
  1363. {GapWidth:=}ReadSHORT;
  1364. {ColWidth:=}ReadSHORT;
  1365. end;
  1366. end;
  1367. if P^.TL.RecordType=$23 then
  1368. begin
  1369. {Column:=}ReadSHORT; {-1 = end of topic}
  1370. {Unknown:=}ReadSHORT; {Always0:=}ReadCHAR;
  1371. end;
  1372. {Unknown:=}ReadUCHAR; {Uknown:=}ReadCHAR;
  1373. ID:=ReadUSHORT;
  1374. Flags:=ReadUSHORT;
  1375. if (Flags and 1)<>0 then
  1376. {Unknown:=}ReadComprLONG;
  1377. if (Flags and 2)<>0 then
  1378. {SpacingAbove:=}ReadComprSHORT;
  1379. if (Flags and 4)<>0 then
  1380. {SpacingBelow:=}ReadComprSHORT;
  1381. if (Flags and 8)<>0 then
  1382. {SpacingLines:=}ReadComprSHORT;
  1383. if (Flags and 16)<>0 then
  1384. {LeftIndent:=}ReadComprSHORT;
  1385. if (Flags and 32)<>0 then
  1386. {RightIndent:=}ReadComprSHORT;
  1387. if (Flags and 64)<>0 then
  1388. {FirstLineIndent:=}ReadComprSHORT;
  1389. if (Flags and 256)<>0 then {BorderInfo}
  1390. begin
  1391. {BorderFlags:=}ReadUCHAR;
  1392. {BorderWidth:=}ReadSHORT;
  1393. end;
  1394. if (Flags and 512)<>0 then {TabInfo}
  1395. begin
  1396. NumberOfTabStops:=ReadComprSHORT;
  1397. for I:=1 to NumberOfTabStops do
  1398. begin
  1399. TabStop:=ReadComprUSHORT;
  1400. if (TabStop and $4000)<>0 then
  1401. {TabType:=}ReadComprUSHORT;
  1402. end;
  1403. end;
  1404. for I:=10 to 14 do
  1405. if (Flags and (1 shl I))<>0 then
  1406. ReadUCHAR;
  1407. {
  1408. if (TH.NonScrollRgnOfs<>-1) then
  1409. if (P^.TopicPos=(TH.ScrollRgnOfs and $3fff)) then
  1410. begin
  1411. EmitText(hscLineBreak);
  1412. EmitText(CharStr('Ä',80));
  1413. EmitText(hscLineBreak);
  1414. end;
  1415. }
  1416. while (LinkData2Ofs<P^.LinkData2Size) do
  1417. begin
  1418. LinkOfs:=-1;
  1419. SPtr:=@(P^.LinkData2^[LinkData2Ofs]);
  1420. SLen:=StrLen(SPtr);
  1421. if SLen>0 then
  1422. SBuf:=SPtr
  1423. else
  1424. SBuf:=nil;
  1425. Inc(LinkData2Ofs,SLen+1);
  1426. Cmd:=-1;
  1427. if (LinkData1Ofs<P^.LinkData1Size) then
  1428. begin
  1429. Cmd:=ReadUCHAR;
  1430. case Cmd of
  1431. $ff : { End of formatting }
  1432. EmitDebugText('[blockend]');
  1433. $20 : begin
  1434. EmitDebugText('[vfld]');
  1435. {vfldNumber:=}ReadLONG;
  1436. end;
  1437. $21 : begin
  1438. EmitDebugText('[dtype]');
  1439. {dtypeNumber:=}ReadSHORT;
  1440. end;
  1441. $3a,
  1442. $3c : {????}
  1443. begin
  1444. if LastEmittedChar<>ord(hscLineBreak) then
  1445. EmitText(hscLineBreak);
  1446. EmitDebugText('[tag0x'+hexstr(Cmd,2)+']');
  1447. end;
  1448. $80 : begin
  1449. FontNumber:=ReadSHORT;
  1450. EmitDebugText('[font'+inttostr(FontNumber)+']');
  1451. end;
  1452. $81 : {LineBreak}
  1453. begin
  1454. EmitDebugText('[br]');
  1455. EmitText(hscLineBreak);
  1456. end;
  1457. $82 : {End Of Paragraph}
  1458. begin
  1459. EmitDebugText('[eop]');
  1460. EmitText(hscLineBreak);
  1461. end;
  1462. $83 : {TAB}
  1463. begin
  1464. EmitDebugText('[tab]');
  1465. EmitText(' ');
  1466. end;
  1467. $86,
  1468. $87,
  1469. $88 : { ewc or bmc or bmcwd or bmct or button or mci }
  1470. begin
  1471. PType:=ReadUCHAR;
  1472. PictureSize:=ReadComprLONG;
  1473. if PType=$22 then
  1474. {NumberOfHotSpots:=}ReadComprSHORT;
  1475. PictureStartOfs:=LinkData1Ofs;
  1476. PictureSize:=Min(PictureSize,P^.LinkData1Size-LinkData1Ofs);
  1477. if PType in[$03,$22] then
  1478. begin
  1479. {PictureIsEmbedded:=}ReadSHORT;
  1480. {PictureNumber:=}ReadSHORT;
  1481. for I:=1 to PictureSize-4 do
  1482. {PictureData[I-1]:=}ReadCHAR;
  1483. end;
  1484. if PType=$05 then
  1485. begin
  1486. {Unknown1:=}ReadSHORT;
  1487. {Unknown2:=}ReadSHORT;
  1488. {Unknown3:=}ReadSHORT;
  1489. { +??? }
  1490. end;
  1491. while (LinkData1Ofs<PictureStartOfs+PictureSize) do
  1492. {}ReadCHAR;
  1493. EmitText('[img]');
  1494. end;
  1495. $89 : { end of hotspot }EmitDebugText('[ehs]');
  1496. $8b : { non-break space }; { does not appear in LinkData2 !!!! }
  1497. $8c : { non-break hypen };
  1498. $c6 : {????}
  1499. ReadLONG;
  1500. $c8, { macro }
  1501. $cc : { macro without font change }
  1502. begin
  1503. Len:=ReadSHORT;
  1504. for I:=1 to longint(Len)-3 do
  1505. {C:=}ReadUCHAR; { string }
  1506. end;
  1507. $e0, { popup jump } { start with underlined green }
  1508. $e1 : { topic jump } { start with underlined green }
  1509. begin
  1510. EmitDebugText('[linkgr]');
  1511. LinkOfs:=ReadLONG;
  1512. if LinkOfs>0 then
  1513. begin
  1514. EmitText(hscLink);
  1515. AddLinkToTopic(T,ID,LinkOfs);
  1516. end;
  1517. end;
  1518. $e2, { popup jump }
  1519. $e3, { topic jump }
  1520. $e6, { popup jump without font change }
  1521. $e7 : { topic jump without font change }
  1522. begin
  1523. EmitDebugText('[link]');
  1524. LinkOfs:=ReadLONG;
  1525. if LinkOfs>0 then
  1526. begin
  1527. EmitText(hscLink);
  1528. AddLinkToTopic(T,ID,LinkOfs);
  1529. end;
  1530. end;
  1531. $ea, { popup jump into external file }
  1532. $eb, { popup jump into external file without font change }
  1533. $ee, { popup jump into external file / secondary window }
  1534. $ef : { popup jump into external file / secondary window }
  1535. begin
  1536. EmitDebugText('[linkext]');
  1537. Len:=ReadSHORT;
  1538. PType:=ReadUCHAR;
  1539. LinkOfs:=ReadLONG;
  1540. {WindowNo:=}ReadUCHAR;
  1541. {NameOfExternalFile:=}ReadString;
  1542. {WindowName:=}ReadString;
  1543. if LinkOfs>0 then
  1544. begin
  1545. EmitText(hscLink);
  1546. AddLinkToTopic(T,ID,LinkOfs);
  1547. end;
  1548. end;
  1549. else EmitDebugText('[tag0x'+hexstr(Cmd,2)+']');
  1550. end;
  1551. end;
  1552. if SLen>0 then
  1553. EmitTextC(SPtr);
  1554. { case Cmd of
  1555. $81 : EmitText(hscLineBreak);
  1556. $82 : EmitText(hscLineBreak);
  1557. end;}
  1558. if LinkOfs>0 then
  1559. begin
  1560. EmitText(hscLink);
  1561. EmitDebugText('[eol]');
  1562. end;
  1563. end;
  1564. end;
  1565. end;
  1566. RenderTopicProc:=not Finished;
  1567. end;
  1568. begin
  1569. F^.Reset;
  1570. OK:=(TopicFileStart<>0) and (T<>nil);
  1571. if OK then
  1572. begin
  1573. ExtractTopicOffset(T^.FileOfs,BlockNo,BlockOfs);
  1574. TopicStartPos:=-1; GotIt:=false;
  1575. OK:=ProcessTopicBlock(BlockNo,TCallbackFunBoolParam(@SearchTopicStart));
  1576. OK:=OK and GotIt and (TopicStartPos<>-1);
  1577. if OK then
  1578. begin
  1579. CurLine:='';
  1580. New(Lines, Init(1000,1000));
  1581. LastEmittedChar:=-1;
  1582. OK:=ProcessTopicBlock(BlockNo,TCallbackFunBoolParam(@RenderTopicProc));
  1583. FlushLine;
  1584. BuildTopic(Lines,T);
  1585. Dispose(Lines, Done);
  1586. end;
  1587. end;
  1588. ReadTopic:=OK;
  1589. end;
  1590. destructor TWinHelpFile.Done;
  1591. begin
  1592. if Assigned(F) then Dispose(F, Done); F:=nil;
  1593. if Assigned(Phrases) then Dispose(Phrases, Done); Phrases:=nil;
  1594. inherited Done;
  1595. end;
  1596. function CreateProc(const FileName,Param: string;Index : longint): PHelpFile;
  1597. begin
  1598. CreateProc:=New(PWinHelpFile, Init(FileName,Index));
  1599. end;
  1600. procedure RegisterHelpType;
  1601. begin
  1602. RegisterHelpFileType({$ifdef FPC}@{$endif}CreateProc);
  1603. end;
  1604. END.