wwinhelp.pas 47 KB

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