mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-08 08:08:13 +02:00

- lowers build times - ide is not that important anymore than years before - other utils like pastojs are also located in the packages tree git-svn-id: trunk@37926 -
1680 lines
47 KiB
ObjectPascal
1680 lines
47 KiB
ObjectPascal
{
|
||
This file is part of the Free Pascal Integrated Development Environment
|
||
Copyright (c) 2000 by Berczi Gabor
|
||
|
||
Help support for Windows Help (.HLP) files
|
||
|
||
See the file COPYING.FPC, included in this distribution,
|
||
for details about the copyright.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
||
**********************************************************************}
|
||
{$R-}
|
||
unit WWinHelp;
|
||
|
||
interface
|
||
|
||
uses Objects,
|
||
WUtils,WHelp;
|
||
|
||
const
|
||
WinHelpMagicNo = $00035F3F;
|
||
WinHelpBTreeHeaderMagicNo = $293B;
|
||
WinHelpSystemHeaderMagicNo = $036c;
|
||
|
||
{ WinHelp Btree dataformat descriptors }
|
||
wh_bdf_Long = 'L';
|
||
wh_bdf_ASCIIZ = 'F';
|
||
wh_bdf_ASCIIZ2 = 'i';
|
||
wh_bdf_Short = '2';
|
||
wh_bdf_Long2 = '4';
|
||
wh_bdf_ASCIIZ3 = 'z';
|
||
|
||
{ WinHelp system record type }
|
||
wh_srt_Title = 1;
|
||
wh_srt_Copyright = 2;
|
||
wh_srt_Contents = 3;
|
||
wh_srt_Config = 4;
|
||
wh_srt_Icon = 5;
|
||
wh_srt_Window = 6;
|
||
wh_srt_Citation = 8;
|
||
wh_srt_LangID = 9;
|
||
wh_srt_Content = 10;
|
||
wh_srt_DefFont = 11;
|
||
wh_srt_FtIndex = 12;
|
||
wh_srt_Groups = 13;
|
||
wh_srt_KeyIndex = 14;
|
||
wh_srt_Language = 18;
|
||
wh_srt_DLLMaps = 19;
|
||
|
||
type
|
||
PInteger = ^integer;
|
||
|
||
TWinHelpHeader = packed record
|
||
MagicNo : longint;
|
||
DirectoryStart : longint;
|
||
NonDirectoryStart: longint;
|
||
EntireFileSize : longint;
|
||
end;
|
||
|
||
TWinHelpFileEntryHeader = packed record
|
||
ReservedSpace : longint;
|
||
UsedSpace : longint;
|
||
FileFlags : byte;
|
||
end;
|
||
|
||
TWinHelpRecordHeader = packed record
|
||
RecordType : word;
|
||
DataSize : word;
|
||
end;
|
||
|
||
TWinHelpBTreeHeader = packed record
|
||
Magic : word;
|
||
Flags : word; { 0x0002 always set, 0x0400 set if directory }
|
||
PageSize : word;
|
||
DataFormat : array[1..16] of char;
|
||
MustBeZero : word; { $0000 }
|
||
PageSplits : word;
|
||
RootPage : word;
|
||
MustBeNegOne : integer; { $ffff }
|
||
TotalPages : word;
|
||
NumLevels : word;
|
||
TotalEntries : word;
|
||
Pages : record end;
|
||
end;
|
||
|
||
TWinHelpBTreeIndexHeader = packed record
|
||
Unknown : longint;
|
||
NumEntries : word; { no of index-entries }
|
||
PrevPage : integer;
|
||
end;
|
||
|
||
{
|
||
TWinHelpBTreeIndexEntry = packed record
|
||
FileName : STRINGZ;
|
||
PageNumber : word;
|
||
end;
|
||
}
|
||
|
||
TWinHelpBTreeNodeHeader = packed record
|
||
Unknown : longint;
|
||
NumEntries : word; { no of index-entries }
|
||
PrevPage : integer;
|
||
NextPage : integer;
|
||
end;
|
||
|
||
TWinHelpSystemHeader = packed record
|
||
Magic : word;
|
||
Version : word;
|
||
Always1 : word;
|
||
GenDate : longint;
|
||
Flags : word;
|
||
end;
|
||
|
||
TWinHelpPhrIndexHeader = packed record
|
||
Magic : longint;
|
||
NumEntries : longint;
|
||
CompressedSize : longint;
|
||
PhrImageSize : longint;
|
||
PhrImageCompressedSize: longint;
|
||
Always0 : longint;
|
||
BitCount_Unk : word; { BitCount = lower 4 bits }
|
||
Dunno : word;
|
||
end;
|
||
|
||
TWinHelpTopicBlockHeader = packed record
|
||
LastTopicLink : longint;
|
||
FirstTopicLink : longint;
|
||
LastTopicHeader : longint;
|
||
end;
|
||
|
||
TWinHelpTopicBlock = packed record
|
||
Header : TWinHelpTopicBlockHeader;
|
||
Data : record end;
|
||
end;
|
||
|
||
TWinHelpTopicHeader = packed record
|
||
BlockSize : longint;
|
||
PrevOffset : longint; { prev topic }
|
||
NextOffset : longint; { next topic }
|
||
TopicNumber : longint;
|
||
NonScrollRgnOfs : longint; { start of non-scrolling region (topic ofs) }
|
||
ScrollRgnOfs : longint; { topic ofs }
|
||
NextTopic : longint; { next type 2 record }
|
||
end;
|
||
|
||
TWinHelpTopicLink = packed record
|
||
BlockSize : longint;
|
||
DataLen2 : longint;
|
||
PrevBlock : longint;
|
||
NextBlock : longint;
|
||
DataLen1 : longint;
|
||
RecordType : byte;
|
||
end;
|
||
|
||
TTopicBlock = object
|
||
Header : TWinHelpTopicBlockHeader;
|
||
DataSize : longint;
|
||
DataPtr : PByteArray;
|
||
private
|
||
CurOfs: longint;
|
||
procedure Seek(Pos: longint);
|
||
function GetPos: longint;
|
||
function GetSize: longint;
|
||
procedure Read(var Buf; Count: longint);
|
||
end;
|
||
|
||
PTopicEnumData = ^TTopicEnumData;
|
||
TTopicEnumData = record
|
||
TB : TTopicBlock;
|
||
BlockNo : longint;
|
||
TopicPos: longint;
|
||
TopicOfs: longint;
|
||
TL : TWinHelpTopicLink;
|
||
LinkData1Size: longint;
|
||
LinkData1: PByteArray;
|
||
LinkData2Size: longint;
|
||
LinkData2: PByteArray;
|
||
end;
|
||
|
||
PWinHelpFile = ^TWinHelpFile;
|
||
TWinHelpFile = object(THelpFile)
|
||
constructor Init(AFileName: string; AID: word);
|
||
destructor Done; virtual;
|
||
public
|
||
function LoadIndex: boolean; virtual;
|
||
function ReadTopic(T: PTopic): boolean; virtual;
|
||
private
|
||
F: PStream;
|
||
Header: TWinHelpHeader;
|
||
SysHeader: TWinHelpSystemHeader;
|
||
Title: string;
|
||
CNTFileName: string;
|
||
PhrasesStart: longint;
|
||
TTLBTreeStart: longint;
|
||
TopicFileStart: longint;
|
||
PhrIndexStart: longint;
|
||
PhrImageStart: longint;
|
||
Phrases: PUnsortedStringCollection;
|
||
TreeDone: boolean;
|
||
IndexLoaded: boolean;
|
||
function ReadHeader: boolean;
|
||
function ReadInternalDirectory: boolean;
|
||
function ProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word;
|
||
ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
|
||
function ProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
|
||
TotalPages: word; ReadIndexEntryMethod, ReadLeafEntryMethod: pointer;
|
||
ScannedPages: PIntCollection): boolean;
|
||
function ProcessTree(ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; NoDuplicates: boolean): boolean;
|
||
function IDIRReadIndexEntry(SubPageNo: PInteger): boolean;
|
||
function IDIRReadLeafEntry(P: pointer): boolean;
|
||
function IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
|
||
function TTLBReadIndexEntry(SubPageNo: PInteger): boolean;
|
||
function TTLBReadLeafEntry(P: pointer): boolean;
|
||
function TTLBProcessTopicEntry(const TopicTitle: string; FileOfs: longint): boolean;
|
||
function ReadSystemFile: boolean;
|
||
function ReadPhraseFile: boolean;
|
||
function ReadTTLBTree: boolean;
|
||
function ReadPhrIndexFile(PhraseOfs: PIntCollection; var IH: TWinHelpPhrIndexHeader): boolean;
|
||
function ReadPhrImageFile(PhraseOfs: PIntCollection; const IH: TWinHelpPhrIndexHeader): boolean;
|
||
function TopicBlockSize: word;
|
||
function LZ77Compressed: boolean;
|
||
function UsesHallCompression: boolean;
|
||
procedure ExtractTopicOffset(TopicOffset: longint; var TopicBlockNo, TopicBlockOffset: word);
|
||
function ReadTopicBlock(BlockNo: word; var T: TTopicBlock; ReadData: boolean): boolean;
|
||
function ProcessTopicBlock(BlockNo: longint; EnumProc: pointer): boolean;
|
||
procedure PhraseDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
|
||
procedure HallDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
|
||
end;
|
||
|
||
procedure RegisterHelpType;
|
||
|
||
implementation
|
||
|
||
uses Strings;
|
||
|
||
function ReadString(F: PStream): string;
|
||
var S: string;
|
||
C: char;
|
||
begin
|
||
S:='';
|
||
if Assigned(F) then
|
||
repeat
|
||
F^.Read(C,sizeof(C));
|
||
if (F^.Status=stOK) and (C<>#0) then
|
||
S:=S+C;
|
||
until (C=#0) or (F^.Status<>stOK);
|
||
ReadString:=S;
|
||
end;
|
||
|
||
function TTopicBlock.GetPos: longint;
|
||
begin
|
||
GetPos:=CurOfs;
|
||
end;
|
||
|
||
function TTopicBlock.GetSize: longint;
|
||
begin
|
||
GetSize:=DataSize;
|
||
end;
|
||
|
||
procedure TTopicBlock.Seek(Pos: longint);
|
||
begin
|
||
CurOfs:=Pos;
|
||
end;
|
||
|
||
procedure TTopicBlock.Read(var Buf; Count: longint);
|
||
begin
|
||
FillChar(Buf,Count,0);
|
||
if Count>(DataSize-CurOfs) then
|
||
begin
|
||
Count:=Max(0,DataSize-CurOfs);
|
||
end;
|
||
Move(DataPtr^[CurOfs],Buf,Count);
|
||
Inc(CurOfs,Count);
|
||
end;
|
||
|
||
constructor TWinHelpFile.Init(AFileName: string; AID: word);
|
||
var OK: boolean;
|
||
begin
|
||
if inherited Init(AID)=false then Fail;
|
||
New(Phrases, Init(1000,1000));
|
||
F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
|
||
OK:=F<>nil;
|
||
if OK then OK:=(F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
OK:=ReadHeader;
|
||
end;
|
||
if OK=false then
|
||
begin
|
||
Done;
|
||
Fail;
|
||
end;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadHeader: boolean;
|
||
var OK: boolean;
|
||
begin
|
||
F^.Read(Header,sizeof(Header));
|
||
OK:=(F^.Status=stOK);
|
||
OK:=OK and (Header.MagicNo=WinHelpMagicNo);
|
||
if OK then
|
||
begin
|
||
F^.Seek(Header.DirectoryStart);
|
||
OK:=(F^.Status=stOK);
|
||
end;
|
||
if OK then
|
||
OK:=ReadInternalDirectory;
|
||
ReadHeader:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.TopicBlockSize: word;
|
||
var Size: word;
|
||
begin
|
||
if (SysHeader.Version<=16) then
|
||
Size:=2048 else
|
||
if (SysHeader.Flags in[0,4]) then
|
||
Size:=4096
|
||
else
|
||
Size:=2048;
|
||
TopicBlockSize:=Size;
|
||
end;
|
||
|
||
function TWinHelpFile.LZ77Compressed: boolean;
|
||
begin
|
||
LZ77Compressed:=(SysHeader.Version>16) and (SysHeader.Flags<>0);
|
||
end;
|
||
|
||
function TWinHelpFile.UsesHallCompression: boolean;
|
||
begin
|
||
UsesHallCompression:=(PhrIndexStart<>0) and (PhrImageStart<>0);
|
||
end;
|
||
|
||
function TWinHelpFile.ReadSystemFile: boolean;
|
||
var OK: boolean;
|
||
FH: TWinHelpFileEntryHeader;
|
||
RH: TWinHelpRecordHeader;
|
||
StartOfs,RecStartOfs: longint;
|
||
begin
|
||
F^.Read(FH,sizeof(FH));
|
||
OK:=(F^.Status=stOK);
|
||
StartOfs:=F^.GetPos;
|
||
F^.Read(SysHeader,sizeof(SysHeader));
|
||
OK:=OK and (F^.Status=stOK);
|
||
OK:=OK and (SysHeader.Magic=WinHelpSystemHeaderMagicNo);
|
||
if OK then
|
||
if SysHeader.Version>16 then
|
||
begin
|
||
repeat
|
||
F^.Read(RH,sizeof(RH));
|
||
OK:=(F^.Status=stOK);
|
||
RecStartOfs:=F^.GetPos;
|
||
if OK then
|
||
begin
|
||
case RH.RecordType of
|
||
wh_srt_Title : Title:=ReadString(F);
|
||
wh_srt_Content : CNTFileName:=ReadString(F);
|
||
end;
|
||
if F^.GetPos<>RecStartOfs+RH.DataSize then
|
||
F^.Seek(RecStartOfs+RH.DataSize);
|
||
OK:=(F^.Status=stOK);
|
||
end;
|
||
until (OK=false) or (F^.GetPos>=StartOfs+FH.UsedSpace);
|
||
end
|
||
else
|
||
Title:=ReadString(F);
|
||
OK:=OK and (F^.Status=stOK);
|
||
ReadSystemFile:=OK;
|
||
end;
|
||
|
||
function LZ77Decompress(SrcBufP: pointer; SrcSize: longint; DestBufP: pointer; DestSize: longint): longint;
|
||
var SrcBuf: PByteArray absolute SrcBufP;
|
||
DestBuf: PByteArray absolute DestBufP;
|
||
var SrcOfs: longint;
|
||
function GetByte: byte;
|
||
begin
|
||
GetByte:=PByteArray(SrcBuf)^[SrcOfs];
|
||
Inc(SrcOfs);
|
||
end;
|
||
var DestOfs: longint;
|
||
procedure PutByte(B: byte);
|
||
begin
|
||
PByteArray(DestBuf)^[DestOfs]:=B;
|
||
Inc(DestOfs);
|
||
end;
|
||
var B,Mask: byte;
|
||
Len,J: word;
|
||
I: integer;
|
||
const N = 4096; F=16;
|
||
begin
|
||
SrcOfs:=0; DestOfs:=0; I:=N-F;
|
||
while (SrcOfs<SrcSize) do
|
||
begin
|
||
B:=GetByte;
|
||
for Mask:=0 to 7 do
|
||
begin
|
||
if SrcOfs=SrcSize then Break;
|
||
if (B and (1 shl Mask))<>0 then
|
||
begin
|
||
J:=GetByte;
|
||
Len:=GetByte;
|
||
J:=J+(Len and $0f) shl 8;
|
||
Len:=(Len and $f0) shr 4+3;
|
||
while (Len>0) do
|
||
begin
|
||
PutByte(PByteArray(DestBuf)^[DestOfs-J-1]);
|
||
{Inc(J); }Inc(I);
|
||
Dec(Len);
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
PutByte(GetByte);
|
||
I:=(I+1) and (N-1);
|
||
end;
|
||
end;
|
||
end;
|
||
LZ77Decompress:=DestOfs;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadPhraseFile: boolean;
|
||
var OK: boolean;
|
||
FH: TWinHelpFileEntryHeader;
|
||
NumPhrases: word;
|
||
DecompSize: longint;
|
||
W: word;
|
||
PhraseOfss: PWordArray;
|
||
PhraseOfssSize: word;
|
||
PhraseBuf: PByteArray;
|
||
TempBuf: pointer;
|
||
TempSize: longint;
|
||
I,PhraseBufSize,PhraseOfs,PhraseSize: longint;
|
||
S: string;
|
||
begin
|
||
F^.Read(FH,sizeof(FH));
|
||
OK:=(F^.Status=stOK);
|
||
F^.Read(NumPhrases,sizeof(NumPhrases));
|
||
F^.Read(W,sizeof(W));
|
||
OK:=(F^.Status=stOK) and (W=$0100);
|
||
if OK then
|
||
begin
|
||
PhraseOfssSize:=(NumPhrases+1)*sizeof(word);
|
||
GetMem(PhraseOfss,PhraseOfssSize);
|
||
F^.Read(W,sizeof(W));
|
||
if W=2*(NumPhrases+1) then
|
||
begin
|
||
{ uncompressed data }
|
||
PhraseOfss^[0]:=W;
|
||
F^.Read(PhraseOfss^[1],PhraseOfssSize-sizeof(word)*1);
|
||
DecompSize:=FH.UsedSpace-(PhraseOfssSize+2+2+2);
|
||
PhraseBufSize:=DecompSize;
|
||
GetMem(PhraseBuf,PhraseBufSize);
|
||
F^.Read(PhraseBuf^,DecompSize);
|
||
end
|
||
else
|
||
begin
|
||
DecompSize:=W;
|
||
F^.Read(W,sizeof(W));
|
||
DecompSize:=DecompSize+longint(W) shl 16;
|
||
Inc(DecompSize);
|
||
PhraseOfss^[0]:=DecompSize;
|
||
F^.Read(PhraseOfss^[1],PhraseOfssSize);
|
||
PhraseBufSize:=DecompSize+10;
|
||
GetMem(PhraseBuf,PhraseBufSize); FillChar(PhraseBuf^,DecompSize,0);
|
||
TempSize:=FH.UsedSpace-(PhraseOfssSize+2+2+2);
|
||
GetMem(TempBuf,TempSize);
|
||
F^.Read(TempBuf^,TempSize);
|
||
LZ77Decompress(TempBuf,TempSize,PhraseBuf,DecompSize);
|
||
FreeMem(TempBuf,TempSize);
|
||
end;
|
||
for I:=1 to NumPhrases do
|
||
begin
|
||
PhraseOfs:=PhraseOfss^[I]-PhraseOfss^[1];
|
||
if I=NumPhrases then
|
||
PhraseSize:=DecompSize-PhraseOfs
|
||
else
|
||
PhraseSize:=PhraseOfss^[I+1]-PhraseOfss^[I];
|
||
S:=MemToStr(PhraseBuf^[PhraseOfs],PhraseSize);
|
||
Phrases^.InsertStr(S);
|
||
end;
|
||
FreeMem(PhraseOfss,PhraseOfssSize);
|
||
FreeMem(PhraseBuf,PhraseBufSize);
|
||
end;
|
||
ReadPhraseFile:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word;
|
||
ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
|
||
var OK: boolean;
|
||
BNH: TWinHelpBTreeNodeHeader;
|
||
Count: longint;
|
||
PageOfs: longint;
|
||
CurPage: longint;
|
||
begin
|
||
CurPage:=PageNo;
|
||
repeat
|
||
PageOfs:=PagesBase+longint(PageSize)*CurPage;
|
||
F^.Seek(PageOfs);
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
F^.Read(BNH,sizeof(BNH));
|
||
OK:=(F^.Status=stOK);
|
||
end;
|
||
if OK then
|
||
if (ScannedPages<>nil) and ScannedPages^.Contains(CurPage) then
|
||
Break
|
||
else
|
||
begin
|
||
if Assigned(ScannedPages) then ScannedPages^.Add(CurPage);
|
||
Count:=0;
|
||
repeat
|
||
TreeDone:=not (longint(CallPointerMethod(ReadLeafEntryMethod,@Self,nil))<>0);
|
||
Inc(Count);
|
||
until (OK=false) or (F^.Status<>stOK) or TreeDone or (Count>=BNH.NumEntries){ or
|
||
(F^.GetPos>=PageOfs+PageSize-BNH.NumEntries)};
|
||
end;
|
||
|
||
if (BNH.PrevPage<>-1) and (TreeDone=false) then
|
||
CurPage:=BNH.PrevPage;
|
||
until (OK=false) or TreeDone or (BNH.PrevPage=-1);
|
||
ProcessLeafPage:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
|
||
TotalPages: word; ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
|
||
var BIH: TWinHelpBTreeIndexHeader;
|
||
I: integer;
|
||
SubPageNo: integer;
|
||
OK: boolean;
|
||
OldPos: longint;
|
||
CurPage: longint;
|
||
begin
|
||
CurPage:=PageNo;
|
||
repeat
|
||
F^.Seek(PagesBase+longint(PageSize)*CurPage);
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
F^.Read(BIH,sizeof(BIH));
|
||
OK:=(F^.Status=stOK);
|
||
end;
|
||
if OK then
|
||
if (ScannedPages<>nil) and ScannedPages^.Contains(CurPage) then
|
||
Break
|
||
else
|
||
begin
|
||
if Assigned(ScannedPages) then ScannedPages^.Add(CurPage);
|
||
for I:=1 to BIH.NumEntries do
|
||
begin
|
||
SubPageNo:=-1;
|
||
OK:=(longint(CallPointerMethod(ReadIndexEntryMethod,@Self,@SubPageNo))<>0);
|
||
OK:=OK and (F^.Status=stOK);
|
||
if OK then
|
||
if CurLevel<MaxLevel-1 then
|
||
begin
|
||
if (0<=SubPageNo) then
|
||
if (ScannedPages=nil) or (ScannedPages^.Contains(SubPageNo)=false) then
|
||
begin
|
||
OldPos:=F^.GetPos;
|
||
OK:=ProcessIndexPage(CurLevel+1,MaxLevel,PagesBase,PageSize,SubPageNo,TotalPages,
|
||
ReadIndexEntryMethod,ReadLeafEntryMethod,ScannedPages);
|
||
if F^.GetPos<>OldPos then
|
||
F^.Seek(OldPos);
|
||
end
|
||
end
|
||
else
|
||
{ process leaf page }
|
||
if (0<=SubPageNo) then
|
||
if (ScannedPages=nil) or (ScannedPages^.Contains(SubPageNo)=false) then
|
||
begin
|
||
OldPos:=F^.GetPos;
|
||
OK:=ProcessLeafPage(PagesBase,PageSize,SubPageNo,TotalPages,ReadLeafEntryMethod,ScannedPages);
|
||
if F^.GetPos<>OldPos then
|
||
F^.Seek(OldPos);
|
||
end;
|
||
if TreeDone then
|
||
Break;
|
||
end;
|
||
end;
|
||
|
||
if (BIH.PrevPage>0) and (TreeDone=false) then
|
||
CurPage:=BIH.PrevPage
|
||
else
|
||
Break;
|
||
until (OK=false) or TreeDone;
|
||
ProcessIndexPage:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ProcessTree(ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; NoDuplicates: boolean): boolean;
|
||
var BTH: TWinHelpBTreeHeader;
|
||
OK: boolean;
|
||
PagesBase: longint;
|
||
ScannedPages: PIntCollection;
|
||
begin
|
||
ScannedPages:=nil;
|
||
TreeDone:=false;
|
||
F^.Read(BTH,sizeof(BTH));
|
||
OK:=(F^.Status=stOK);
|
||
PagesBase:=F^.GetPos;
|
||
if OK then
|
||
begin
|
||
OK:=(BTH.Magic=WinHelpBTreeHeaderMagicNo) and
|
||
(BTH.MustBeZero=0) and
|
||
(BTH.MustBeNegOne=-1);
|
||
end;
|
||
if OK then
|
||
begin
|
||
if NoDuplicates then
|
||
New(ScannedPages, Init(500,100));
|
||
if BTH.NumLevels>1 then
|
||
begin
|
||
OK:=ProcessIndexPage(1,BTH.NumLevels,PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages,
|
||
ReadIndexEntryMethod,ReadLeafEntryMethod,ScannedPages);
|
||
end
|
||
else
|
||
OK:=ProcessLeafPage(PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages,ReadLeafEntryMethod,ScannedPAges);
|
||
if Assigned(ScannedPages) then
|
||
Dispose(ScannedPages, Done);
|
||
end;
|
||
ProcessTree:=OK;
|
||
end;
|
||
|
||
(*function TWinHelpFile.IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
|
||
var OK: boolean;
|
||
begin
|
||
OK:=true;
|
||
if FileName='|SYSTEM' then
|
||
begin
|
||
F^.Seek(FileOfs); OK:=(F^.Status=stOK);
|
||
if OK then OK:=ReadSystemFile;
|
||
end else
|
||
if (FileName='|Phrases') then
|
||
begin
|
||
PhrasesStart:=FileOfs;
|
||
end else
|
||
;
|
||
IDIRProcessFile:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.IDIRProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word): boolean;
|
||
var OK: boolean;
|
||
BNH: TWinHelpBTreeNodeHeader;
|
||
FileOfs: longint;
|
||
Count: integer;
|
||
S: string;
|
||
CurOfs,PageOfs,OldPos: longint;
|
||
begin
|
||
PageOfs:=PagesBase+PageSize*PageNo;
|
||
F^.Seek(PageOfs);
|
||
OK:=(F^.Status=stOK);
|
||
repeat
|
||
if OK then
|
||
begin
|
||
F^.Read(BNH,sizeof(BNH));
|
||
OK:=(F^.Status=stOK);
|
||
end;
|
||
if OK then
|
||
begin
|
||
Count:=0;
|
||
repeat
|
||
S:=ReadString(F);
|
||
F^.Read(FileOfs,sizeof(FileOfs)); { longint }
|
||
OK:=OK and (F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
OldPos:=F^.GetPos;
|
||
OK:=IDIRProcessFile(S,FileOfs);
|
||
if F^.GetPos<>OldPos then
|
||
F^.Seek(OldPos);
|
||
end;
|
||
Inc(Count);
|
||
until (OK=false) or (Count=BNH.NumEntries){ or (F^.GetPos>=PageOfs+PageSize-BNH.NumEntries)};
|
||
end;
|
||
|
||
if BNH.PrevPage<>-1 then
|
||
begin
|
||
F^.Seek(PagesBase+PageSize*BNH.PrevPage);
|
||
OK:=(F^.Status=stOK);
|
||
end;
|
||
until (OK=false) or (BNH.PrevPage=-1);
|
||
IDIRProcessLeafPage:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.IDIRProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
|
||
TotalPages: word): boolean;
|
||
var BIH: TWinHelpBTreeIndexHeader;
|
||
I: integer;
|
||
SubPageNo: integer;
|
||
OK: boolean;
|
||
S: string;
|
||
OldPos: longint;
|
||
begin
|
||
F^.Seek(PagesBase+PageSize*PageNo);
|
||
OK:=(F^.Status=stOK);
|
||
repeat
|
||
if OK then
|
||
begin
|
||
F^.Read(BIH,sizeof(BIH));
|
||
OK:=(F^.Status=stOK);
|
||
end;
|
||
if OK then
|
||
for I:=1 to BIH.NumEntries do
|
||
begin
|
||
S:=ReadString(F);
|
||
F^.Read(SubPageNo,sizeof(SubPageNo)); { word }
|
||
OK:=OK and (F^.Status=stOK);
|
||
if OK then
|
||
if CurLevel<MaxLevel-1 then
|
||
begin
|
||
if (0<=SubPageNo) then
|
||
begin
|
||
OldPos:=F^.GetPos;
|
||
OK:=IDIRProcessIndexPage(CurLevel+1,MaxLevel,PagesBase,PageSize,SubPageNo,TotalPages);
|
||
if F^.GetPos<>OldPos then
|
||
F^.Seek(OldPos);
|
||
end
|
||
end
|
||
else
|
||
{ process leaf page }
|
||
if (0<=SubPageNo) then
|
||
OK:=IDIRProcessLeafPage(PagesBase,PageSize,SubPageNo,TotalPages);
|
||
end;
|
||
|
||
if (BIH.PrevPage>0) then
|
||
begin
|
||
F^.Seek(PagesBase+PageSize*BIH.PrevPage);
|
||
OK:=(F^.Status=stOK);
|
||
end
|
||
else
|
||
Break;
|
||
until (OK=false);
|
||
IDIRProcessIndexPage:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadInternalDirectory: boolean;
|
||
var BTH: TWinHelpBTreeHeader;
|
||
OK: boolean;
|
||
PagesBase: longint;
|
||
begin
|
||
F^.Read(BTH,sizeof(BTH));
|
||
OK:=(F^.Status=stOK);
|
||
PagesBase:=F^.GetPos;
|
||
if OK then
|
||
begin
|
||
OK:=(BTH.Magic=WinHelpBTreeHeaderMagicNo) and
|
||
(BTH.MustBeZero=0) and
|
||
(BTH.MustBeNegOne=-1);
|
||
end;
|
||
if BTH.NumLevels>1 then
|
||
OK:=IDIRProcessIndexPage(1,BTH.NumLevels,PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages)
|
||
else
|
||
OK:=IDIRProcessLeafPage(PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages);
|
||
ReadInternalDirectory:=OK;
|
||
end;
|
||
*)
|
||
function TWinHelpFile.IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
|
||
var OK: boolean;
|
||
begin
|
||
OK:=true;
|
||
if FileName='|SYSTEM' then
|
||
begin
|
||
F^.Seek(FileOfs); OK:=(F^.Status=stOK);
|
||
if OK then OK:=ReadSystemFile;
|
||
end else
|
||
if (FileName='|Phrases') then begin PhrasesStart:=FileOfs; end else
|
||
if (FileName='|TOPIC') then begin TopicFileStart:=FileOfs; end else
|
||
if (FileName='|TTLBTREE') then begin TTLBTreeStart:=FileOfs; end else
|
||
if (FileName='|PhrIndex') then begin PhrIndexStart:=FileOfs; end else
|
||
if (FileName='|PhrImage') then begin PhrImageStart:=FileOfs; end else
|
||
;
|
||
IDIRProcessFile:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.IDIRReadIndexEntry(SubPageNo: PInteger): boolean;
|
||
var {S: string;}
|
||
OK: boolean;
|
||
begin
|
||
{S:=}ReadString(F);
|
||
F^.Read(SubPageNo^,sizeof(SubPageNo^));
|
||
OK:=(F^.Status=stOK);
|
||
IDIRReadIndexEntry:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.IDIRReadLeafEntry(P: pointer): boolean;
|
||
var OK: boolean;
|
||
S: string;
|
||
FileOfs,OldPos: longint;
|
||
begin
|
||
S:=ReadString(F);
|
||
F^.Read(FileOfs,sizeof(FileOfs)); { longint }
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
OldPos:=F^.GetPos;
|
||
OK:=IDIRProcessFile(S,FileOfs);
|
||
if F^.GetPos<>OldPos then
|
||
F^.Seek(OldPos);
|
||
OK:=OK and (F^.Status=stOK);
|
||
end;
|
||
IDIRReadLeafEntry:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadInternalDirectory: boolean;
|
||
var OK: boolean;
|
||
FH: TWinHelpFileEntryHeader;
|
||
begin
|
||
F^.Read(FH,sizeof(FH));
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
OK:=ProcessTree(@TWinHelpFile.IDIRReadIndexEntry,@TWinHelpFile.IDIRReadLeafEntry,true);
|
||
ReadInternalDirectory:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.TTLBReadIndexEntry(SubPageNo: PInteger): boolean;
|
||
var TopicOffset: longint;
|
||
OK: boolean;
|
||
begin
|
||
F^.Read(TopicOffset,sizeof(TopicOffset));
|
||
F^.Read(SubPageNo^,sizeof(SubPageNo^));
|
||
OK:=(F^.Status=stOK);
|
||
TTLBReadIndexEntry:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.TTLBReadLeafEntry(P: pointer): boolean;
|
||
var OK: boolean;
|
||
S: string;
|
||
TopicOfs,OldPos: longint;
|
||
begin
|
||
F^.Read(TopicOfs,sizeof(TopicOfs)); { longint }
|
||
S:=ReadString(F);
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
OldPos:=F^.GetPos;
|
||
OK:=TTLBProcessTopicEntry(S,TopicOfs);
|
||
if F^.GetPos<>OldPos then
|
||
F^.Seek(OldPos);
|
||
OK:=OK and (F^.Status=stOK);
|
||
end;
|
||
TTLBReadLeafEntry:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.TTLBProcessTopicEntry(const TopicTitle: string; FileOfs: longint): boolean;
|
||
var OK: boolean;
|
||
{const Count: longint = 0;}
|
||
begin
|
||
{ Inc(Count);
|
||
if (Count mod 100)=1 then
|
||
begin
|
||
gotoxy(1,1); write(Count,' - ',IndexEntries^.Count,' - ',Topics^.Count);
|
||
end;}
|
||
OK:=(IndexEntries^.Count<MaxCollectionSize-10);
|
||
if OK then
|
||
begin
|
||
if (TopicTitle<>'') and (FileOfs>=0) then
|
||
begin
|
||
AddIndexEntry(TopicTitle,FileOfs);
|
||
AddTopic(FileOfs,FileOfs,'',nil,0);
|
||
end;
|
||
end;
|
||
TTLBProcessTopicEntry:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadTTLBTree: boolean;
|
||
var OK: boolean;
|
||
FH: TWinHelpFileEntryHeader;
|
||
begin
|
||
F^.Read(FH,sizeof(FH));
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
OK:=ProcessTree(@TWinHelpFile.TTLBReadIndexEntry,@TWinHelpFile.TTLBReadLeafEntry,true);
|
||
ReadTTLBTree:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadPhrIndexFile(PhraseOfs: PIntCollection; var IH: TWinHelpPhrIndexHeader): boolean;
|
||
var OK: boolean;
|
||
FH: TWinHelpFileEntryHeader;
|
||
TotalBitPos,BufBitPos: longint;
|
||
BitBuf: array[0..1023] of byte;
|
||
CurFrag: word;
|
||
function GetBit: integer;
|
||
begin
|
||
BufBitPos:=(TotalBitPos mod ((High(BitBuf)-Low(BitBuf)+1)*8));
|
||
if (BufBitPos=0) then
|
||
begin
|
||
CurFrag:=Min(sizeof(BitBuf),FH.UsedSpace-(TotalBitPos div 8));
|
||
F^.Read(BitBuf,CurFrag);
|
||
OK:=OK and (F^.Status=stOK);
|
||
end;
|
||
if (BitBuf[Low(BitBuf)+BufBitPos div 8] and (1 shl (BufBitPos mod 8)))<>0 then
|
||
GetBit:=1
|
||
else
|
||
GetBit:=0;
|
||
Inc(TotalBitPos);
|
||
end;
|
||
var Delta: longint;
|
||
I,J,LastOfs: longint;
|
||
BitCount: integer;
|
||
begin
|
||
F^.Read(FH,sizeof(FH));
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
F^.Read(IH,sizeof(IH));
|
||
OK:=(F^.Status=stOK) and (IH.Magic=1);
|
||
end;
|
||
if OK then
|
||
begin
|
||
PhraseOfs^.Add(0);
|
||
TotalBitPos:=0; LastOfs:=0; BitCount:=(IH.BitCount_Unk and $0f);
|
||
for I:=1 to IH.NumEntries do
|
||
begin
|
||
Delta:=1;
|
||
while GetBit=1 do
|
||
Delta:=Delta+(1 shl BitCount);
|
||
for J:=0 to BitCount-1 do
|
||
Delta:=Delta+(1 shl J)*GetBit;
|
||
Inc(LastOfs,Delta);
|
||
PhraseOfs^.Add(LastOfs);
|
||
end;
|
||
end;
|
||
ReadPhrIndexFile:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadPhrImageFile(PhraseOfs: PIntCollection; const IH: TWinHelpPhrIndexHeader): boolean;
|
||
var OK: boolean;
|
||
FH: TWinHelpFileEntryHeader;
|
||
PhraseBufSize: longint;
|
||
PhraseBuf: PByteArray;
|
||
TempBufSize: longint;
|
||
TempBuf: pointer;
|
||
CurOfs,NextOfs: longint;
|
||
I: longint;
|
||
begin
|
||
F^.Read(FH,sizeof(FH));
|
||
OK:=(F^.Status=stOK);
|
||
OK:=OK and (IH.PhrImageCompressedSize=FH.UsedSpace);
|
||
if OK then
|
||
begin
|
||
PhraseBufSize:=IH.PhrImageSize;
|
||
GetMem(PhraseBuf,PhraseBufSize);
|
||
if IH.PhrImageSize=IH.PhrImageCompressedSize then
|
||
begin
|
||
F^.Read(PhraseBuf^,PhraseBufSize);
|
||
end
|
||
else
|
||
begin
|
||
TempBufSize:=IH.PhrImageCompressedSize;
|
||
GetMem(TempBuf,TempBufSize);
|
||
F^.Read(TempBuf^,TempBufSize);
|
||
OK:=(F^.Status=stOK);
|
||
if OK then LZ77Decompress(TempBuf,TempBufSize,PhraseBuf,PhraseBufSize);
|
||
FreeMem(TempBuf,TempBufSize);
|
||
end;
|
||
if OK then
|
||
begin
|
||
for I:=1 to IH.NumEntries do
|
||
begin
|
||
CurOfs:=PhraseOfs^.AtInt(I-1);
|
||
NextOfs:=PhraseOfs^.AtInt(I);
|
||
Phrases^.InsertStr(MemToStr(PhraseBuf^[CurOfs],NextOfs-CurOfs));
|
||
end;
|
||
end;
|
||
FreeMem(PhraseBuf,PhraseBufSize);
|
||
end;
|
||
ReadPhrImageFile:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.LoadIndex: boolean;
|
||
var OK: boolean;
|
||
PO: PIntCollection;
|
||
IH: TWinHelpPhrIndexHeader;
|
||
begin
|
||
if IndexLoaded then OK:=true else
|
||
begin
|
||
if PhrasesStart<>0 then
|
||
begin
|
||
F^.Seek(PhrasesStart); OK:=(F^.Status=stOK);
|
||
if OK then OK:=ReadPhraseFile;
|
||
end else
|
||
if (PhrIndexStart<>0) and (PhrImageStart<>0) then
|
||
begin
|
||
New(PO, Init(1000,1000));
|
||
F^.Seek(PhrIndexStart); OK:=(F^.Status=stOK);
|
||
if OK then OK:=ReadPhrIndexFile(PO,IH);
|
||
if OK then begin F^.Seek(PhrImageStart); OK:=(F^.Status=stOK); end;
|
||
if OK then OK:=ReadPhrImageFile(PO,IH);
|
||
Dispose(PO, Done);
|
||
end;
|
||
if TTLBTreeStart<>0 then
|
||
begin
|
||
F^.Seek(TTLBTreeStart); OK:=(F^.Status=stOK);
|
||
if OK then OK:=ReadTTLBTree;
|
||
end;
|
||
IndexLoaded:=OK;
|
||
end;
|
||
LoadIndex:=OK;
|
||
end;
|
||
|
||
procedure TWinHelpFile.ExtractTopicOffset(TopicOffset: longint; var TopicBlockNo, TopicBlockOffset: word);
|
||
var {OfsBitCount: longint;
|
||
OfsMask: longint;}
|
||
BS: longint;
|
||
begin
|
||
{ if LZ77Compressed then
|
||
BS:=32768
|
||
else
|
||
BS:=TopicBlockSize;}
|
||
BS:=32768;
|
||
|
||
{ for OfsBitCount:=0 to 31 do
|
||
if (1 shl OfsBitCount)=BS then
|
||
Break;
|
||
OfsMask:=(1 shl OfsBitCount)-1;
|
||
TopicBlockNo:=(TopicOffset and not OfsMask) shr OfsBitCount;
|
||
TopicBlockOffset:=(TopicOffset and OfsMask);}
|
||
|
||
TopicBlockNo:=TopicOffset div BS;
|
||
TopicBlockOffset:=TopicOffset mod BS;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadTopicBlock(BlockNo: word; var T: TTopicBlock; ReadData: boolean): boolean;
|
||
var TempBuf: pointer;
|
||
BlockBuf: ^TWinHelpTopicBlock;
|
||
OK: boolean;
|
||
RS,DecompSize: longint;
|
||
const TempBufSize = 16384;
|
||
begin
|
||
F^.Reset;
|
||
FillChar(T,sizeof(T),0);
|
||
F^.Seek(TopicFileStart+sizeof(TWinHelpFileEntryHeader)+longint(BlockNo)*TopicBlockSize);
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
if ReadData=false then
|
||
begin
|
||
F^.Read(T.Header,sizeof(T.Header));
|
||
OK:=(F^.Status=stOK);
|
||
end
|
||
else
|
||
begin
|
||
GetMem(BlockBuf, TopicBlockSize);
|
||
F^.Read(BlockBuf^,TopicBlockSize);
|
||
OK:=(F^.Status=stOK);
|
||
if OK then
|
||
begin
|
||
Move(BlockBuf^.Header,T.Header,sizeof(T.Header));
|
||
if LZ77Compressed then
|
||
begin
|
||
GetMem(TempBuf,TempBufSize);
|
||
DecompSize:=LZ77Decompress(@BlockBuf^.Data,TopicBlockSize-sizeof(BlockBuf^.Header),TempBuf,TempBufSize);
|
||
T.DataSize:=DecompSize;
|
||
GetMem(T.DataPtr,T.DataSize);
|
||
Move(TempBuf^,T.DataPtr^,T.DataSize);
|
||
FreeMem(TempBuf,TempBufSize);
|
||
end
|
||
else
|
||
begin
|
||
T.DataSize:=TopicBlockSize-sizeof(BlockBuf^.Header);
|
||
GetMem(T.DataPtr,T.DataSize);
|
||
Move(BlockBuf^.Data,T.DataPtr^,T.DataSize);
|
||
end;
|
||
end;
|
||
FreeMem(BlockBuf,TopicBlockSize);
|
||
end;
|
||
ReadTopicBlock:=OK;
|
||
end;
|
||
|
||
procedure FreeTopicBlock(T: TTopicBlock);
|
||
begin
|
||
if (T.DataSize>0) and (T.DataPtr<>nil) then
|
||
begin
|
||
FreeMem(T.DataPtr,T.DataSize);
|
||
T.DataPtr:=nil;
|
||
end;
|
||
end;
|
||
|
||
procedure TWinHelpFile.PhraseDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
|
||
var SrcBuf: PByteArray absolute SrcBufP;
|
||
DestBuf: PByteArray absolute DestBufP;
|
||
var SrcOfs: longint;
|
||
function GetByte: byte;
|
||
begin
|
||
GetByte:=SrcBuf^[SrcOfs];
|
||
Inc(SrcOfs);
|
||
end;
|
||
var DestOfs: longint;
|
||
procedure PutByte(B: byte);
|
||
begin
|
||
if DestOfs<DestBufSize then
|
||
DestBuf^[DestOfs]:=B;
|
||
Inc(DestOfs);
|
||
end;
|
||
var B: byte;
|
||
I,Index: longint;
|
||
S: string;
|
||
begin
|
||
SrcOfs:=0; DestOfs:=0;
|
||
while (SrcOfs<SrcBufSize) do
|
||
begin
|
||
B:=GetByte;
|
||
if (B=0) or (B>15) then
|
||
PutByte(B)
|
||
else
|
||
begin
|
||
Index:=longint(B)*256-256+GetByte;
|
||
S:=GetStr(Phrases^.At(Index div 2));
|
||
if (Index mod 2)=1 then S:=S+' ';
|
||
for I:=1 to length(S) do
|
||
PutByte(ord(S[I]));
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TWinHelpFile.HallDecompress(SrcBufP: pointer; SrcBufSize: longint; DestBufP: pointer; DestBufSize: longint);
|
||
var SrcBuf: PByteArray absolute SrcBufP;
|
||
DestBuf: PByteArray absolute DestBufP;
|
||
var SrcOfs: longint;
|
||
function GetByte: byte;
|
||
begin
|
||
GetByte:=SrcBuf^[SrcOfs];
|
||
Inc(SrcOfs);
|
||
end;
|
||
var DestOfs: longint;
|
||
procedure PutByte(B: byte);
|
||
begin
|
||
if DestOfs<DestBufSize then
|
||
DestBuf^[DestOfs]:=B;
|
||
Inc(DestOfs);
|
||
end;
|
||
procedure EmitStr(const S: string);
|
||
var I: longint;
|
||
begin
|
||
for I:=1 to length(S) do
|
||
PutByte(ord(S[I]));
|
||
end;
|
||
procedure EmitStrIndex(Index: longint);
|
||
begin
|
||
EmitStr(GetStr(Phrases^.At(Index)));
|
||
end;
|
||
var B: longint;
|
||
I,Index: longint;
|
||
S: string;
|
||
begin
|
||
SrcOfs:=0; DestOfs:=0;
|
||
while (SrcOfs<SrcBufSize) do
|
||
begin
|
||
B:=GetByte;
|
||
if (B and 1)=0 then
|
||
EmitStrIndex(B div 2)
|
||
else
|
||
if (B and 3)=1 then
|
||
EmitStrIndex(B*64+64+GetByte)
|
||
else
|
||
if (B and 7)=3 then
|
||
for I:=1 to (B div 8)+1 do
|
||
PutByte(GetByte)
|
||
else
|
||
if (B and 15)=7 then
|
||
EmitStr(CharStr(' ',B div 16+1))
|
||
else
|
||
EmitStr(CharStr(#0,B div 16+1));
|
||
end;
|
||
end;
|
||
|
||
function TWinHelpFile.ProcessTopicBlock(BlockNo: longint; EnumProc: pointer): boolean;
|
||
var TB: TTopicBlock;
|
||
TL: TWinHelpTopicLink;
|
||
BlockFileOfs: longint;
|
||
LinkData1Size: longint;
|
||
LinkData1: PByteArray;
|
||
LinkData2Size: longint;
|
||
LinkData2: PByteArray;
|
||
TempBufSize: longint;
|
||
TempBuf: PByteArray;
|
||
CurBlockOfs,LastLinkOfs: longint;
|
||
TopicPos,TopicOfs: longint;
|
||
OK: boolean;
|
||
TEN: TTopicEnumData;
|
||
DoCont: boolean;
|
||
begin
|
||
OK:=ReadTopicBlock(BlockNo,TB,true);
|
||
if OK then
|
||
begin
|
||
TopicOfs:=0; DoCont:=true;
|
||
BlockFileOfs:=longint(BlockNo)*TopicBlockSize;
|
||
if TB.Header.FirstTopicLink>0 then
|
||
TB.Seek((TB.Header.FirstTopicLink and $3fff)-sizeof(TB.Header));
|
||
if TB.Header.LastTopicLink=-1 then
|
||
LastLinkOfs:=TB.DataSize-1-sizeof(TL)
|
||
else
|
||
LastLinkOfs:={(TB.Header.LastTopicLink-BlockFileOfs-sizeof(TB.Header))}TB.GetSize-1;
|
||
while (DoCont) and OK and (TB.GetPos<=LastLinkOfs) do
|
||
begin
|
||
CurBlockOfs:=TB.GetPos;
|
||
TopicPos:=TB.GetPos+sizeof(TB.Header);
|
||
TB.Read(TL,sizeof(TL));
|
||
if (TL.BlockSize=0) or (TL.DataLen1=0) or (TB.GetPos>LastLinkOfs) or
|
||
(TB.GetSize-TB.GetPos<TL.BlockSize) then
|
||
Break;
|
||
LinkData1Size:=TL.DataLen1-sizeof(TL);
|
||
GetMem(LinkData1,LinkData1Size);
|
||
TB.Read(LinkData1^,LinkData1Size);
|
||
LinkData2Size:=TL.DataLen2;
|
||
GetMem(LinkData2,LinkData2Size);
|
||
if TL.DataLen2>TL.BlockSize-TL.DataLen1 then
|
||
begin
|
||
TempBufSize:=TL.BlockSize-TL.DataLen1;
|
||
GetMem(TempBuf,TempBufSize);
|
||
TB.Read(TempBuf^,TempBufSize);
|
||
if UsesHallCompression then
|
||
HallDecompress(TempBuf,TempBufSize,LinkData2,LinkData2Size)
|
||
else
|
||
PhraseDecompress(TempBuf,TempBufSize,LinkData2,LinkData2Size);
|
||
FreeMem(TempBuf,TempBufSize);
|
||
end
|
||
else
|
||
TB.Read(LinkData2^,TL.DataLen2);
|
||
FillChar(TEN,sizeof(TEN),0);
|
||
TEN.TB:=TB;
|
||
TEN.BlockNo:=BlockNo;
|
||
TEN.TopicPos:=TopicPos;
|
||
TEN.TopicOfs:=TopicOfs;
|
||
TEN.TL:=TL;
|
||
TEN.LinkData1Size:=LinkData1Size;
|
||
TEN.LinkData1:=LinkData1;
|
||
TEN.LinkData2Size:=LinkData2Size;
|
||
TEN.LinkData2:=LinkData2;
|
||
DoCont:=(longint(CallPointerLocal(EnumProc,
|
||
get_caller_frame(get_frame,get_pc_addr),@TEN)) and $ff)<>0;
|
||
case TL.RecordType of
|
||
$02: ;
|
||
$20,$23:
|
||
begin
|
||
Inc(TopicOfs,TL.DataLen2);
|
||
end;
|
||
end;
|
||
FreeMem(LinkData1,LinkData1Size);
|
||
FreeMem(LinkData2,LinkData2Size);
|
||
end;
|
||
FreeTopicBlock(TB);
|
||
end;
|
||
ProcessTopicBlock:=OK;
|
||
end;
|
||
|
||
function TWinHelpFile.ReadTopic(T: PTopic): boolean;
|
||
var OK: boolean;
|
||
BlockNo,BlockOfs: word;
|
||
TopicStartPos: longint;
|
||
GotIt: boolean;
|
||
TH: TWinHelpTopicHeader;
|
||
CurLine: string;
|
||
Lines: PUnsortedStringCollection;
|
||
EmitSize: longint;
|
||
LastEmittedChar: integer;
|
||
procedure FlushLine;
|
||
begin
|
||
Lines^.InsertStr(CurLine); CurLine:='';
|
||
end;
|
||
procedure EmitText(const S: string);
|
||
begin
|
||
Inc(EmitSize,length(S));
|
||
if length(CurLine)+length(S)>High(S) then
|
||
FlushLine;
|
||
CurLine:=CurLine+S;
|
||
if length(S)>0 then
|
||
LastEmittedChar:=ord(S[length(S)]);
|
||
end;
|
||
procedure EmitTextC(C: PChar);
|
||
var RemSize,CurOfs,CurFrag: longint;
|
||
S: string;
|
||
begin
|
||
if C=nil then Exit;
|
||
RemSize:=StrLen(C); CurOfs:=0;
|
||
while (RemSize>0) do
|
||
begin
|
||
CurFrag:=Min(RemSize,255);
|
||
S[0]:=chr(CurFrag);
|
||
Move(PByteArray(C)^[CurOfs],S[1],CurFrag);
|
||
EmitText(S);
|
||
Dec(RemSize,CurFrag); Inc(CurOfs,CurFrag);
|
||
end;
|
||
end;
|
||
function SearchTopicStart(P: PTopicEnumData): boolean;
|
||
begin
|
||
case P^.TL.RecordType of
|
||
$02 : TopicStartPos:=P^.TopicPos;
|
||
end;
|
||
GotIt:=(P^.TL.RecordType in [$20,$23]) and (P^.TopicOfs<=BlockOfs) and (BlockOfs<P^.TopicOfs+P^.LinkData2Size);
|
||
SearchTopicStart:=not GotIt;
|
||
end;
|
||
function RenderTopicProc(P: PTopicEnumData): boolean;
|
||
var LinkData1Ofs: longint;
|
||
LinkData2Ofs: longint;
|
||
function ReadUCHAR: byte;
|
||
begin
|
||
ReadUCHAR:=P^.LinkData1^[LinkData1Ofs];
|
||
Inc(LinkData1Ofs);
|
||
end;
|
||
function ReadCHAR: shortint;
|
||
var B: byte;
|
||
U: shortint absolute B;
|
||
begin
|
||
B:=ReadUCHAR;
|
||
ReadCHAR:=U;
|
||
end;
|
||
function ReadUSHORT: word;
|
||
begin
|
||
ReadUSHORT:=ReadUCHAR+longint(ReadUCHAR)*256;
|
||
end;
|
||
function ReadSHORT: integer;
|
||
var W: word;
|
||
I: integer absolute W;
|
||
begin
|
||
W:=ReadUSHORT;
|
||
ReadSHORT:=I;
|
||
end;
|
||
function ReadComprUSHORT: word;
|
||
var B: byte;
|
||
begin
|
||
B:=ReadUCHAR;
|
||
if (B mod 2)=0 then
|
||
ReadComprUSHORT:=(B div 2)
|
||
else
|
||
ReadComprUSHORT:=(B div 2)+longint(ReadUCHAR)*128;
|
||
end;
|
||
{$Q-}
|
||
function ReadLONG: longint;
|
||
begin
|
||
ReadLONG:=ReadUSHORT+longint(ReadUSHORT)*65536;
|
||
end;
|
||
function ReadULONG: longint;
|
||
begin
|
||
ReadULONG:=ReadLONG;
|
||
end;
|
||
{$Q+}
|
||
function ReadComprSHORT: integer;
|
||
var B: byte;
|
||
begin
|
||
B:=ReadUCHAR;
|
||
if (B mod 2)=0 then
|
||
ReadComprSHORT:=longint(B div 2)-64
|
||
else
|
||
ReadComprSHORT:=((B div 2)+longint(ReadUCHAR)*128)-16384;
|
||
end;
|
||
function ReadComprULONG: longint;
|
||
var W: word;
|
||
begin
|
||
W:=ReadUSHORT;
|
||
if (W mod 2)=0 then
|
||
ReadComprULONG:=(W div 2)
|
||
else
|
||
ReadComprULONG:=(W div 2)+longint(ReadUSHORT)*32768;
|
||
end;
|
||
function ReadComprLONG: longint;
|
||
var W: word;
|
||
begin
|
||
W:=ReadUSHORT;
|
||
if (W mod 2)=0 then
|
||
ReadComprLONG:=longint(W div 2)-16384
|
||
else
|
||
ReadComprLONG:=(W div 2)+longint(ReadUSHORT)*32768-67108864;
|
||
end;
|
||
function ReadString: string;
|
||
var S: string;
|
||
B: byte;
|
||
begin
|
||
S:='';
|
||
repeat
|
||
B:=ReadUCHAR;
|
||
if B<>0 then
|
||
S:=S+chr(B);
|
||
until B=0;
|
||
ReadString:=S;
|
||
end;
|
||
procedure EmitDebugText(const S: string);
|
||
begin
|
||
{$ifdef DEBUGMSG}
|
||
EmitText(S);
|
||
{$endif}
|
||
end;
|
||
var Finished: boolean;
|
||
S: string;
|
||
{ ---- }
|
||
Cmd: integer;
|
||
I,TopicSize: longint;
|
||
NumberOfCols,TableType: byte;
|
||
MinTableWidth: integer;
|
||
Flags: longint;
|
||
NumberOfTabStops: integer;
|
||
TabStop: longint;
|
||
PType: integer;
|
||
Len: word;
|
||
SLen,LinkOfs: longint;
|
||
SPtr: pointer;
|
||
SBuf: PChar;
|
||
PictureSize,PictureStartOfs: longint;
|
||
FontNumber: integer;
|
||
begin
|
||
Finished:=((P^.TopicPos>TopicStartPos) or (P^.BlockNo>BlockNo)) and
|
||
(P^.TL.RecordType=$02); { next topic header found }
|
||
if (Finished=false) and (P^.TopicPos>=TopicStartPos) then
|
||
case P^.TL.RecordType of
|
||
$02 :
|
||
begin
|
||
S[0]:=chr(Min(StrLen(pointer(P^.LinkData2)),P^.LinkData2Size));
|
||
Move(P^.LinkData2^,S[1],ord(S[0]));
|
||
if S<>'' then
|
||
begin
|
||
EmitText(' '+S+' <20>'+hscLineBreak);
|
||
EmitText(' '+CharStr('<27>',length(S)+3)+hscLineBreak);
|
||
end;
|
||
end;
|
||
$20,$23 :
|
||
begin
|
||
EmitDebugText(hscLineBreak+'<------ new record ------>'+hscLineBreak);
|
||
LinkData1Ofs:=0; LinkData2Ofs:=0; EmitSize:=0;
|
||
{ ---- }
|
||
MinTableWidth:=0;
|
||
TopicSize:=ReadComprULONG;
|
||
if P^.TL.RecordType in[$20,$23] then
|
||
{TopicLen:=}ReadComprUSHORT;
|
||
if P^.TL.RecordType=$23 then
|
||
begin
|
||
NumberOfCols:=ReadUCHAR; TableType:=ReadUCHAR;
|
||
if TableType in[0,2] then
|
||
MinTableWidth:=ReadSHORT;
|
||
for I:=1 to NumberOfCols do
|
||
begin
|
||
{GapWidth:=}ReadSHORT;
|
||
{ColWidth:=}ReadSHORT;
|
||
end;
|
||
end;
|
||
|
||
if P^.TL.RecordType=$23 then
|
||
begin
|
||
{Column:=}ReadSHORT; {-1 = end of topic}
|
||
{Unknown:=}ReadSHORT; {Always0:=}ReadCHAR;
|
||
end;
|
||
{Unknown:=}ReadUCHAR; {Uknown:=}ReadCHAR;
|
||
ID:=ReadUSHORT;
|
||
Flags:=ReadUSHORT;
|
||
if (Flags and 1)<>0 then
|
||
{Unknown:=}ReadComprLONG;
|
||
if (Flags and 2)<>0 then
|
||
{SpacingAbove:=}ReadComprSHORT;
|
||
if (Flags and 4)<>0 then
|
||
{SpacingBelow:=}ReadComprSHORT;
|
||
if (Flags and 8)<>0 then
|
||
{SpacingLines:=}ReadComprSHORT;
|
||
if (Flags and 16)<>0 then
|
||
{LeftIndent:=}ReadComprSHORT;
|
||
if (Flags and 32)<>0 then
|
||
{RightIndent:=}ReadComprSHORT;
|
||
if (Flags and 64)<>0 then
|
||
{FirstLineIndent:=}ReadComprSHORT;
|
||
if (Flags and 256)<>0 then {BorderInfo}
|
||
begin
|
||
{BorderFlags:=}ReadUCHAR;
|
||
{BorderWidth:=}ReadSHORT;
|
||
end;
|
||
if (Flags and 512)<>0 then {TabInfo}
|
||
begin
|
||
NumberOfTabStops:=ReadComprSHORT;
|
||
for I:=1 to NumberOfTabStops do
|
||
begin
|
||
TabStop:=ReadComprUSHORT;
|
||
if (TabStop and $4000)<>0 then
|
||
{TabType:=}ReadComprUSHORT;
|
||
end;
|
||
end;
|
||
for I:=10 to 14 do
|
||
if (Flags and (1 shl I))<>0 then
|
||
ReadUCHAR;
|
||
{
|
||
if (TH.NonScrollRgnOfs<>-1) then
|
||
if (P^.TopicPos=(TH.ScrollRgnOfs and $3fff)) then
|
||
begin
|
||
EmitText(hscLineBreak);
|
||
EmitText(CharStr('<27>',80));
|
||
EmitText(hscLineBreak);
|
||
end;
|
||
}
|
||
while (LinkData2Ofs<P^.LinkData2Size) do
|
||
begin
|
||
LinkOfs:=-1;
|
||
SPtr:=@(P^.LinkData2^[LinkData2Ofs]);
|
||
SLen:=StrLen(SPtr);
|
||
if SLen>0 then
|
||
SBuf:=SPtr
|
||
else
|
||
SBuf:=nil;
|
||
Inc(LinkData2Ofs,SLen+1);
|
||
|
||
Cmd:=-1;
|
||
if (LinkData1Ofs<P^.LinkData1Size) then
|
||
begin
|
||
Cmd:=ReadUCHAR;
|
||
case Cmd of
|
||
$ff : { End of formatting }
|
||
EmitDebugText('[blockend]');
|
||
$20 : begin
|
||
EmitDebugText('[vfld]');
|
||
{vfldNumber:=}ReadLONG;
|
||
end;
|
||
$21 : begin
|
||
EmitDebugText('[dtype]');
|
||
{dtypeNumber:=}ReadSHORT;
|
||
end;
|
||
$3a,
|
||
$3c : {????}
|
||
begin
|
||
if LastEmittedChar<>ord(hscLineBreak) then
|
||
EmitText(hscLineBreak);
|
||
EmitDebugText('[tag0x'+hexstr(Cmd,2)+']');
|
||
end;
|
||
$80 : begin
|
||
FontNumber:=ReadSHORT;
|
||
EmitDebugText('[font'+inttostr(FontNumber)+']');
|
||
end;
|
||
$81 : {LineBreak}
|
||
begin
|
||
EmitDebugText('[br]');
|
||
EmitText(hscLineBreak);
|
||
end;
|
||
$82 : {End Of Paragraph}
|
||
begin
|
||
EmitDebugText('[eop]');
|
||
EmitText(hscLineBreak);
|
||
end;
|
||
$83 : {TAB}
|
||
begin
|
||
EmitDebugText('[tab]');
|
||
EmitText(' ');
|
||
end;
|
||
$86,
|
||
$87,
|
||
$88 : { ewc or bmc or bmcwd or bmct or button or mci }
|
||
begin
|
||
PType:=ReadUCHAR;
|
||
PictureSize:=ReadComprLONG;
|
||
if PType=$22 then
|
||
{NumberOfHotSpots:=}ReadComprSHORT;
|
||
PictureStartOfs:=LinkData1Ofs;
|
||
PictureSize:=Min(PictureSize,P^.LinkData1Size-LinkData1Ofs);
|
||
if PType in[$03,$22] then
|
||
begin
|
||
{PictureIsEmbedded:=}ReadSHORT;
|
||
{PictureNumber:=}ReadSHORT;
|
||
for I:=1 to PictureSize-4 do
|
||
{PictureData[I-1]:=}ReadCHAR;
|
||
end;
|
||
if PType=$05 then
|
||
begin
|
||
{Unknown1:=}ReadSHORT;
|
||
{Unknown2:=}ReadSHORT;
|
||
{Unknown3:=}ReadSHORT;
|
||
{ +??? }
|
||
end;
|
||
while (LinkData1Ofs<PictureStartOfs+PictureSize) do
|
||
{}ReadCHAR;
|
||
EmitText('[img]');
|
||
end;
|
||
$89 : { end of hotspot }EmitDebugText('[ehs]');
|
||
$8b : { non-break space }; { does not appear in LinkData2 !!!! }
|
||
$8c : { non-break hypen };
|
||
$c6 : {????}
|
||
ReadLONG;
|
||
$c8, { macro }
|
||
$cc : { macro without font change }
|
||
begin
|
||
Len:=ReadSHORT;
|
||
for I:=1 to longint(Len)-3 do
|
||
{C:=}ReadUCHAR; { string }
|
||
end;
|
||
$e0, { popup jump } { start with underlined green }
|
||
$e1 : { topic jump } { start with underlined green }
|
||
begin
|
||
EmitDebugText('[linkgr]');
|
||
LinkOfs:=ReadLONG;
|
||
if LinkOfs>0 then
|
||
begin
|
||
EmitText(hscLink);
|
||
AddLinkToTopic(T,ID,LinkOfs);
|
||
end;
|
||
end;
|
||
$e2, { popup jump }
|
||
$e3, { topic jump }
|
||
$e6, { popup jump without font change }
|
||
$e7 : { topic jump without font change }
|
||
begin
|
||
EmitDebugText('[link]');
|
||
LinkOfs:=ReadLONG;
|
||
if LinkOfs>0 then
|
||
begin
|
||
EmitText(hscLink);
|
||
AddLinkToTopic(T,ID,LinkOfs);
|
||
end;
|
||
end;
|
||
$ea, { popup jump into external file }
|
||
$eb, { popup jump into external file without font change }
|
||
$ee, { popup jump into external file / secondary window }
|
||
$ef : { popup jump into external file / secondary window }
|
||
begin
|
||
EmitDebugText('[linkext]');
|
||
Len:=ReadSHORT;
|
||
PType:=ReadUCHAR;
|
||
LinkOfs:=ReadLONG;
|
||
{WindowNo:=}ReadUCHAR;
|
||
{NameOfExternalFile:=}ReadString;
|
||
{WindowName:=}ReadString;
|
||
if LinkOfs>0 then
|
||
begin
|
||
EmitText(hscLink);
|
||
AddLinkToTopic(T,ID,LinkOfs);
|
||
end;
|
||
end;
|
||
else EmitDebugText('[tag0x'+hexstr(Cmd,2)+']');
|
||
end;
|
||
end;
|
||
if SLen>0 then
|
||
EmitTextC(SPtr);
|
||
{ case Cmd of
|
||
$81 : EmitText(hscLineBreak);
|
||
$82 : EmitText(hscLineBreak);
|
||
end;}
|
||
if LinkOfs>0 then
|
||
begin
|
||
EmitText(hscLink);
|
||
EmitDebugText('[eol]');
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
RenderTopicProc:=not Finished;
|
||
end;
|
||
begin
|
||
F^.Reset;
|
||
OK:=(TopicFileStart<>0) and (T<>nil);
|
||
if OK then
|
||
begin
|
||
ExtractTopicOffset(T^.FileOfs,BlockNo,BlockOfs);
|
||
TopicStartPos:=-1; GotIt:=false;
|
||
OK:=ProcessTopicBlock(BlockNo,@SearchTopicStart);
|
||
OK:=OK and GotIt and (TopicStartPos<>-1);
|
||
if OK then
|
||
begin
|
||
CurLine:='';
|
||
New(Lines, Init(1000,1000));
|
||
LastEmittedChar:=-1;
|
||
OK:=ProcessTopicBlock(BlockNo,@RenderTopicProc);
|
||
FlushLine;
|
||
BuildTopic(Lines,T);
|
||
Dispose(Lines, Done);
|
||
end;
|
||
end;
|
||
ReadTopic:=OK;
|
||
end;
|
||
|
||
destructor TWinHelpFile.Done;
|
||
begin
|
||
if Assigned(F) then Dispose(F, Done); F:=nil;
|
||
if Assigned(Phrases) then Dispose(Phrases, Done); Phrases:=nil;
|
||
inherited Done;
|
||
end;
|
||
|
||
function CreateProc(const FileName,Param: string;Index : longint): PHelpFile;
|
||
begin
|
||
CreateProc:=New(PWinHelpFile, Init(FileName,Index));
|
||
end;
|
||
|
||
procedure RegisterHelpType;
|
||
begin
|
||
RegisterHelpFileType({$ifdef FPC}@{$endif}CreateProc);
|
||
end;
|
||
|
||
END.
|