wnghelp.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. {
  2. $Id$
  3. This file is part of the Free Pascal Integrated Development Environment
  4. Copyright (c) 2000 by Berczi Gabor
  5. Help support for Norton Guide (.NG) files
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$R-}
  13. unit WNGHelp;
  14. interface
  15. uses Objects,
  16. WUtils,WHelp;
  17. const
  18. NGFileSignature = 'NG';
  19. NGXORByte = $1a;
  20. NGMinRecordSize = $1a;
  21. ng_rtContainer = Byte ($0);
  22. ng_rtTopic = Byte ($1);
  23. type
  24. TNGFileHeader = packed record
  25. Signature : array[1..2] of char;
  26. Unknown : word;
  27. Version : word;
  28. MenuCount : word;
  29. GuideName : array[8..47] of char;
  30. Credits : array[48..377] of char;
  31. end;
  32. TNGRecordHeader = packed record
  33. RecType : word;
  34. RecLength : word;
  35. end;
  36. TNGContainerItem = packed record
  37. EntryNameOfs : word; { relative in record }
  38. SubItemsOfs : longint; { file offset to a new record header }
  39. end;
  40. PNGContainerRecord = ^TNGContainerRecord;
  41. TNGContainerRecord = packed record
  42. ItemCount : word;
  43. Unknown : word;
  44. IndexInParent : integer;
  45. ParentOfs : longint;
  46. MenuNo : integer;{ belongs to menu # }
  47. MenuItemNo : integer;{ belongs to menu item # }
  48. Unknown2 : array[18..25] of byte;
  49. Items : array[0..0] of TNGContainerItem;
  50. end;
  51. TNGTopicRecord = packed record
  52. NumberOfLines : word;
  53. SeeAlsoOfs : word;
  54. IndexInParent : integer;
  55. ParentOfs : longint;
  56. MenuNo : integer;{ belongs to menu # }
  57. MenuItemNo : integer;{ belongs to menu item # }
  58. PrevTopicOfs : longint;
  59. NextTopicOfs : longint;
  60. TopicLines : record end;
  61. { array of TNGSeeAlsoRec }
  62. end;
  63. PNGSeeAlsoRec = ^TNGSeeAlsoRec;
  64. TNGSeeAlsoRec = packed record
  65. EntryCount : word;
  66. Entries : record end;
  67. { array of LinkedRecOfs : longint; }
  68. { array of LinkNames : ASCIIZ; }
  69. end;
  70. PContainerItemRec = ^TContainerItemRec;
  71. TContainerItemRec = record
  72. Name : string;
  73. FilePos : longint;
  74. Container: PNGContainerRecord;
  75. end;
  76. PLinkRec = ^TLinkRec;
  77. TLinkRec = record
  78. Name : string;
  79. FilePos : longint;
  80. end;
  81. PNGHelpFile = ^TNGHelpFile;
  82. TNGHelpFile = object(THelpFile)
  83. constructor Init(AFileName: string; AID: word);
  84. destructor Done; virtual;
  85. public
  86. function LoadIndex: boolean; virtual;
  87. function ReadTopic(T: PTopic): boolean; virtual;
  88. private
  89. F: PStream;
  90. Header: TNGFileHeader;
  91. FirstRecordPos: longint;
  92. IndexLoaded: boolean;
  93. { NextHelpCtx: longint;}
  94. function ReadHeader: boolean;
  95. function ReadContainer(EnumProc: pointer): boolean;
  96. function ReadTopicRec(LineEnumProc: pointer; LinkEnumProc: pointer): boolean;
  97. function ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  98. end;
  99. TNGGetAttrColorProc = function(Attr: char; var Color: byte): boolean;
  100. function DefNGGetAttrColor(Attr: char; var Color: byte): boolean;
  101. const NGGetAttrColor : TNGGetAttrColorProc = DefNGGetAttrColor;
  102. implementation
  103. uses CallSpec;
  104. function DefNGGetAttrColor(Attr: char; var Color: byte): boolean;
  105. begin
  106. DefNGGetAttrColor:=false;
  107. end;
  108. function NGDecompressStr(const S: string): string;
  109. var NS: string;
  110. I: sw_integer;
  111. begin
  112. NS:='';
  113. I:=1;
  114. while (I<=length(S)) do
  115. begin
  116. if S[I]=#255 then
  117. begin
  118. NS:=NS+CharStr(' ',ord(S[I+1]));
  119. Inc(I);
  120. end
  121. else
  122. NS:=NS+S[I];
  123. Inc(I);
  124. end;
  125. NGDecompressStr:=NS;
  126. end;
  127. function TranslateStr(const S: string): string;
  128. var NS: string;
  129. I: sw_integer;
  130. InHiLite: boolean;
  131. Color: byte;
  132. begin
  133. NS:=''; InHiLite:=false;
  134. I:=1;
  135. while (I<=length(S)) do
  136. begin
  137. case S[I] of
  138. '^' : begin
  139. Inc(I);
  140. case S[I] of
  141. '^' : NS:=NS+'^';
  142. 'A'..'Z',
  143. 'a'..'z' :
  144. begin
  145. if InHiLite then
  146. NS:=NS+hscNormText
  147. else
  148. if NGGetAttrColor(S[I],Color) then
  149. NS:=NS+hscTextColor+chr(Color);
  150. InHiLite:=not InHiLite;
  151. end;
  152. else
  153. NS:=NS;
  154. end;
  155. end;
  156. else NS:=NS+S[I];
  157. end;
  158. Inc(I);
  159. end;
  160. if InHiLite then NS:=NS+hscNormText;
  161. TranslateStr:=NS;
  162. end;
  163. procedure TranslateLines(P: PUnsortedStringCollection);
  164. var S: string;
  165. I: sw_integer;
  166. begin
  167. for I:=0 to P^.Count-1 do
  168. begin
  169. S:=GetStr(P^.At(I));
  170. P^.AtFree(I);
  171. P^.AtInsert(I,NewStr(TranslateStr(S)));
  172. end;
  173. end;
  174. constructor TNGHelpFile.Init(AFileName: string; AID: word);
  175. var OK: boolean;
  176. begin
  177. if inherited Init(AID)=false then Fail;
  178. F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  179. OK:=F<>nil;
  180. if OK then OK:=(F^.Status=stOK);
  181. if OK then
  182. begin
  183. OK:=ReadHeader;
  184. if OK then
  185. FirstRecordPos:=F^.GetPos;
  186. end;
  187. if OK=false then
  188. begin
  189. Done;
  190. Fail;
  191. end;
  192. end;
  193. function TNGHelpFile.ReadHeader: boolean;
  194. var OK: boolean;
  195. begin
  196. F^.Read(Header,sizeof(Header));
  197. OK:=(F^.Status=stOK);
  198. OK:=OK and (Header.Signature=NGFileSignature);
  199. ReadHeader:=OK;
  200. end;
  201. function TNGHelpFile.ReadContainer(EnumProc: pointer): boolean;
  202. var OK: boolean;
  203. R: TRecord;
  204. I: longint;
  205. P: pointer;
  206. CIR: TContainerItemRec;
  207. begin
  208. OK:=ReadRecord(R, true);
  209. if OK then
  210. with TNGContainerRecord(R.Data^) do
  211. begin
  212. I:=0;
  213. while (I<ItemCount) do
  214. with Items[I] do
  215. begin
  216. P:=@(PByteArray(R.Data)^[NGMinRecordSize-sizeof(TNGRecordHeader)+EntryNameOfs]);
  217. FillChar(CIR,sizeof(CIR),0);
  218. with CIR do
  219. begin
  220. Container:=R.Data;
  221. Name:=NGDecompressStr(StrPas(P));
  222. FilePos:=SubItemsOfs;
  223. end;
  224. CallPointerLocal(EnumProc,PreviousFramePointer,@CIR);
  225. Inc(I);
  226. end;
  227. end;
  228. DisposeRecord(R);
  229. ReadContainer:=OK;
  230. end;
  231. function TNGHelpFile.ReadTopicRec(LineEnumProc, LinkEnumProc: pointer): boolean;
  232. var OK: boolean;
  233. R: TRecord;
  234. I: sw_integer;
  235. LineP: pointer;
  236. S: string;
  237. ParamS: string;
  238. NextLinkOfsPtr,NextLinkNamePtr: pointer;
  239. SeeAlso: PNGSeeAlsoRec;
  240. LR: TLinkRec;
  241. begin
  242. OK:=ReadRecord(R, true);
  243. if OK then
  244. with TNGTopicRecord(R.Data^) do
  245. begin
  246. LineP:=@TopicLines;
  247. if Assigned(LineEnumProc) then
  248. for I:=1 to NumberOfLines do
  249. begin
  250. S:=StrPas(LineP);
  251. ParamS:=NGDecompressStr(S);
  252. CallPointerLocal(LineEnumProc,PreviousFramePointer,@ParamS);
  253. Inc(longint(LineP),length(S)+1);
  254. end;
  255. if Assigned(LinkEnumProc) and (SeeAlsoOfs>0) then
  256. begin
  257. SeeAlso:=@PByteArray(R.Data)^[NGMinRecordSize-sizeof(TNGRecordHeader)+SeeAlsoOfs];
  258. NextLinkOfsPtr:=@SeeAlso^.Entries;
  259. NextLinkNamePtr:=@PByteArray(NextLinkOfsPtr)^[SeeAlso^.EntryCount*4];
  260. for I:=1 to SeeAlso^.EntryCount do
  261. begin
  262. FillChar(LR,sizeof(LR),0);
  263. S:=StrPas(NextLinkNamePtr);
  264. LR.Name:=S;
  265. Move(NextLinkOfsPtr^,LR.FilePos,4);
  266. CallPointerLocal(LinkEnumProc,PreviousFramePointer,@LR);
  267. Inc(longint(NextLinkNamePtr),length(S)+1);
  268. Inc(longint(NextLinkOfsPtr),4);
  269. end;
  270. end;
  271. end;
  272. DisposeRecord(R);
  273. ReadTopicRec:=OK;
  274. end;
  275. function TNGHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  276. var OK: boolean;
  277. H: TNGRecordHeader;
  278. I: sw_integer;
  279. begin
  280. FillChar(R, SizeOf(R), 0);
  281. F^.Read(H,SizeOf(H));
  282. OK:=F^.Status=stOK;
  283. if OK then
  284. for I:=0 to SizeOf(H)-1 do
  285. PByteArray(@H)^[I]:=PByteArray(@H)^[I] xor NGXORByte;
  286. if OK then
  287. begin
  288. R.SClass:=H.RecType; R.Size:=H.RecLength+(NGMinRecordSize-sizeof(TNGRecordHeader));
  289. if (R.Size>0) and ReadData then
  290. begin
  291. GetMem(R.Data,R.Size);
  292. F^.Read(R.Data^,R.Size);
  293. if R.Size>0 then
  294. for I:=0 to R.Size-1 do
  295. PByteArray(R.Data)^[I]:=PByteArray(R.Data)^[I] xor NGXORByte;
  296. OK:=F^.Status=stOK;
  297. end;
  298. if OK=false then DisposeRecord(R);
  299. end;
  300. ReadRecord:=OK;
  301. end;
  302. function TNGHelpFile.LoadIndex: boolean;
  303. {function FormatAlias(Alias: string): string;
  304. var StartP,EndP: sw_integer;
  305. begin
  306. repeat
  307. StartP:=Pos(' ',Alias);
  308. if StartP>0 then
  309. begin
  310. EndP:=StartP;
  311. while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
  312. Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
  313. end;
  314. until StartP=0;
  315. if Assigned(HelpFacility) then
  316. if length(Alias)>HelpFacility^.IndexTabSize-4 then
  317. Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
  318. FormatAlias:=Alias;
  319. end;}
  320. procedure AddToIndex(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
  321. var S: string;
  322. begin
  323. S:=Trim(P^.Name);
  324. S:=TranslateStr(S);
  325. S:=Trim({FormatAlias}(S));
  326. if (S<>'') and (P^.FilePos<>-1) then
  327. begin
  328. { Inc(NextHelpCtx);}
  329. AddIndexEntry(S,P^.FilePos);
  330. AddTopic(P^.FilePos,P^.FilePos,'',nil,0);
  331. end;
  332. end;
  333. var OK: boolean;
  334. FS: longint;
  335. R: TRecord;
  336. L: longint;
  337. begin
  338. if IndexLoaded then OK:=true else
  339. begin
  340. FS:=F^.GetSize;
  341. OK:=FirstRecordPos<>0;
  342. while OK do
  343. begin
  344. L:=F^.GetPos;
  345. if (L>=FS) then Break;
  346. OK:=ReadRecord(R,false);
  347. if (OK=false) then Break;
  348. case R.SClass of
  349. ng_rtContainer : begin F^.Seek(L); OK:=ReadContainer(@AddToIndex); end;
  350. ng_rtTopic : ;
  351. else
  352. begin
  353. {$ifdef DEBUGMSG}
  354. ClearFormatParams;
  355. AddFormatParamInt(R.SClass);
  356. AddFormatParamInt(L);
  357. AddFormatParamInt(R.Size);
  358. ErrorBox('Uknown help record tag %x encountered, '+
  359. 'offset %x, size %d',@FormatParams);
  360. {$else}
  361. {Skip};
  362. {$endif}
  363. end;
  364. end;
  365. if OK then
  366. begin
  367. Inc(L, sizeof(TNGRecordHeader)+R.Size); F^.Seek(L);
  368. OK:=(F^.Status=stOK);
  369. end;
  370. end;
  371. IndexLoaded:=OK;
  372. end;
  373. LoadIndex:=OK;
  374. end;
  375. function TNGHelpFile.ReadTopic(T: PTopic): boolean;
  376. function ExtractStr(var Buf; BufSize: word): string;
  377. var S: string;
  378. I,Size: integer;
  379. StartP,EndP: sw_integer;
  380. begin
  381. S:='';
  382. Size:=BufSize;
  383. while (PByteArray(@Buf)^[Size-1]=0) and (Size>0) do
  384. Dec(Size);
  385. S:=MemToStr(Buf,Size);
  386. S:=NGDecompressStr(S);
  387. for I:=1 to length(S) do
  388. if S[I]=#0 then S[I]:=#32;
  389. { kill long spaces }
  390. repeat
  391. StartP:=Pos(' ',S);
  392. if StartP>0 then
  393. begin
  394. EndP:=StartP;
  395. while (EndP+1<=length(S)) and (S[EndP+1]=' ') do Inc(EndP);
  396. S:=copy(S,1,StartP-1)+hscLineBreak+copy(S,EndP+1,High(S));
  397. end;
  398. until StartP=0;
  399. S:=Trim(S);
  400. ExtractStr:=S;
  401. end;
  402. var Lines: PUnsortedStringCollection;
  403. procedure AddLine(const S: string);
  404. begin
  405. Lines^.InsertStr(S);
  406. end;
  407. procedure AddToTopic(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
  408. begin
  409. AddLine(hscLink+Trim(P^.Name)+hscLink);
  410. AddLinkToTopic(T,ID,P^.FilePos);
  411. end;
  412. procedure AddTopicLine(P: PString); {$ifndef FPC}far;{$endif}
  413. begin
  414. AddLine(' '+GetStr(P));
  415. end;
  416. var LinkCount: sw_integer;
  417. procedure AddLink(P: PLinkRec); {$ifndef FPC}far;{$endif}
  418. begin
  419. Inc(LinkCount);
  420. if LinkCount=1 then
  421. begin
  422. AddLine('');
  423. AddLine(' See also :');
  424. end;
  425. AddLine(' '+hscLink+Trim(P^.Name)+hscLink);
  426. AddLinkToTopic(T,ID,P^.FilePos);
  427. end;
  428. var OK: boolean;
  429. R: TRecord;
  430. begin
  431. LinkCount:=0;
  432. New(Lines, Init(100,100));
  433. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  434. if OK then OK:=ReadRecord(R,false);
  435. case R.SClass of
  436. ng_rtContainer :
  437. begin
  438. F^.Seek(T^.FileOfs);
  439. AddLine('');
  440. OK:=ReadContainer(@AddToTopic);
  441. RenderTopic(Lines,T);
  442. end;
  443. ng_rtTopic :
  444. begin
  445. F^.Seek(T^.FileOfs);
  446. AddLine('');
  447. OK:=ReadTopicRec(@AddTopicLine,@AddLink);
  448. TranslateLines(Lines);
  449. AddLine('');
  450. { include copyright info }
  451. { AddLine(CharStr('Ä',80));
  452. AddLine(ExtractStr(Header.GuideName,sizeof(Header.GuideName)));
  453. AddLine(ExtractStr(Header.Credits,sizeof(Header.Credits)));}
  454. RenderTopic(Lines,T);
  455. end;
  456. else OK:=false;
  457. end;
  458. Dispose(Lines, Done);
  459. ReadTopic:=OK;
  460. end;
  461. destructor TNGHelpFile.Done;
  462. begin
  463. if Assigned(F) then Dispose(F, Done); F:=nil;
  464. inherited Done;
  465. end;
  466. END.
  467. {
  468. $Log$
  469. Revision 1.2 2000-10-31 22:35:56 pierre
  470. * New big merge from fixes branch
  471. Revision 1.1.2.1 2000/09/18 13:20:56 pierre
  472. New bunch of Gabor changes
  473. Revision 1.1 2000/07/13 09:48:37 michael
  474. + Initial import
  475. Revision 1.3 2000/07/03 08:54:54 pierre
  476. * Some enhancements for WinHelp support by G abor
  477. Revision 1.2 2000/06/26 07:29:23 pierre
  478. * new bunch of Gabor's changes
  479. Revision 1.1 2000/06/22 09:07:15 pierre
  480. * Gabor changes: see fixes.txt
  481. }