wnghelp.pas 13 KB

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