wnghelp.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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 = {$ifdef fpc}@{$endif}DefNGGetAttrColor;
  102. procedure RegisterHelpType;
  103. implementation
  104. uses CallSpec;
  105. function DefNGGetAttrColor(Attr: char; var Color: byte): boolean;
  106. begin
  107. DefNGGetAttrColor:=false;
  108. end;
  109. function NGDecompressStr(const S: string): string;
  110. var NS: string;
  111. I: sw_integer;
  112. begin
  113. NS:='';
  114. I:=1;
  115. while (I<=length(S)) do
  116. begin
  117. if S[I]=#255 then
  118. begin
  119. NS:=NS+CharStr(' ',ord(S[I+1]));
  120. Inc(I);
  121. end
  122. else
  123. NS:=NS+S[I];
  124. Inc(I);
  125. end;
  126. NGDecompressStr:=NS;
  127. end;
  128. function TranslateStr(const S: string): string;
  129. var NS: string;
  130. I: sw_integer;
  131. InHiLite: boolean;
  132. Color: byte;
  133. begin
  134. NS:=''; InHiLite:=false;
  135. I:=1;
  136. while (I<=length(S)) do
  137. begin
  138. case S[I] of
  139. '^' : begin
  140. Inc(I);
  141. case S[I] of
  142. '^' : NS:=NS+'^';
  143. 'A'..'Z',
  144. 'a'..'z' :
  145. begin
  146. if InHiLite then
  147. NS:=NS+hscNormText
  148. else
  149. if NGGetAttrColor(S[I],Color) then
  150. NS:=NS+hscTextColor+chr(Color);
  151. InHiLite:=not InHiLite;
  152. end;
  153. else
  154. NS:=NS;
  155. end;
  156. end;
  157. else NS:=NS+S[I];
  158. end;
  159. Inc(I);
  160. end;
  161. if InHiLite then NS:=NS+hscNormText;
  162. TranslateStr:=NS;
  163. end;
  164. procedure TranslateLines(P: PUnsortedStringCollection);
  165. var S: string;
  166. I: sw_integer;
  167. begin
  168. for I:=0 to P^.Count-1 do
  169. begin
  170. S:=GetStr(P^.At(I));
  171. P^.AtFree(I);
  172. P^.AtInsert(I,NewStr(TranslateStr(S)));
  173. end;
  174. end;
  175. constructor TNGHelpFile.Init(AFileName: string; AID: word);
  176. var OK: boolean;
  177. begin
  178. if inherited Init(AID)=false then Fail;
  179. F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  180. OK:=F<>nil;
  181. if OK then OK:=(F^.Status=stOK);
  182. if OK then
  183. begin
  184. OK:=ReadHeader;
  185. if OK then
  186. FirstRecordPos:=F^.GetPos;
  187. end;
  188. if OK=false then
  189. begin
  190. Done;
  191. Fail;
  192. end;
  193. end;
  194. function TNGHelpFile.ReadHeader: boolean;
  195. var OK: boolean;
  196. begin
  197. F^.Read(Header,sizeof(Header));
  198. OK:=(F^.Status=stOK);
  199. OK:=OK and (Header.Signature=NGFileSignature);
  200. ReadHeader:=OK;
  201. end;
  202. function TNGHelpFile.ReadContainer(EnumProc: pointer): boolean;
  203. var OK: boolean;
  204. R: TRecord;
  205. I: longint;
  206. P: pointer;
  207. CIR: TContainerItemRec;
  208. begin
  209. OK:=ReadRecord(R, true);
  210. if OK then
  211. with TNGContainerRecord(R.Data^) do
  212. begin
  213. I:=0;
  214. while (I<ItemCount) do
  215. with Items[I] do
  216. begin
  217. P:=@(PByteArray(R.Data)^[NGMinRecordSize-sizeof(TNGRecordHeader)+EntryNameOfs]);
  218. FillChar(CIR,sizeof(CIR),0);
  219. with CIR do
  220. begin
  221. Container:=R.Data;
  222. Name:=NGDecompressStr(StrPas(P));
  223. FilePos:=SubItemsOfs;
  224. end;
  225. CallPointerLocal(EnumProc,PreviousFramePointer,@CIR);
  226. Inc(I);
  227. end;
  228. end;
  229. DisposeRecord(R);
  230. ReadContainer:=OK;
  231. end;
  232. function TNGHelpFile.ReadTopicRec(LineEnumProc, LinkEnumProc: pointer): boolean;
  233. var OK: boolean;
  234. R: TRecord;
  235. I: sw_integer;
  236. LineP: pointer;
  237. S: string;
  238. ParamS: string;
  239. NextLinkOfsPtr,NextLinkNamePtr: pointer;
  240. SeeAlso: PNGSeeAlsoRec;
  241. LR: TLinkRec;
  242. begin
  243. OK:=ReadRecord(R, true);
  244. if OK then
  245. with TNGTopicRecord(R.Data^) do
  246. begin
  247. LineP:=@TopicLines;
  248. if Assigned(LineEnumProc) then
  249. for I:=1 to NumberOfLines do
  250. begin
  251. S:=StrPas(LineP);
  252. ParamS:=NGDecompressStr(S);
  253. CallPointerLocal(LineEnumProc,PreviousFramePointer,@ParamS);
  254. Inc(longint(LineP),length(S)+1);
  255. end;
  256. if Assigned(LinkEnumProc) and (SeeAlsoOfs>0) then
  257. begin
  258. SeeAlso:=@PByteArray(R.Data)^[NGMinRecordSize-sizeof(TNGRecordHeader)+SeeAlsoOfs];
  259. NextLinkOfsPtr:=@SeeAlso^.Entries;
  260. NextLinkNamePtr:=@PByteArray(NextLinkOfsPtr)^[SeeAlso^.EntryCount*4];
  261. for I:=1 to SeeAlso^.EntryCount do
  262. begin
  263. FillChar(LR,sizeof(LR),0);
  264. S:=StrPas(NextLinkNamePtr);
  265. LR.Name:=S;
  266. Move(NextLinkOfsPtr^,LR.FilePos,4);
  267. CallPointerLocal(LinkEnumProc,PreviousFramePointer,@LR);
  268. Inc(longint(NextLinkNamePtr),length(S)+1);
  269. Inc(longint(NextLinkOfsPtr),4);
  270. end;
  271. end;
  272. end;
  273. DisposeRecord(R);
  274. ReadTopicRec:=OK;
  275. end;
  276. function TNGHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  277. var OK: boolean;
  278. H: TNGRecordHeader;
  279. I: sw_integer;
  280. begin
  281. FillChar(R, SizeOf(R), 0);
  282. F^.Read(H,SizeOf(H));
  283. OK:=F^.Status=stOK;
  284. if OK then
  285. for I:=0 to SizeOf(H)-1 do
  286. PByteArray(@H)^[I]:=PByteArray(@H)^[I] xor NGXORByte;
  287. if OK then
  288. begin
  289. R.SClass:=H.RecType; R.Size:=H.RecLength+(NGMinRecordSize-sizeof(TNGRecordHeader));
  290. if (R.Size>0) and ReadData then
  291. begin
  292. GetMem(R.Data,R.Size);
  293. F^.Read(R.Data^,R.Size);
  294. if R.Size>0 then
  295. for I:=0 to R.Size-1 do
  296. PByteArray(R.Data)^[I]:=PByteArray(R.Data)^[I] xor NGXORByte;
  297. OK:=F^.Status=stOK;
  298. end;
  299. if OK=false then DisposeRecord(R);
  300. end;
  301. ReadRecord:=OK;
  302. end;
  303. function TNGHelpFile.LoadIndex: boolean;
  304. {function FormatAlias(Alias: string): string;
  305. var StartP,EndP: sw_integer;
  306. begin
  307. repeat
  308. StartP:=Pos(' ',Alias);
  309. if StartP>0 then
  310. begin
  311. EndP:=StartP;
  312. while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
  313. Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
  314. end;
  315. until StartP=0;
  316. if Assigned(HelpFacility) then
  317. if length(Alias)>HelpFacility^.IndexTabSize-4 then
  318. Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
  319. FormatAlias:=Alias;
  320. end;}
  321. procedure AddToIndex(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
  322. var S: string;
  323. begin
  324. S:=Trim(P^.Name);
  325. S:=TranslateStr(S);
  326. S:=Trim({FormatAlias}(S));
  327. if (S<>'') and (P^.FilePos<>-1) then
  328. begin
  329. { Inc(NextHelpCtx);}
  330. AddIndexEntry(S,P^.FilePos);
  331. AddTopic(P^.FilePos,P^.FilePos,'',nil,0);
  332. end;
  333. end;
  334. var OK: boolean;
  335. FS: longint;
  336. R: TRecord;
  337. L: longint;
  338. begin
  339. if IndexLoaded then OK:=true else
  340. begin
  341. FS:=F^.GetSize;
  342. OK:=FirstRecordPos<>0;
  343. while OK do
  344. begin
  345. L:=F^.GetPos;
  346. if (L>=FS) then Break;
  347. OK:=ReadRecord(R,false);
  348. if (OK=false) then Break;
  349. case R.SClass of
  350. ng_rtContainer : begin F^.Seek(L); OK:=ReadContainer(@AddToIndex); end;
  351. ng_rtTopic : ;
  352. else
  353. begin
  354. {$ifdef DEBUGMSG}
  355. ClearFormatParams;
  356. AddFormatParamInt(R.SClass);
  357. AddFormatParamInt(L);
  358. AddFormatParamInt(R.Size);
  359. ErrorBox('Uknown help record tag %x encountered, '+
  360. 'offset %x, size %d',@FormatParams);
  361. {$else}
  362. {Skip};
  363. {$endif}
  364. end;
  365. end;
  366. if OK then
  367. begin
  368. Inc(L, sizeof(TNGRecordHeader)+R.Size); F^.Seek(L);
  369. OK:=(F^.Status=stOK);
  370. end;
  371. end;
  372. IndexLoaded:=OK;
  373. end;
  374. LoadIndex:=OK;
  375. end;
  376. function TNGHelpFile.ReadTopic(T: PTopic): boolean;
  377. function ExtractStr(var Buf; BufSize: word): string;
  378. var S: string;
  379. I,Size: integer;
  380. StartP,EndP: sw_integer;
  381. begin
  382. S:='';
  383. Size:=BufSize;
  384. while (PByteArray(@Buf)^[Size-1]=0) and (Size>0) do
  385. Dec(Size);
  386. S:=MemToStr(Buf,Size);
  387. S:=NGDecompressStr(S);
  388. for I:=1 to length(S) do
  389. if S[I]=#0 then S[I]:=#32;
  390. { kill long spaces }
  391. repeat
  392. StartP:=Pos(' ',S);
  393. if StartP>0 then
  394. begin
  395. EndP:=StartP;
  396. while (EndP+1<=length(S)) and (S[EndP+1]=' ') do Inc(EndP);
  397. S:=copy(S,1,StartP-1)+hscLineBreak+copy(S,EndP+1,High(S));
  398. end;
  399. until StartP=0;
  400. S:=Trim(S);
  401. ExtractStr:=S;
  402. end;
  403. var Lines: PUnsortedStringCollection;
  404. procedure AddLine(const S: string);
  405. begin
  406. Lines^.InsertStr(S);
  407. end;
  408. procedure AddToTopic(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
  409. begin
  410. AddLine(hscLink+Trim(P^.Name)+hscLink);
  411. AddLinkToTopic(T,ID,P^.FilePos);
  412. end;
  413. procedure AddTopicLine(P: PString); {$ifndef FPC}far;{$endif}
  414. begin
  415. AddLine(' '+GetStr(P));
  416. end;
  417. var LinkCount: sw_integer;
  418. procedure AddLink(P: PLinkRec); {$ifndef FPC}far;{$endif}
  419. begin
  420. Inc(LinkCount);
  421. if LinkCount=1 then
  422. begin
  423. AddLine('');
  424. AddLine(' See also :');
  425. end;
  426. AddLine(' '+hscLink+Trim(P^.Name)+hscLink);
  427. AddLinkToTopic(T,ID,P^.FilePos);
  428. end;
  429. var OK: boolean;
  430. R: TRecord;
  431. begin
  432. LinkCount:=0;
  433. New(Lines, Init(100,100));
  434. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  435. if OK then OK:=ReadRecord(R,false);
  436. case R.SClass of
  437. ng_rtContainer :
  438. begin
  439. F^.Seek(T^.FileOfs);
  440. AddLine('');
  441. OK:=ReadContainer(@AddToTopic);
  442. RenderTopic(Lines,T);
  443. end;
  444. ng_rtTopic :
  445. begin
  446. F^.Seek(T^.FileOfs);
  447. AddLine('');
  448. OK:=ReadTopicRec(@AddTopicLine,@AddLink);
  449. TranslateLines(Lines);
  450. AddLine('');
  451. { include copyright info }
  452. { AddLine(CharStr('Ä',80));
  453. AddLine(ExtractStr(Header.GuideName,sizeof(Header.GuideName)));
  454. AddLine(ExtractStr(Header.Credits,sizeof(Header.Credits)));}
  455. RenderTopic(Lines,T);
  456. end;
  457. else OK:=false;
  458. end;
  459. Dispose(Lines, Done);
  460. ReadTopic:=OK;
  461. end;
  462. destructor TNGHelpFile.Done;
  463. begin
  464. if Assigned(F) then Dispose(F, Done); F:=nil;
  465. inherited Done;
  466. end;
  467. function CreateProc(const FileName,Param: string;Index : longint): PHelpFile; {$ifndef FPC}far;{$endif}
  468. begin
  469. CreateProc:=New(PNGHelpFile, Init(FileName,Index));
  470. end;
  471. procedure RegisterHelpType;
  472. begin
  473. RegisterHelpFileType({$ifdef FPC}@{$endif}CreateProc);
  474. end;
  475. END.
  476. {
  477. $Log$
  478. Revision 1.3 2002-09-07 15:40:50 peter
  479. * old logs removed and tabs fixed
  480. }