wwinhelp.pas 47 KB

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