woahelp.pas 15 KB

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