wnghelp.pas 13 KB

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