wnghelp.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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. 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. PNGHelpFile = ^TNGHelpFile;
  76. TNGHelpFile = object(THelpFile)
  77. constructor Init(AFileName: string; AID: word);
  78. destructor Done; virtual;
  79. public
  80. function LoadIndex: boolean; virtual;
  81. function ReadTopic(T: PTopic): boolean; virtual;
  82. private
  83. F: PStream;
  84. Header: TNGFileHeader;
  85. { NextHelpCtx: longint;}
  86. function ReadHeader: boolean;
  87. function ReadContainer(EnumProc: pointer): boolean;
  88. function ReadTopicRec(Lines: PUnsortedStringCollection): boolean;
  89. function ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  90. end;
  91. implementation
  92. uses CallSpec;
  93. function NGDecompressStr(const S: string): string;
  94. var NS: string;
  95. I: sw_integer;
  96. begin
  97. NS:='';
  98. I:=1;
  99. while (I<=length(S)) do
  100. begin
  101. if S[I]=#255 then
  102. begin
  103. NS:=NS+CharStr(' ',ord(S[I+1]));
  104. Inc(I);
  105. end
  106. else
  107. NS:=NS+S[I];
  108. Inc(I);
  109. end;
  110. NGDecompressStr:=NS;
  111. end;
  112. function TranslateStr(const S: string): string;
  113. var NS: string;
  114. I: sw_integer;
  115. InHiLite: boolean;
  116. begin
  117. NS:=''; InHiLite:=false;
  118. I:=1;
  119. while (I<=length(S)) do
  120. begin
  121. case S[I] of
  122. '^' : begin
  123. Inc(I);
  124. case S[I] of
  125. '^' : NS:=NS+'^';
  126. 'B' : begin
  127. if InHiLite then
  128. NS:=NS+hscNormText
  129. else
  130. NS:=NS+hscTextColor+chr(15);
  131. InHiLite:=not InHiLite;
  132. end;
  133. 'b' : begin
  134. if InHiLite then
  135. NS:=NS+hscNormText
  136. else
  137. NS:=NS+hscTextColor+chr(11);
  138. InHiLite:=not InHiLite;
  139. end;
  140. 'U' : begin
  141. if InHiLite then
  142. NS:=NS+hscNormText
  143. else
  144. NS:=NS+hscTextColor+chr(3);
  145. InHiLite:=not InHiLite;
  146. end;
  147. else
  148. NS:=NS;
  149. end;
  150. end;
  151. else NS:=NS+S[I];
  152. end;
  153. Inc(I);
  154. end;
  155. if InHiLite then NS:=NS+hscNormText;
  156. TranslateStr:=NS;
  157. end;
  158. procedure TranslateLines(P: PUnsortedStringCollection);
  159. var S: string;
  160. I: sw_integer;
  161. begin
  162. for I:=0 to P^.Count-1 do
  163. begin
  164. S:=GetStr(P^.At(I));
  165. P^.AtFree(I);
  166. P^.AtInsert(I,NewStr(TranslateStr(S)));
  167. end;
  168. end;
  169. constructor TNGHelpFile.Init(AFileName: string; AID: word);
  170. function FormatAlias(Alias: string): string;
  171. var StartP,EndP: sw_integer;
  172. begin
  173. repeat
  174. StartP:=Pos(' ',Alias);
  175. if StartP>0 then
  176. begin
  177. EndP:=StartP;
  178. while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
  179. Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
  180. end;
  181. until StartP=0;
  182. if Assigned(HelpFacility) then
  183. if length(Alias)>HelpFacility^.IndexTabSize-4 then
  184. Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
  185. FormatAlias:=Alias;
  186. end;
  187. procedure AddToIndex(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
  188. var S: string;
  189. begin
  190. S:=Trim(P^.Name);
  191. S:=TranslateStr(S);
  192. S:=Trim(FormatAlias(S));
  193. if (S<>'') and (P^.FilePos<>-1) then
  194. begin
  195. { Inc(NextHelpCtx);}
  196. AddIndexEntry(S,P^.FilePos);
  197. AddTopic(P^.FilePos,P^.FilePos,'');
  198. end;
  199. end;
  200. var OK: boolean;
  201. FS: longint;
  202. R: TRecord;
  203. L: longint;
  204. begin
  205. if inherited Init(AID)=false then Fail;
  206. F:=New(PBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  207. OK:=F<>nil;
  208. if OK then OK:=(F^.Status=stOK);
  209. if OK then
  210. begin
  211. FS:=F^.GetSize;
  212. OK:=ReadHeader;
  213. end;
  214. while OK do
  215. begin
  216. L:=F^.GetPos;
  217. if (L>=FS) then Break;
  218. OK:=ReadRecord(R,false);
  219. if (OK=false) then Break;
  220. case R.SClass of
  221. ng_rtContainer : begin F^.Seek(L); OK:=ReadContainer(@AddToIndex); end;
  222. { ng_rtTopic : begin F^.Seek(L); OK:=ReadTopicRec; end;}
  223. else
  224. begin
  225. {$ifdef DEBUGMSG}
  226. ClearFormatParams;
  227. AddFormatParamInt(R.SClass);
  228. AddFormatParamInt(L);
  229. AddFormatParamInt(R.Size);
  230. ErrorBox('Uknown help record tag %x encountered, '+
  231. 'offset %x, size %d',@FormatParams);
  232. {$else}
  233. {Skip};
  234. {$endif}
  235. end;
  236. end;
  237. if OK then
  238. begin
  239. Inc(L, sizeof(TNGRecordHeader)+R.Size); F^.Seek(L);
  240. OK:=(F^.Status=stOK);
  241. end;
  242. end;
  243. if OK=false then
  244. begin
  245. Done;
  246. Fail;
  247. end;
  248. end;
  249. function TNGHelpFile.ReadHeader: boolean;
  250. var OK: boolean;
  251. begin
  252. F^.Read(Header,sizeof(Header));
  253. OK:=(F^.Status=stOK);
  254. OK:=OK and (Header.Signature=NGFileSignature);
  255. ReadHeader:=OK;
  256. end;
  257. function KillSpecChars(const S: string): string;
  258. var I: sw_integer;
  259. RS: string;
  260. begin
  261. RS:='';
  262. for I:=1 to length(S) do
  263. if S[I]>=#32 then
  264. RS:=RS+S[I];
  265. KillSpecChars:=RS;
  266. end;
  267. function TNGHelpFile.ReadContainer(EnumProc: pointer): boolean;
  268. var OK: boolean;
  269. R: TRecord;
  270. I,L: longint;
  271. CI: TNGContainerItem;
  272. P: pointer;
  273. CIR: TContainerItemRec;
  274. begin
  275. OK:=ReadRecord(R, true);
  276. if OK then
  277. with TNGContainerRecord(R.Data^) do
  278. begin
  279. I:=0;
  280. while (I<ItemCount) do
  281. with Items[I] do
  282. begin
  283. P:=@(PByteArray(R.Data)^[NGMinRecordSize-sizeof(TNGRecordHeader)+EntryNameOfs]);
  284. FillChar(CIR,sizeof(CIR),0);
  285. with CIR do
  286. begin
  287. Container:=R.Data;
  288. Name:=NGDecompressStr(StrPas(P));
  289. FilePos:=SubItemsOfs;
  290. end;
  291. CallPointerLocal(EnumProc,PreviousFramePointer,@CIR);
  292. Inc(I);
  293. end;
  294. end;
  295. DisposeRecord(R);
  296. ReadContainer:=OK;
  297. end;
  298. function TNGHelpFile.ReadTopicRec(Lines: PUnsortedStringCollection): boolean;
  299. var OK: boolean;
  300. R: TRecord;
  301. I: sw_integer;
  302. LineP: pointer;
  303. S: string;
  304. begin
  305. OK:=ReadRecord(R, true);
  306. if OK then
  307. with TNGTopicRecord(R.Data^) do
  308. begin
  309. LineP:=@TopicLines;
  310. for I:=1 to NumberOfLines do
  311. begin
  312. S:=StrPas(LineP);
  313. Lines^.InsertStr(NGDecompressStr(S));
  314. LineP:=pointer(longint(LineP)+length(S)+1);
  315. end;
  316. end;
  317. DisposeRecord(R);
  318. ReadTopicRec:=OK;
  319. end;
  320. function TNGHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  321. var OK: boolean;
  322. H: TNGRecordHeader;
  323. I: sw_integer;
  324. begin
  325. FillChar(R, SizeOf(R), 0);
  326. F^.Read(H,SizeOf(H));
  327. OK:=F^.Status=stOK;
  328. if OK then
  329. for I:=0 to SizeOf(H)-1 do
  330. PByteArray(@H)^[I]:=PByteArray(@H)^[I] xor NGXORByte;
  331. if OK then
  332. begin
  333. R.SClass:=H.RecType; R.Size:=H.RecLength+(NGMinRecordSize-sizeof(TNGRecordHeader));
  334. if (R.Size>0) and ReadData then
  335. begin
  336. GetMem(R.Data,R.Size);
  337. F^.Read(R.Data^,R.Size);
  338. if R.Size>0 then
  339. for I:=0 to R.Size-1 do
  340. PByteArray(R.Data)^[I]:=PByteArray(R.Data)^[I] xor NGXORByte;
  341. OK:=F^.Status=stOK;
  342. end;
  343. if OK=false then DisposeRecord(R);
  344. end;
  345. ReadRecord:=OK;
  346. end;
  347. function TNGHelpFile.LoadIndex: boolean;
  348. begin
  349. LoadIndex:=false;
  350. end;
  351. function TNGHelpFile.ReadTopic(T: PTopic): boolean;
  352. var Lines: PUnsortedStringCollection;
  353. procedure AddToTopic(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
  354. begin
  355. Lines^.InsertStr(hscLink+P^.Name+hscLink);
  356. AddLinkToTopic(T,ID,P^.FilePos);
  357. end;
  358. var OK: boolean;
  359. R: TRecord;
  360. begin
  361. New(Lines, Init(100,100));
  362. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  363. if OK then OK:=ReadRecord(R,false);
  364. case R.SClass of
  365. ng_rtContainer :
  366. begin
  367. F^.Seek(T^.FileOfs);
  368. Lines^.InsertStr(' ');
  369. OK:=ReadContainer(@AddToTopic);
  370. RenderTopic(Lines,T);
  371. end;
  372. ng_rtTopic :
  373. begin
  374. F^.Seek(T^.FileOfs);
  375. Lines^.InsertStr(' ');
  376. OK:=ReadTopicRec(Lines);
  377. TranslateLines(Lines);
  378. Lines^.InsertStr(' ');
  379. RenderTopic(Lines,T);
  380. end;
  381. else OK:=false;
  382. end;
  383. Dispose(Lines, Done);
  384. ReadTopic:=OK;
  385. end;
  386. destructor TNGHelpFile.Done;
  387. begin
  388. if Assigned(F) then Dispose(F, Done); F:=nil;
  389. inherited Done;
  390. end;
  391. END.
  392. {
  393. $Log$
  394. Revision 1.1 2000-06-22 09:07:15 pierre
  395. * Gabor changes: see fixes.txt
  396. }