woahelp.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. {
  2. $Id$
  3. This file is part of the Free Pascal Integrated Development Environment
  4. Copyright (c) 2000 by Berczi Gabor
  5. Borland OA .HLP reader objects and routines
  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 WOAHelp;
  14. interface
  15. uses Objects,WUtils,WHelp;
  16. const
  17. MinFormatVersion = $04; { was $34 }
  18. TP55FormatVersion = $04;
  19. TP70FormatVersion = $34;
  20. Signature = '$*$* &&&&$*$'#0;
  21. ncRawChar = $F;
  22. ncRepChar = $E;
  23. oa_rtFileHeader = Byte ($0);
  24. oa_rtContext = Byte ($1);
  25. oa_rtText = Byte ($2);
  26. oa_rtKeyWord = Byte ($3);
  27. oa_rtIndex = Byte ($4);
  28. oa_rtCompression = Byte ($5);
  29. oa_rtIndexTags = Byte ($6);
  30. ctNone = $00;
  31. ctNibble = $02;
  32. type
  33. FileStamp = array [0..32] of char; {+ null terminator + $1A }
  34. FileSignature = array [0..12] of char; {+ null terminator }
  35. THLPVersion = packed record
  36. FormatVersion : byte;
  37. TextVersion : byte;
  38. end;
  39. THLPRecordHeader = packed record
  40. RecType : byte; {TPRecType}
  41. RecLength : word;
  42. end;
  43. THLPContextPos = packed record
  44. LoW: word;
  45. HiB: byte;
  46. end;
  47. THLPContexts = packed record
  48. ContextCount : word;
  49. Contexts : array[0..0] of THLPContextPos;
  50. end;
  51. THLPFileHeader = packed record
  52. Options : word;
  53. MainIndexScreen : word;
  54. MaxScreenSize : word;
  55. Height : byte;
  56. Width : byte;
  57. LeftMargin : byte;
  58. end;
  59. THLPCompression = packed record
  60. CompType : byte;
  61. CharTable : array [0..13] of byte;
  62. end;
  63. THLPIndexDescriptor = packed record
  64. LengthCode : byte;
  65. UniqueChars : array [0..0] of byte;
  66. Context : word;
  67. end;
  68. THLPIndexTable = packed record
  69. IndexCount : word;
  70. Entries : record end;
  71. end;
  72. THLPKeywordDescriptor = packed record
  73. KwContext : word;
  74. end;
  75. THLPKeyWordRecord = packed record
  76. UpContext : word;
  77. DownContext : word;
  78. KeyWordCount : word;
  79. Keywords : array[0..0] of THLPKeywordDescriptor;
  80. end;
  81. THLPKeywordDescriptor55 = packed record
  82. PosY : byte;
  83. StartX : byte;
  84. EndX : byte;
  85. Dunno : array[0..1] of word;
  86. KwContext : word;
  87. end;
  88. THLPKeyWordRecord55 = packed record
  89. UpContext : word;
  90. DownContext : word;
  91. KeyWordCount : byte;
  92. Keywords : array[0..0] of THLPKeywordDescriptor55;
  93. end;
  94. POAHelpFile = ^TOAHelpFile;
  95. TOAHelpFile = object(THelpFile)
  96. Version : THLPVersion;
  97. Header : THLPFileHeader;
  98. Compression : THLPCompression;
  99. constructor Init(AFileName: string; AID: word);
  100. destructor Done; virtual;
  101. public
  102. function LoadIndex: boolean; virtual;
  103. function ReadTopic(T: PTopic): boolean; virtual;
  104. public { protected }
  105. F: PStream;
  106. TopicsRead : boolean;
  107. IndexTableRead : boolean;
  108. CompressionRead: boolean;
  109. IndexTagsRead : boolean;
  110. IndexTagsPos : longint;
  111. IndexTablePos : longint;
  112. function ReadHeader: boolean;
  113. function ReadTopics: boolean;
  114. function ReadIndexTable: boolean;
  115. function ReadCompression: boolean;
  116. function ReadIndexTags: boolean;
  117. function ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  118. end;
  119. procedure RegisterHelpType;
  120. implementation
  121. constructor TOAHelpFile.Init(AFileName: string; AID: word);
  122. var OK: boolean;
  123. FS,L: longint;
  124. R: TRecord;
  125. begin
  126. if inherited Init(AID)=false then Fail;
  127. F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  128. OK:=F<>nil;
  129. if OK then OK:=(F^.Status=stOK);
  130. if OK then
  131. begin
  132. FS:=F^.GetSize;
  133. OK:=ReadHeader;
  134. end;
  135. while OK do
  136. begin
  137. L:=F^.GetPos;
  138. if (L>=FS) then Break;
  139. OK:=ReadRecord(R,false);
  140. if (OK=false) or (R.SClass=0) or (R.Size=0) then Break;
  141. case R.SClass of
  142. oa_rtContext : begin F^.Seek(L); OK:=ReadTopics; end;
  143. oa_rtText : {Skip};
  144. oa_rtKeyword : {Skip};
  145. oa_rtIndex : begin IndexTablePos:=L; {OK:=ReadIndexTable; }end;
  146. oa_rtCompression : begin F^.Seek(L); OK:=ReadCompression; end;
  147. oa_rtIndexTags : begin IndexTagsPos:=L; {OK:=ReadIndexTags; }end;
  148. else
  149. begin
  150. {$ifdef DEBUGMSG}
  151. ClearFormatParams;
  152. AddFormatParamInt(R.SClass);
  153. AddFormatParamInt(L);
  154. AddFormatParamInt(R.Size);
  155. ErrorBox('Uknown help record tag %x encountered, '+
  156. 'offset %x, size %d',@FormatParams);
  157. {$else}
  158. {Skip};
  159. {$endif}
  160. end;
  161. end;
  162. if OK then
  163. begin Inc(L, SizeOf(THLPRecordHeader)); Inc(L, R.Size); F^.Seek(L); OK:=(F^.Status=stOK); end
  164. end;
  165. OK:=OK and (TopicsRead=true);
  166. if OK=false then
  167. Begin
  168. Done;
  169. Fail;
  170. End;
  171. end;
  172. function TOAHelpFile.LoadIndex: boolean;
  173. begin
  174. LoadIndex:=ReadIndexTable;
  175. end;
  176. function TOAHelpFile.ReadHeader: boolean;
  177. var S: string;
  178. P: longint;
  179. R: TRecord;
  180. OK: boolean;
  181. begin
  182. F^.Seek(0);
  183. F^.Read(S[1],128); S[0]:=#255;
  184. OK:=(F^.Status=stOK); P:=Pos(Signature,S);
  185. OK:=OK and (P>0);
  186. if OK then
  187. begin
  188. F^.Seek(P+length(Signature)-1);
  189. F^.Read(Version,SizeOf(Version));
  190. OK:=(F^.Status=stOK) and (Version.FormatVersion>=MinFormatVersion);
  191. if OK then
  192. begin
  193. OK:=ReadRecord(R,true);
  194. OK:=OK and (R.SClass=oa_rtFileHeader) and (R.Size=SizeOf(Header));
  195. if OK then Move(R.Data^,Header,SizeOf(Header));
  196. DisposeRecord(R);
  197. end;
  198. end;
  199. ReadHeader:=OK;
  200. end;
  201. function TOAHelpFile.ReadTopics: boolean;
  202. var OK: boolean;
  203. R: TRecord;
  204. L,I: longint;
  205. function GetCtxPos(C: THLPContextPos): longint;
  206. begin
  207. GetCtxPos:=longint(C.HiB) shl 16 + C.LoW;
  208. end;
  209. begin
  210. OK:=ReadRecord(R, true);
  211. if OK then
  212. with THLPContexts(R.Data^) do
  213. for I:=1 to longint(ContextCount)-1 do
  214. begin
  215. if Topics^.Count=MaxCollectionSize then Break;
  216. L:=GetCtxPos(Contexts[I]);
  217. if (L and $800000)<>0 then L:=not L;
  218. if (L=-1) and (Header.MainIndexScreen>0) then
  219. L:=GetCtxPos(Contexts[Header.MainIndexScreen]);
  220. if (L>0) then
  221. AddTopic(I,L,'',nil,0);
  222. end;
  223. DisposeRecord(R);
  224. TopicsRead:=OK;
  225. ReadTopics:=OK;
  226. end;
  227. function TOAHelpFile.ReadIndexTable: boolean;
  228. var OK: boolean;
  229. R: TRecord;
  230. I: longint;
  231. LastTag,S: string;
  232. CurPtr: sw_word;
  233. HelpCtx: THelpCtx;
  234. LenCode,CopyCnt,AddLen: byte;
  235. type pword = ^word;
  236. begin
  237. if IndexTableRead then OK:=true else
  238. begin
  239. LastTag:=''; CurPtr:=0;
  240. OK:=(IndexTablePos<>0);
  241. if OK then begin F^.Seek(IndexTablePos); OK:=F^.Status=stOK; end;
  242. if OK then OK:=ReadRecord(R, true);
  243. if OK then
  244. with THLPIndexTable(R.Data^) do
  245. for I:=0 to IndexCount-1 do
  246. begin
  247. LenCode:=PByteArray(@Entries)^[CurPtr];
  248. AddLen:=LenCode and $1f; CopyCnt:=LenCode shr 5;
  249. S[0]:=chr(AddLen); Move(PByteArray(@Entries)^[CurPtr+1],S[1],AddLen);
  250. LastTag:=copy(LastTag,1,CopyCnt)+S;
  251. HelpCtx:=PWord(@PByteArray(@Entries)^[CurPtr+1+AddLen])^;
  252. AddIndexEntry(LastTag,HelpCtx);
  253. Inc(CurPtr,1+AddLen+2);
  254. end;
  255. DisposeRecord(R);
  256. IndexTableRead:=OK;
  257. end;
  258. ReadIndexTable:=OK;
  259. end;
  260. function TOAHelpFile.ReadCompression: boolean;
  261. var OK: boolean;
  262. R: TRecord;
  263. begin
  264. OK:=ReadRecord(R, true);
  265. OK:=OK and (R.Size=SizeOf(THLPCompression));
  266. if OK then Move(R.Data^,Compression,SizeOf(Compression));
  267. DisposeRecord(R);
  268. CompressionRead:=OK;
  269. ReadCompression:=OK;
  270. end;
  271. function TOAHelpFile.ReadIndexTags: boolean;
  272. var OK: boolean;
  273. begin
  274. OK:={ReadRecord(R, true)}true;
  275. IndexTagsRead:=OK;
  276. ReadIndexTags:=OK;
  277. end;
  278. function TOAHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  279. var OK: boolean;
  280. H: THLPRecordHeader;
  281. begin
  282. FillChar(R, SizeOf(R), 0);
  283. F^.Read(H,SizeOf(H));
  284. OK:=F^.Status=stOK;
  285. if OK then
  286. begin
  287. R.SClass:=H.RecType; R.Size:=H.RecLength;
  288. if (R.Size>0) and ReadData then
  289. begin
  290. GetMem(R.Data,R.Size);
  291. F^.Read(R.Data^,R.Size);
  292. OK:=F^.Status=stOK;
  293. end;
  294. if OK=false then DisposeRecord(R);
  295. end;
  296. ReadRecord:=OK;
  297. end;
  298. function TOAHelpFile.ReadTopic(T: PTopic): boolean;
  299. var SrcPtr,DestPtr,TopicSize: sw_word;
  300. NewR: TRecord;
  301. LinkPosCount: integer;
  302. LinkPos: array[1..50] of TRect;
  303. function IsLinkPosStart(X,Y: integer): boolean;
  304. var OK: boolean;
  305. I: integer;
  306. begin
  307. OK:=false;
  308. for I:=1 to LinkPosCount do
  309. with LinkPos[I] do
  310. if (A.X=X) and (A.Y=Y) then
  311. begin
  312. OK:=true;
  313. Break;
  314. end;
  315. IsLinkPosStart:=OK;
  316. end;
  317. function IsLinkPosEnd(X,Y: integer): boolean;
  318. var OK: boolean;
  319. I: integer;
  320. begin
  321. OK:=false;
  322. for I:=1 to LinkPosCount do
  323. with LinkPos[I] do
  324. if (B.X=X) and (B.Y=Y) then
  325. begin
  326. OK:=true;
  327. Break;
  328. end;
  329. IsLinkPosEnd:=OK;
  330. end;
  331. function ExtractTextRec(var R: TRecord): boolean;
  332. function GetNextNibble: byte;
  333. var B,N: byte;
  334. begin
  335. B:=PByteArray(R.Data)^[SrcPtr div 2];
  336. N:=( B and ($0f shl (4*(SrcPtr mod 2))) ) shr (4*(SrcPtr mod 2));
  337. Inc(SrcPtr);
  338. GetNextNibble:=N;
  339. end;
  340. procedure RealAddChar(C: char);
  341. begin
  342. if Assigned(NewR.Data) then
  343. PByteArray(NewR.Data)^[DestPtr]:=ord(C);
  344. Inc(DestPtr);
  345. end;
  346. var CurX,CurY: integer;
  347. InLink: boolean;
  348. procedure AddChar(C: char);
  349. begin
  350. if IsLinkPosStart(CurX+2,CurY) then
  351. begin
  352. RealAddChar(hscLink);
  353. InLink:=true;
  354. end
  355. else
  356. if (C=hscLineBreak) and (InLink) then
  357. begin
  358. RealAddChar(hscLink);
  359. InLink:=false;
  360. end;
  361. RealAddChar(C);
  362. if IsLinkPosEnd(CurX+2,CurY) then
  363. begin
  364. RealAddChar(hscLink);
  365. InLink:=false;
  366. end;
  367. if C<>hscLineBreak then
  368. Inc(CurX)
  369. else
  370. begin
  371. CurX:=0;
  372. Inc(CurY);
  373. end;
  374. end;
  375. var OK: boolean;
  376. C: char;
  377. P: pointer;
  378. function GetNextChar: char;
  379. var C: char;
  380. I,N,Cnt: byte;
  381. begin
  382. N:=GetNextNibble;
  383. case N of
  384. $00 : C:=#0;
  385. $01..$0D : C:=chr(Compression.CharTable[N]);
  386. ncRawChar : begin
  387. I:=GetNextNibble;
  388. C:=chr(I+GetNextNibble shl 4);
  389. end;
  390. ncRepChar : begin
  391. Cnt:=2+GetNextNibble;
  392. C:=GetNextChar{$ifdef FPC}(){$endif};
  393. for I:=1 to Cnt-1 do AddChar(C);
  394. end;
  395. end;
  396. GetNextChar:=C;
  397. end;
  398. begin
  399. OK:=Compression.CompType in[ctNone,ctNibble];
  400. if OK then
  401. case Compression.CompType of
  402. ctNone : ;
  403. ctNibble :
  404. begin
  405. CurX:=0; CurY:=0; InLink:=false;
  406. NewR.SClass:=0;
  407. NewR.Size:=0;
  408. NewR.Data:=nil;
  409. SrcPtr:=0; DestPtr:=0;
  410. while SrcPtr<(R.Size*2) do
  411. begin
  412. C:=GetNextChar;
  413. AddChar(C);
  414. end;
  415. if InLink then AddChar(hscLineBreak);
  416. TopicSize:=DestPtr;
  417. CurX:=0; CurY:=0; InLink:=false;
  418. NewR.SClass:=R.SClass;
  419. NewR.Size:=Min(MaxHelpTopicSize,TopicSize);
  420. GetMem(NewR.Data, NewR.Size);
  421. SrcPtr:=0; DestPtr:=0;
  422. while SrcPtr<(R.Size*2) do
  423. begin
  424. C:=GetNextChar;
  425. AddChar(C);
  426. end;
  427. if InLink then AddChar(hscLineBreak);
  428. DisposeRecord(R); R:=NewR;
  429. if (R.Size>DestPtr) then
  430. begin
  431. P:=R.Data; GetMem(R.Data,DestPtr);
  432. Move(P^,R.Data^,DestPtr); FreeMem(P,R.Size); R.Size:=DestPtr;
  433. end;
  434. end;
  435. else OK:=false;
  436. end;
  437. ExtractTextRec:=OK;
  438. end;
  439. var OK: boolean;
  440. TextR,KeyWR: TRecord;
  441. I: sw_word;
  442. begin
  443. OK:=T<>nil;
  444. if OK and (T^.Text=nil) then
  445. begin
  446. LinkPosCount:=0; FillChar(LinkPos,Sizeof(LinkPos),0);
  447. FillChar(TextR,SizeOf(TextR),0); FillChar(KeyWR,SizeOf(KeyWR),0);
  448. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  449. if OK then OK:=ReadRecord(TextR,true);
  450. OK:=OK and (TextR.SClass=oa_rtText);
  451. if OK then OK:=ReadRecord(KeyWR,true);
  452. OK:=OK and (KeyWR.SClass=oa_rtKeyword);
  453. if OK then
  454. begin
  455. case Version.FormatVersion of
  456. TP55FormatVersion :
  457. with THLPKeywordRecord55(KeyWR.Data^) do
  458. begin
  459. T^.LinkCount:=KeywordCount;
  460. GetMem(T^.Links,T^.LinkSize);
  461. if T^.LinkCount>0 then
  462. for I:=0 to T^.LinkCount-1 do
  463. with Keywords[I] do
  464. begin
  465. T^.Links^[I].Context:=KwContext;
  466. T^.Links^[I].FileID:=ID;
  467. Inc(LinkPosCount);
  468. with LinkPos[LinkPosCount] do
  469. begin
  470. A.Y:=PosY-1; B.Y:=PosY-1;
  471. A.X:=StartX-1; B.X:=EndX-1;
  472. end;
  473. end;
  474. end;
  475. else
  476. with THLPKeywordRecord(KeyWR.Data^) do
  477. begin
  478. T^.LinkCount:=KeywordCount;
  479. GetMem(T^.Links,T^.LinkSize);
  480. if KeywordCount>0 then
  481. for I:=0 to KeywordCount-1 do
  482. begin
  483. T^.Links^[I].Context:=Keywords[I].KwContext;
  484. T^.Links^[I].FileID:=ID;
  485. end;
  486. end;
  487. end;
  488. end;
  489. if OK then OK:=ExtractTextRec(TextR);
  490. if OK then
  491. if TextR.Size>0 then
  492. begin
  493. T^.Text:=TextR.Data; T^.TextSize:=TextR.Size;
  494. TextR.Data:=nil; TextR.Size:=0;
  495. end;
  496. DisposeRecord(TextR); DisposeRecord(KeyWR);
  497. end;
  498. ReadTopic:=OK;
  499. end;
  500. destructor TOAHelpFile.Done;
  501. begin
  502. if F<>nil then Dispose(F, Done);
  503. inherited Done;
  504. end;
  505. function CreateProc(const FileName,Param: string;Index : longint): PHelpFile; {$ifndef FPC}far;{$endif}
  506. begin
  507. CreateProc:=New(POAHelpFile, Init(FileName,Index));
  508. end;
  509. procedure RegisterHelpType;
  510. begin
  511. RegisterHelpFileType({$ifdef FPC}@{$endif}CreateProc);
  512. end;
  513. END.