wwinhelp.pas 47 KB

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