woahelp.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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. FillChar(R, SizeOf(R), 0);
  240. LastTag:=''; CurPtr:=0;
  241. OK:=(IndexTablePos<>0);
  242. if OK then begin F^.Seek(IndexTablePos); OK:=F^.Status=stOK; end;
  243. if OK then OK:=ReadRecord(R, true);
  244. if OK then
  245. with THLPIndexTable(R.Data^) do
  246. for I:=0 to IndexCount-1 do
  247. begin
  248. LenCode:=PByteArray(@Entries)^[CurPtr];
  249. AddLen:=LenCode and $1f; CopyCnt:=LenCode shr 5;
  250. S[0]:=chr(AddLen); Move(PByteArray(@Entries)^[CurPtr+1],S[1],AddLen);
  251. LastTag:=copy(LastTag,1,CopyCnt)+S;
  252. HelpCtx:=PWord(@PByteArray(@Entries)^[CurPtr+1+AddLen])^;
  253. AddIndexEntry(LastTag,HelpCtx);
  254. Inc(CurPtr,1+AddLen+2);
  255. end;
  256. DisposeRecord(R);
  257. IndexTableRead:=OK;
  258. end;
  259. ReadIndexTable:=OK;
  260. end;
  261. function TOAHelpFile.ReadCompression: boolean;
  262. var OK: boolean;
  263. R: TRecord;
  264. begin
  265. OK:=ReadRecord(R, true);
  266. OK:=OK and (R.Size=SizeOf(THLPCompression));
  267. if OK then Move(R.Data^,Compression,SizeOf(Compression));
  268. DisposeRecord(R);
  269. CompressionRead:=OK;
  270. ReadCompression:=OK;
  271. end;
  272. function TOAHelpFile.ReadIndexTags: boolean;
  273. var OK: boolean;
  274. begin
  275. OK:={ReadRecord(R, true)}true;
  276. IndexTagsRead:=OK;
  277. ReadIndexTags:=OK;
  278. end;
  279. function TOAHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  280. var OK: boolean;
  281. H: THLPRecordHeader;
  282. begin
  283. FillChar(R, SizeOf(R), 0);
  284. F^.Read(H,SizeOf(H));
  285. OK:=F^.Status=stOK;
  286. if OK then
  287. begin
  288. R.SClass:=H.RecType; R.Size:=H.RecLength;
  289. if (R.Size>0) and ReadData then
  290. begin
  291. GetMem(R.Data,R.Size);
  292. F^.Read(R.Data^,R.Size);
  293. OK:=F^.Status=stOK;
  294. end;
  295. if OK=false then DisposeRecord(R);
  296. end;
  297. ReadRecord:=OK;
  298. end;
  299. function TOAHelpFile.ReadTopic(T: PTopic): boolean;
  300. var SrcPtr,DestPtr,TopicSize: sw_word;
  301. NewR: TRecord;
  302. LinkPosCount: integer;
  303. LinkPos: array[1..50] of TRect;
  304. function IsLinkPosStart(X,Y: integer): boolean;
  305. var OK: boolean;
  306. I: integer;
  307. begin
  308. OK:=false;
  309. for I:=1 to LinkPosCount do
  310. with LinkPos[I] do
  311. if (A.X=X) and (A.Y=Y) then
  312. begin
  313. OK:=true;
  314. Break;
  315. end;
  316. IsLinkPosStart:=OK;
  317. end;
  318. function IsLinkPosEnd(X,Y: integer): boolean;
  319. var OK: boolean;
  320. I: integer;
  321. begin
  322. OK:=false;
  323. for I:=1 to LinkPosCount do
  324. with LinkPos[I] do
  325. if (B.X=X) and (B.Y=Y) then
  326. begin
  327. OK:=true;
  328. Break;
  329. end;
  330. IsLinkPosEnd:=OK;
  331. end;
  332. function ExtractTextRec(var R: TRecord): boolean;
  333. function GetNextNibble: byte;
  334. var B,N: byte;
  335. begin
  336. B:=PByteArray(R.Data)^[SrcPtr div 2];
  337. N:=( B and ($0f shl (4*(SrcPtr mod 2))) ) shr (4*(SrcPtr mod 2));
  338. Inc(SrcPtr);
  339. GetNextNibble:=N;
  340. end;
  341. procedure RealAddChar(C: char);
  342. begin
  343. if Assigned(NewR.Data) then
  344. PByteArray(NewR.Data)^[DestPtr]:=ord(C);
  345. Inc(DestPtr);
  346. end;
  347. var CurX,CurY: integer;
  348. InLink: boolean;
  349. procedure AddChar(C: char);
  350. begin
  351. if IsLinkPosStart(CurX+2,CurY) then
  352. begin
  353. RealAddChar(hscLink);
  354. InLink:=true;
  355. end
  356. else
  357. if (C=hscLineBreak) and (InLink) then
  358. begin
  359. RealAddChar(hscLink);
  360. InLink:=false;
  361. end;
  362. RealAddChar(C);
  363. if IsLinkPosEnd(CurX+2,CurY) then
  364. begin
  365. RealAddChar(hscLink);
  366. InLink:=false;
  367. end;
  368. if C<>hscLineBreak then
  369. Inc(CurX)
  370. else
  371. begin
  372. CurX:=0;
  373. Inc(CurY);
  374. end;
  375. end;
  376. var OK: boolean;
  377. C: char;
  378. P: pointer;
  379. function GetNextChar: char;
  380. var C: char;
  381. I,N,Cnt: byte;
  382. begin
  383. N:=GetNextNibble;
  384. case N of
  385. $00 : C:=#0;
  386. $01..$0D : C:=chr(Compression.CharTable[N]);
  387. ncRawChar : begin
  388. I:=GetNextNibble;
  389. C:=chr(I+GetNextNibble shl 4);
  390. end;
  391. ncRepChar : begin
  392. Cnt:=2+GetNextNibble;
  393. C:=GetNextChar{$ifdef FPC}(){$endif};
  394. for I:=1 to Cnt-1 do AddChar(C);
  395. end;
  396. end;
  397. GetNextChar:=C;
  398. end;
  399. begin
  400. OK:=Compression.CompType in[ctNone,ctNibble];
  401. if OK then
  402. case Compression.CompType of
  403. ctNone : ;
  404. ctNibble :
  405. begin
  406. CurX:=0; CurY:=0; InLink:=false;
  407. NewR.SClass:=0;
  408. NewR.Size:=0;
  409. NewR.Data:=nil;
  410. SrcPtr:=0; DestPtr:=0;
  411. while SrcPtr<(R.Size*2) do
  412. begin
  413. C:=GetNextChar;
  414. AddChar(C);
  415. end;
  416. if InLink then AddChar(hscLineBreak);
  417. TopicSize:=DestPtr;
  418. CurX:=0; CurY:=0; InLink:=false;
  419. NewR.SClass:=R.SClass;
  420. NewR.Size:=Min(MaxHelpTopicSize,TopicSize);
  421. GetMem(NewR.Data, NewR.Size);
  422. SrcPtr:=0; DestPtr:=0;
  423. while SrcPtr<(R.Size*2) do
  424. begin
  425. C:=GetNextChar;
  426. AddChar(C);
  427. end;
  428. if InLink then AddChar(hscLineBreak);
  429. DisposeRecord(R); R:=NewR;
  430. if (R.Size>DestPtr) then
  431. begin
  432. P:=R.Data; GetMem(R.Data,DestPtr);
  433. Move(P^,R.Data^,DestPtr); FreeMem(P,R.Size); R.Size:=DestPtr;
  434. end;
  435. end;
  436. else OK:=false;
  437. end;
  438. ExtractTextRec:=OK;
  439. end;
  440. var OK: boolean;
  441. TextR,KeyWR: TRecord;
  442. I: sw_word;
  443. begin
  444. OK:=T<>nil;
  445. if OK and (T^.Text=nil) then
  446. begin
  447. LinkPosCount:=0; FillChar(LinkPos,Sizeof(LinkPos),0);
  448. FillChar(TextR,SizeOf(TextR),0); FillChar(KeyWR,SizeOf(KeyWR),0);
  449. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  450. if OK then OK:=ReadRecord(TextR,true);
  451. OK:=OK and (TextR.SClass=oa_rtText);
  452. if OK then OK:=ReadRecord(KeyWR,true);
  453. OK:=OK and (KeyWR.SClass=oa_rtKeyword);
  454. if OK then
  455. begin
  456. case Version.FormatVersion of
  457. TP55FormatVersion :
  458. with THLPKeywordRecord55(KeyWR.Data^) do
  459. begin
  460. T^.LinkCount:=KeywordCount;
  461. GetMem(T^.Links,T^.LinkSize);
  462. if T^.LinkCount>0 then
  463. for I:=0 to T^.LinkCount-1 do
  464. with Keywords[I] do
  465. begin
  466. T^.Links^[I].Context:=KwContext;
  467. T^.Links^[I].FileID:=ID;
  468. Inc(LinkPosCount);
  469. with LinkPos[LinkPosCount] do
  470. begin
  471. A.Y:=PosY-1; B.Y:=PosY-1;
  472. A.X:=StartX-1; B.X:=EndX-1;
  473. end;
  474. end;
  475. end;
  476. else
  477. with THLPKeywordRecord(KeyWR.Data^) do
  478. begin
  479. T^.LinkCount:=KeywordCount;
  480. GetMem(T^.Links,T^.LinkSize);
  481. if KeywordCount>0 then
  482. for I:=0 to KeywordCount-1 do
  483. begin
  484. T^.Links^[I].Context:=Keywords[I].KwContext;
  485. T^.Links^[I].FileID:=ID;
  486. end;
  487. end;
  488. end;
  489. end;
  490. if OK then OK:=ExtractTextRec(TextR);
  491. if OK then
  492. if TextR.Size>0 then
  493. begin
  494. T^.Text:=TextR.Data; T^.TextSize:=TextR.Size;
  495. TextR.Data:=nil; TextR.Size:=0;
  496. end;
  497. DisposeRecord(TextR); DisposeRecord(KeyWR);
  498. end;
  499. ReadTopic:=OK;
  500. end;
  501. destructor TOAHelpFile.Done;
  502. begin
  503. if F<>nil then Dispose(F, Done);
  504. inherited Done;
  505. end;
  506. function CreateProc(const FileName,Param: string;Index : longint): PHelpFile; {$ifndef FPC}far;{$endif}
  507. begin
  508. CreateProc:=New(POAHelpFile, Init(FileName,Index));
  509. end;
  510. procedure RegisterHelpType;
  511. begin
  512. RegisterHelpFileType({$ifdef FPC}@{$endif}CreateProc);
  513. end;
  514. END.