woahelp.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. {
  2. wThis file is part of the Free Pascal Integrated Development Environment
  3. Copyright (c) 2000 by Berczi Gabor
  4. Borland OA .HLP reader objects and routines
  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. {$H-}
  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 AnsiChar; {+ null terminator + $1A }
  34. FileSignature = array [0..12] of AnsiChar; {+ 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. Header.Options :=LEToN(Header.Options);
  197. Header.MainIndexScreen:=LEToN(Header.MainIndexScreen);
  198. Header.MaxScreenSize :=LEToN(Header.MaxScreenSize );
  199. DisposeRecord(R);
  200. end;
  201. end;
  202. ReadHeader:=OK;
  203. end;
  204. function TOAHelpFile.ReadTopics: boolean;
  205. var OK: boolean;
  206. R: TRecord;
  207. L,I: longint;
  208. function GetCtxPos(C: THLPContextPos): longint;
  209. begin
  210. c.LoW:=LEToN(Word(C.LoW));
  211. GetCtxPos:=longint(C.HiB) shl 16 + C.LoW;
  212. end;
  213. begin
  214. OK:=ReadRecord(R, true);
  215. if OK then
  216. with THLPContexts(R.Data^) do
  217. begin
  218. ContextCount:=LEToN(ContextCount);
  219. for I:=1 to longint(ContextCount)-1 do
  220. begin
  221. if Topics^.Count=MaxCollectionSize then Break;
  222. L:=GetCtxPos(Contexts[I]);
  223. if (L and $800000)<>0 then L:=not L;
  224. if (L=-1) and (Header.MainIndexScreen>0) then
  225. L:=GetCtxPos(Contexts[Header.MainIndexScreen]);
  226. if (L>0) then
  227. AddTopic(I,L,'',nil,0);
  228. end;
  229. end;
  230. DisposeRecord(R);
  231. TopicsRead:=OK;
  232. ReadTopics:=OK;
  233. end;
  234. function TOAHelpFile.ReadIndexTable: boolean;
  235. var OK: boolean;
  236. R: TRecord;
  237. I: longint;
  238. LastTag,S: string;
  239. CurPtr: sw_word;
  240. HelpCtx: THelpCtx;
  241. LenCode,CopyCnt,AddLen: byte;
  242. type pword = ^word;
  243. begin
  244. if IndexTableRead then OK:=true else
  245. begin
  246. FillChar(R, SizeOf(R), 0);
  247. LastTag:=''; CurPtr:=0;
  248. OK:=(IndexTablePos<>0);
  249. if OK then begin F^.Seek(IndexTablePos); OK:=F^.Status=stOK; end;
  250. if OK then OK:=ReadRecord(R, true);
  251. if OK then
  252. with THLPIndexTable(R.Data^) do
  253. begin
  254. IndexCount:=LEToN(IndexCount);
  255. for I:=0 to IndexCount-1 do
  256. begin
  257. LenCode:=PByteArray(@Entries)^[CurPtr];
  258. AddLen:=LenCode and $1f; CopyCnt:=LenCode shr 5;
  259. S[0]:=chr(AddLen); Move(PByteArray(@Entries)^[CurPtr+1],S[1],AddLen);
  260. LastTag:=copy(LastTag,1,CopyCnt)+S;
  261. HelpCtx:=PWord(@PByteArray(@Entries)^[CurPtr+1+AddLen])^;
  262. AddIndexEntry(LastTag,HelpCtx);
  263. Inc(CurPtr,1+AddLen+2);
  264. end;
  265. end;
  266. DisposeRecord(R);
  267. IndexTableRead:=OK;
  268. end;
  269. ReadIndexTable:=OK;
  270. end;
  271. function TOAHelpFile.ReadCompression: boolean;
  272. var OK: boolean;
  273. R: TRecord;
  274. begin
  275. OK:=ReadRecord(R, true);
  276. OK:=OK and (R.Size=SizeOf(THLPCompression));
  277. if OK then Move(R.Data^,Compression,SizeOf(Compression));
  278. DisposeRecord(R);
  279. CompressionRead:=OK;
  280. ReadCompression:=OK;
  281. end;
  282. function TOAHelpFile.ReadIndexTags: boolean;
  283. var OK: boolean;
  284. begin
  285. OK:={ReadRecord(R, true)}true;
  286. IndexTagsRead:=OK;
  287. ReadIndexTags:=OK;
  288. end;
  289. function TOAHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  290. var OK: boolean;
  291. H: THLPRecordHeader;
  292. begin
  293. FillChar(R, SizeOf(R), 0);
  294. F^.Read(H,SizeOf(H));
  295. H.RecLength:=LEToN(H.RecLength);
  296. OK:=F^.Status=stOK;
  297. if OK then
  298. begin
  299. R.SClass:=H.RecType; R.Size:=H.RecLength;
  300. if (R.Size>0) and ReadData then
  301. begin
  302. GetMem(R.Data,R.Size);
  303. F^.Read(R.Data^,R.Size);
  304. OK:=F^.Status=stOK;
  305. end;
  306. if OK=false then DisposeRecord(R);
  307. end;
  308. ReadRecord:=OK;
  309. end;
  310. function TOAHelpFile.ReadTopic(T: PTopic): boolean;
  311. var SrcPtr,DestPtr,TopicSize: sw_word;
  312. NewR: TRecord;
  313. LinkPosCount: integer;
  314. LinkPos: array[1..50] of TRect;
  315. function IsLinkPosStart(X,Y: integer): boolean;
  316. var OK: boolean;
  317. I: integer;
  318. begin
  319. OK:=false;
  320. for I:=1 to LinkPosCount do
  321. with LinkPos[I] do
  322. if (A.X=X) and (A.Y=Y) then
  323. begin
  324. OK:=true;
  325. Break;
  326. end;
  327. IsLinkPosStart:=OK;
  328. end;
  329. function IsLinkPosEnd(X,Y: integer): boolean;
  330. var OK: boolean;
  331. I: integer;
  332. begin
  333. OK:=false;
  334. for I:=1 to LinkPosCount do
  335. with LinkPos[I] do
  336. if (B.X=X) and (B.Y=Y) then
  337. begin
  338. OK:=true;
  339. Break;
  340. end;
  341. IsLinkPosEnd:=OK;
  342. end;
  343. function ExtractTextRec(var R: TRecord): boolean;
  344. function GetNextNibble: byte;
  345. var B,N: byte;
  346. begin
  347. B:=PByteArray(R.Data)^[SrcPtr div 2];
  348. N:=( B and ($0f shl (4*(SrcPtr mod 2))) ) shr (4*(SrcPtr mod 2));
  349. Inc(SrcPtr);
  350. GetNextNibble:=N;
  351. end;
  352. procedure RealAddChar(C: AnsiChar);
  353. begin
  354. if Assigned(NewR.Data) then
  355. PByteArray(NewR.Data)^[DestPtr]:=ord(C);
  356. Inc(DestPtr);
  357. end;
  358. var CurX,CurY: integer;
  359. InLink: boolean;
  360. procedure AddChar(C: AnsiChar);
  361. begin
  362. if IsLinkPosStart(CurX+2,CurY) then
  363. begin
  364. RealAddChar(hscLink);
  365. InLink:=true;
  366. end
  367. else
  368. if (C=hscLineBreak) and (InLink) then
  369. begin
  370. RealAddChar(hscLink);
  371. InLink:=false;
  372. end;
  373. RealAddChar(C);
  374. if IsLinkPosEnd(CurX+2,CurY) then
  375. begin
  376. RealAddChar(hscLink);
  377. InLink:=false;
  378. end;
  379. if C<>hscLineBreak then
  380. Inc(CurX)
  381. else
  382. begin
  383. CurX:=0;
  384. Inc(CurY);
  385. end;
  386. end;
  387. var OK: boolean;
  388. C: AnsiChar;
  389. P: pointer;
  390. function GetNextChar: AnsiChar;
  391. var C: AnsiChar;
  392. I,N,Cnt: byte;
  393. begin
  394. N:=GetNextNibble;
  395. case N of
  396. $00 : C:=#0;
  397. $01..$0D : C:=chr(Compression.CharTable[N]);
  398. ncRawChar : begin
  399. I:=GetNextNibble;
  400. C:=chr(I+GetNextNibble shl 4);
  401. end;
  402. ncRepChar : begin
  403. Cnt:=2+GetNextNibble;
  404. C:=GetNextChar();
  405. for I:=1 to Cnt-1 do AddChar(C);
  406. end;
  407. end;
  408. GetNextChar:=C;
  409. end;
  410. begin
  411. OK:=Compression.CompType in[ctNone,ctNibble];
  412. if OK then
  413. case Compression.CompType of
  414. ctNone : ;
  415. ctNibble :
  416. begin
  417. CurX:=0; CurY:=0; InLink:=false;
  418. NewR.SClass:=0;
  419. NewR.Size:=0;
  420. NewR.Data:=nil;
  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. TopicSize:=DestPtr;
  429. CurX:=0; CurY:=0; InLink:=false;
  430. NewR.SClass:=R.SClass;
  431. NewR.Size:=Min(MaxHelpTopicSize,TopicSize);
  432. GetMem(NewR.Data, NewR.Size);
  433. SrcPtr:=0; DestPtr:=0;
  434. while SrcPtr<(R.Size*2) do
  435. begin
  436. C:=GetNextChar;
  437. AddChar(C);
  438. end;
  439. if InLink then AddChar(hscLineBreak);
  440. DisposeRecord(R); R:=NewR;
  441. if (R.Size>DestPtr) then
  442. begin
  443. P:=R.Data; GetMem(R.Data,DestPtr);
  444. Move(P^,R.Data^,DestPtr); FreeMem(P,R.Size); R.Size:=DestPtr;
  445. end;
  446. end;
  447. else OK:=false;
  448. end;
  449. ExtractTextRec:=OK;
  450. end;
  451. var OK: boolean;
  452. TextR,KeyWR: TRecord;
  453. I: sw_word;
  454. begin
  455. OK:=T<>nil;
  456. if OK and (T^.Text=nil) then
  457. begin
  458. LinkPosCount:=0; FillChar(LinkPos,Sizeof(LinkPos),0);
  459. FillChar(TextR,SizeOf(TextR),0); FillChar(KeyWR,SizeOf(KeyWR),0);
  460. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  461. if OK then OK:=ReadRecord(TextR,true);
  462. OK:=OK and (TextR.SClass=oa_rtText);
  463. if OK then OK:=ReadRecord(KeyWR,true);
  464. OK:=OK and (KeyWR.SClass=oa_rtKeyword);
  465. if OK then
  466. begin
  467. case Version.FormatVersion of
  468. TP55FormatVersion :
  469. with THLPKeywordRecord55(KeyWR.Data^) do
  470. begin
  471. UpContext:=LEToN(UpContext);
  472. DownContext:=LEToN(DownContext);
  473. T^.LinkCount:=KeywordCount;
  474. GetMem(T^.Links,T^.LinkSize);
  475. if T^.LinkCount>0 then
  476. for I:=0 to T^.LinkCount-1 do
  477. with Keywords[I] do
  478. begin
  479. KwContext:=LEToN(KwContext);
  480. T^.Links^[I].Context:=KwContext;
  481. T^.Links^[I].FileID:=ID;
  482. Inc(LinkPosCount);
  483. with LinkPos[LinkPosCount] do
  484. begin
  485. A.Y:=PosY-1; B.Y:=PosY-1;
  486. A.X:=StartX-1; B.X:=EndX-1;
  487. end;
  488. end;
  489. end;
  490. else
  491. with THLPKeywordRecord(KeyWR.Data^) do
  492. begin
  493. KeywordCount:=LEToN(KeywordCount);
  494. UpContext:=LEToN(UpContext);
  495. DownContext:=LEToN(DownContext);
  496. T^.LinkCount:=KeywordCount;
  497. GetMem(T^.Links,T^.LinkSize);
  498. if KeywordCount>0 then
  499. for I:=0 to KeywordCount-1 do
  500. begin
  501. Keywords[I].KwContext:=LEToN(Keywords[I].KwContext);
  502. T^.Links^[I].Context:=Keywords[I].KwContext;
  503. T^.Links^[I].FileID:=ID;
  504. end;
  505. end;
  506. end;
  507. end;
  508. if OK then OK:=ExtractTextRec(TextR);
  509. if OK then
  510. if TextR.Size>0 then
  511. begin
  512. T^.Text:=TextR.Data; T^.TextSize:=TextR.Size;
  513. TextR.Data:=nil; TextR.Size:=0;
  514. end;
  515. DisposeRecord(TextR); DisposeRecord(KeyWR);
  516. end;
  517. ReadTopic:=OK;
  518. end;
  519. destructor TOAHelpFile.Done;
  520. begin
  521. if F<>nil then Dispose(F, Done);
  522. inherited Done;
  523. end;
  524. function CreateProc(const FileName,Param: string;Index : longint): PHelpFile;
  525. begin
  526. CreateProc:=New(POAHelpFile, Init(FileName,Index));
  527. end;
  528. procedure RegisterHelpType;
  529. begin
  530. RegisterHelpFileType(@CreateProc);
  531. end;
  532. END.