diff options
| author | andrew <andrew@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2009-11-07 19:36:08 +0000 |
|---|---|---|
| committer | andrew <andrew@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2009-11-07 19:36:08 +0000 |
| commit | e90c00746a7fea9450b38c587d62f772df368420 (patch) | |
| tree | 49dbd1a644ab8f580c4cdc11d6b976e632cf766c /packages/chm | |
| parent | 28c09cf1e3692fedc7b023257cad3543a7ca99d8 (diff) | |
| download | fpc-e90c00746a7fea9450b38c587d62f772df368420.tar.gz | |
* Split TChmWriter to TITSFWriter and TChmWriter
* Some endian fixes for chm binary index
git-svn-id: http://svn.freepascal.org/svn/fpc/trunk@14103 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'packages/chm')
| -rw-r--r-- | packages/chm/src/chmwriter.pas | 1045 |
1 files changed, 558 insertions, 487 deletions
diff --git a/packages/chm/src/chmwriter.pas b/packages/chm/src/chmwriter.pas index 1c479c42d0..6a3afe8c17 100644 --- a/packages/chm/src/chmwriter.pas +++ b/packages/chm/src/chmwriter.pas @@ -44,30 +44,17 @@ Type UrlStrId : Integer; end; - { TChmWriter } + { TITSFWriter } - TChmWriter = class(TObject) + TITSFWriter = class(TObject) FOnLastFile: TNotifyEvent; private - FHasBinaryTOC: Boolean; - FHasBinaryIndex: Boolean; ForceExit: Boolean; - - FDefaultFont: String; - FDefaultPage: String; - FFullTextSearch: Boolean; FInternalFiles: TFileEntryList; // Contains a complete list of files in the chm including FFrameSize: LongWord; // uncompressed files and special internal files of the chm FCurrentStream: TStream; // used to buffer the files that are to be compressed FCurrentIndex: Integer; FOnGetFileData: TGetDataFunc; - FSearchTitlesOnly: Boolean; - FStringsStream: TMemoryStream; // the #STRINGS file - FTopicsStream: TMemoryStream; // the #TOPICS file - FURLTBLStream: TMemoryStream; // the #URLTBL file. has offsets of strings in URLSTR - FURLSTRStream: TMemoryStream; // the #URLSTR file - FFiftiMainStream: TMemoryStream; - FContextStream: TMemoryStream; // the #IVB file FSection0: TMemoryStream; FSection1: TStream; // Compressed Stream FSection1Size: QWord; @@ -78,12 +65,8 @@ Type FDestroyStream: Boolean; FTempStream: TStream; FPostStream: TStream; - FTitle: String; - FHasTOC: Boolean; - FHasIndex: Boolean; FWindowSize: LongWord; FReadCompressedSize: QWord; // Current Size of Uncompressed data that went in Section1 (compressed) - FIndexedFiles: TIndexedWordList; FPostStreamActive: Boolean; // Linear order of file ITSFHeader: TITSFHeader; @@ -92,10 +75,6 @@ Type HeaderSuffix: TITSFHeaderSuffix; //contains the offset of CONTENTSection0 from zero HeaderSection0: TITSPHeaderPrefix; HeaderSection1: TITSPHeader; // DirectoryListings header - FAvlStrings : TAVLTree; // dedupe strings - FAvlURLStr : TAVLTree; // dedupe urltbl + binindex must resolve URL to topicid - SpareString : TStringIndex; - SpareUrlStr : TUrlStrIndex; // DirectoryListings // CONTENT Section 0 (section 1 is contained in section 0) // EOF @@ -106,25 +85,16 @@ Type procedure WriteHeader(Stream: TStream); procedure CreateDirectoryListings; procedure WriteDirectoryListings(Stream: TStream); + procedure WriteInternalFilesBefore; virtual; + procedure WriteInternalFilesAfter; virtual; procedure StartCompressingStream; - procedure WriteSYSTEM; - procedure WriteITBITS; - procedure WriteSTRINGS; - procedure WriteTOPICS; - procedure WriteIVB; // context ids - procedure WriteURL_STR_TBL; - procedure WriteOBJINST; - procedure WriteFiftiMain; procedure WriteREADMEFile; - procedure WriteFinalCompressedFiles; + procedure WriteFinalCompressedFiles; virtual; procedure WriteSection0; procedure WriteSection1; procedure WriteDataSpaceFiles(const AStream: TStream); - function AddString(AString: String): LongWord; - function AddURL(AURL: String; TopicsIndex: DWord): LongWord; - procedure CheckFileMakeSearchable(AStream: TStream; AFileEntry: TFileEntryRec); - function AddTopic(ATitle,AnUrl:AnsiString):integer; - function NextTopicIndex: Integer; + + procedure FileAdded(AStream: TStream; const AEntry: TFileEntryRec); virtual; // callbacks for lzxcomp function AtEndOfData: Longbool; function GetData(Count: LongInt; Buffer: PByte): LongInt; @@ -140,25 +110,79 @@ Type {$ENDIF} // end callbacks public - constructor Create(OutStream: TStream; FreeStreamOnDestroy: Boolean); + constructor Create(AOutStream: TStream; FreeStreamOnDestroy: Boolean); virtual; destructor Destroy; override; procedure Execute; - procedure AppendTOC(AStream: TStream); - procedure AppendBinaryTOCFromSiteMap(ASiteMap: TChmSiteMap); - procedure AppendBinaryIndexFromSiteMap(ASiteMap: TChmSiteMap;chw:boolean); - procedure AppendBinaryTOCStream(AStream: TStream); - procedure AppendBinaryIndexStream(IndexStream,DataStream,MapStream,Propertystream: TStream;chw:boolean); - procedure AppendIndex(AStream: TStream); - procedure AppendSearchDB(AName: String; AStream: TStream); procedure AddStreamToArchive(AFileName, APath: String; AStream: TStream; Compress: Boolean = True); procedure PostAddStreamToArchive(AFileName, APath: String; AStream: TStream; Compress: Boolean = True); - procedure AddContext(AContext: DWord; ATopic: String); property WindowSize: LongWord read FWindowSize write FWindowSize default 2; // in $8000 blocks property FrameSize: LongWord read FFrameSize write FFrameSize default 1; // in $8000 blocks property FilesToCompress: TStrings read FFileNames; property OnGetFileData: TGetDataFunc read FOnGetFileData write FOnGetFileData; property OnLastFile: TNotifyEvent read FOnLastFile write FOnLastFile; property OutStream: TStream read FOutStream; + property TempRawStream: TStream read FTempStream write SetTempRawStream; + //property LocaleID: dword read ITSFHeader.LanguageID write ITSFHeader.LanguageID; + end; + + { TChmWriter } + + TChmWriter = class(TITSFWriter) + private + FHasBinaryTOC: Boolean; + FHasBinaryIndex: Boolean; + FDefaultFont: String; + FDefaultPage: String; + FFullTextSearch: Boolean; + FSearchTitlesOnly: Boolean; + FStringsStream: TMemoryStream; // the #STRINGS file + FTopicsStream: TMemoryStream; // the #TOPICS file + FURLTBLStream: TMemoryStream; // the #URLTBL file. has offsets of strings in URLSTR + FURLSTRStream: TMemoryStream; // the #URLSTR file + FFiftiMainStream: TMemoryStream; + FContextStream: TMemoryStream; // the #IVB file + FTitle: String; + FHasTOC: Boolean; + FHasIndex: Boolean; + FIndexedFiles: TIndexedWordList; + FAvlStrings : TAVLTree; // dedupe strings + FAvlURLStr : TAVLTree; // dedupe urltbl + binindex must resolve URL to topicid + SpareString : TStringIndex; + SpareUrlStr : TUrlStrIndex; + protected + procedure FileAdded(AStream: TStream; const AEntry: TFileEntryRec); override; + private + procedure WriteInternalFilesBefore; override; + procedure WriteInternalFilesAfter; override; + procedure WriteFinalCompressedFiles; override; + procedure WriteSYSTEM; + procedure WriteITBITS; + procedure WriteSTRINGS; + procedure WriteTOPICS; + procedure WriteIVB; // context ids + procedure WriteURL_STR_TBL; + procedure WriteOBJINST; + procedure WriteFiftiMain; + + function AddString(AString: String): LongWord; + function AddURL(AURL: String; TopicsIndex: DWord): LongWord; + procedure CheckFileMakeSearchable(AStream: TStream; AFileEntry: TFileEntryRec); + function AddTopic(ATitle,AnUrl:AnsiString):integer; + function NextTopicIndex: Integer; + + + public + constructor Create(AOutStream: TStream; FreeStreamOnDestroy: Boolean); override; + destructor Destroy; override; + procedure AppendTOC(AStream: TStream); + procedure AppendBinaryTOCFromSiteMap(ASiteMap: TChmSiteMap); + procedure AppendBinaryIndexFromSiteMap(ASiteMap: TChmSiteMap;chw:boolean); + procedure AppendBinaryTOCStream(AStream: TStream); + procedure AppendBinaryIndexStream(IndexStream,DataStream,MapStream,Propertystream: TStream;chw:boolean); + procedure AppendIndex(AStream: TStream); + procedure AppendSearchDB(AName: String; AStream: TStream); + procedure AddContext(AContext: DWord; ATopic: String); + property Title: String read FTitle write FTitle; property FullTextSearch: Boolean read FFullTextSearch write FFullTextSearch; property SearchTitlesOnly: Boolean read FSearchTitlesOnly write FSearchTitlesOnly; @@ -166,8 +190,7 @@ Type property HasBinaryIndex: Boolean read FHasBinaryIndex write FHasBinaryIndex; property DefaultFont: String read FDefaultFont write FDefaultFont; property DefaultPage: String read FDefaultPage write FDefaultPage; - property TempRawStream: TStream read FTempStream write SetTempRawStream; - //property LocaleID: dword read ITSFHeader.LanguageID write ITSFHeader.LanguageID; + end; implementation @@ -208,7 +231,7 @@ end; { TChmWriter } -procedure TChmWriter.InitITSFHeader; +procedure TITSFWriter.InitITSFHeader; begin with ITSFHeader do begin ITSFsig := ITSFFileSig; @@ -223,7 +246,7 @@ begin end; end; -procedure TChmWriter.InitHeaderSectionTable; +procedure TITSFWriter.InitHeaderSectionTable; begin // header section 0 HeaderSection0Table.PosFromZero := LEToN(ITSFHeader.HeaderLength); @@ -276,7 +299,7 @@ begin HeaderSuffix.Offset := NToLE(HeaderSuffix.Offset); end; -procedure TChmWriter.SetTempRawStream(const AValue: TStream); +procedure TITSFWriter.SetTempRawStream(const AValue: TStream); begin if (FCurrentStream.Size > 0) or (FSection1.Size > 0) then raise Exception.Create('Cannot set the TempRawStream once data has been written to it!'); @@ -288,7 +311,7 @@ begin FCurrentStream := AValue; end; -procedure TChmWriter.WriteHeader(Stream: TStream); +procedure TITSFWriter.WriteHeader(Stream: TStream); begin Stream.Write(ITSFHeader, SizeOf(TITSFHeader)); Stream.Write(HeaderSection0Table, SizeOf(TITSFHeaderEntry)); @@ -298,7 +321,7 @@ begin end; -procedure TChmWriter.CreateDirectoryListings; +procedure TITSFWriter.CreateDirectoryListings; type TFirstListEntry = record Entry: array[0..511] of byte; @@ -453,7 +476,7 @@ begin HeaderSection1.IndexTreeDepth := NtoLE(HeaderSection1.IndexTreeDepth); end; -procedure TChmWriter.WriteDirectoryListings(Stream: TStream); +procedure TITSFWriter.WriteDirectoryListings(Stream: TStream); begin Stream.Write(HeaderSection1, SizeOf(HeaderSection1)); FDirectoryListings.Position := 0; @@ -462,6 +485,430 @@ begin //TMemoryStream(FDirectoryListings).SaveToFile('dirlistings.pmg'); end; +procedure TITSFWriter.WriteInternalFilesBefore; +begin + // written to Section0 (uncompressed) + WriteREADMEFile; +end; + +procedure TITSFWriter.WriteInternalFilesAfter; +begin +end; + +procedure IterateWord(aword:TIndexedWord;State:pointer); +var i,cnt : integer; +begin + cnt:=pinteger(state)^; + for i := 0 to AWord.DocumentCount-1 do + Inc(cnt, AWord.GetLogicalDocument(i).NumberOfIndexEntries); + // was commented in original procedure, seems to list index entries per doc. + //WriteLn(AWord.TheWord,' documents = ', AWord.DocumentCount, ' h + pinteger(state)^:=cnt; +end; + +procedure TITSFWriter.WriteREADMEFile; +const DISCLAIMER_STR = 'This archive was not made by the MS HTML Help Workshop(r)(tm) program.'; +var + Entry: TFileEntryRec; +begin + // This procedure puts a file in the archive that says it wasn't compiled with the MS compiler + Entry.Compressed := False; + Entry.DecompressedOffset := FSection0.Position; + FSection0.Write(DISCLAIMER_STR, SizeOf(DISCLAIMER_STR)); + Entry.DecompressedSize := FSection0.Position - Entry.DecompressedOffset; + Entry.Path := '/'; + Entry.Name := '_#_README_#_'; //try to use a name that won't conflict with normal names + FInternalFiles.AddEntry(Entry); +end; + +procedure TITSFWriter.WriteFinalCompressedFiles; +begin + +end; + + +procedure TITSFWriter.WriteSection0; +begin + FSection0.Position := 0; + FOutStream.CopyFrom(FSection0, FSection0.Size); +end; + +procedure TITSFWriter.WriteSection1; +begin + WriteContentToStream(FOutStream, FSection1); +end; + +procedure TITSFWriter.WriteDataSpaceFiles(const AStream: TStream); +var + Entry: TFileEntryRec; +begin + // This procedure will write all files starting with :: + Entry.Compressed := False; // None of these files are compressed + + // ::DataSpace/NameList + Entry.DecompressedOffset := FSection0.Position; + Entry.DecompressedSize := WriteNameListToStream(FSection0, [snUnCompressed,snMSCompressed]); + Entry.Path := '::DataSpace/'; + Entry.Name := 'NameList'; + FInternalFiles.AddEntry(Entry, False); + + // ::DataSpace/Storage/MSCompressed/ControlData + Entry.DecompressedOffset := FSection0.Position; + Entry.DecompressedSize := WriteControlDataToStream(FSection0, 2, 2, 1); + Entry.Path := '::DataSpace/Storage/MSCompressed/'; + Entry.Name := 'ControlData'; + FInternalFiles.AddEntry(Entry, False); + + // ::DataSpace/Storage/MSCompressed/SpanInfo + Entry.DecompressedOffset := FSection0.Position; + Entry.DecompressedSize := WriteSpanInfoToStream(FSection0, FReadCompressedSize); + Entry.Path := '::DataSpace/Storage/MSCompressed/'; + Entry.Name := 'SpanInfo'; + FInternalFiles.AddEntry(Entry, False); + + // ::DataSpace/Storage/MSCompressed/Transform/List + Entry.DecompressedOffset := FSection0.Position; + Entry.DecompressedSize := WriteTransformListToStream(FSection0); + Entry.Path := '::DataSpace/Storage/MSCompressed/Transform/'; + Entry.Name := 'List'; + FInternalFiles.AddEntry(Entry, False); + + // ::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/ + // ::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable + Entry.DecompressedOffset := FSection0.Position; + Entry.DecompressedSize := WriteResetTableToStream(FSection0, FSection1ResetTable); + Entry.Path := '::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/'; + Entry.Name := 'ResetTable'; + FInternalFiles.AddEntry(Entry, True); + + + // ::DataSpace/Storage/MSCompressed/Content do this last + Entry.DecompressedOffset := FSection0.Position; + Entry.DecompressedSize := FSection1Size; // we will write it directly to FOutStream later + Entry.Path := '::DataSpace/Storage/MSCompressed/'; + Entry.Name := 'Content'; + FInternalFiles.AddEntry(Entry, False); + + +end; + +procedure TITSFWriter.FileAdded(AStream: TStream; const AEntry: TFileEntryRec); +begin + // do nothing here +end; + +function _AtEndOfData(arg: pointer): LongBool; cdecl; +begin + Result := TITSFWriter(arg).AtEndOfData; +end; + +function TITSFWriter.AtEndOfData: LongBool; +begin + Result := ForceExit or (FCurrentIndex >= FFileNames.Count-1); + if Result then + Result := Integer(FCurrentStream.Position) >= Integer(FCurrentStream.Size)-1; +end; + +function _GetData(arg: pointer; Count: LongInt; Buffer: Pointer): LongInt; cdecl; +begin + Result := TITSFWriter(arg).GetData(Count, PByte(Buffer)); +end; + +function TITSFWriter.GetData(Count: LongInt; Buffer: PByte): LongInt; +var + FileEntry: TFileEntryRec; +begin + Result := 0; + while (Result < Count) and (not AtEndOfData) do begin + Inc(Result, FCurrentStream.Read(Buffer[Result], Count-Result)); + if (Result < Count) and (not AtEndOfData) + then begin + // the current file has been read. move to the next file in the list + FCurrentStream.Position := 0; + Inc(FCurrentIndex); + ForceExit := OnGetFileData(FFileNames[FCurrentIndex], FileEntry.Path, FileEntry.Name, FCurrentStream); + FileEntry.DecompressedSize := FCurrentStream.Size; + FileEntry.DecompressedOffset := FReadCompressedSize; //269047723;//to test writing really large numbers + FileEntry.Compressed := True; + + FileAdded(FCurrentStream, FileEntry); + + FInternalFiles.AddEntry(FileEntry); + // So the next file knows it's offset + Inc(FReadCompressedSize, FileEntry.DecompressedSize); + FCurrentStream.Position := 0; + end; + + // this is intended for programs to add perhaps a file + // after all the other files have been added. + if (AtEndOfData) + and (FCurrentStream <> FPostStream) then + begin + FPostStreamActive := True; + if Assigned(FOnLastFile) then + FOnLastFile(Self); + FCurrentStream.Free; + WriteFinalCompressedFiles; + FCurrentStream := FPostStream; + FCurrentStream.Position := 0; + Inc(FReadCompressedSize, FCurrentStream.Size); + end; + end; +end; + +function _WriteCompressedData(arg: pointer; Count: LongInt; Buffer: Pointer): LongInt; cdecl; +begin + Result := TITSFWriter(arg).WriteCompressedData(Count, Buffer); +end; + +function TITSFWriter.WriteCompressedData(Count: Longint; Buffer: Pointer): LongInt; +begin + // we allocate a MB at a time to limit memory reallocation since this + // writes usually 2 bytes at a time + if (FSection1 is TMemoryStream) and (FSection1.Position >= FSection1.Size-1) then begin + FSection1.Size := FSection1.Size+$100000; + end; + Result := FSection1.Write(Buffer^, Count); + Inc(FSection1Size, Result); +end; + +procedure _MarkFrame(arg: pointer; UncompressedTotal, CompressedTotal: LongWord); cdecl; +begin + TITSFWriter(arg).MarkFrame(UncompressedTotal, CompressedTotal); +end; + +procedure TITSFWriter.MarkFrame(UnCompressedTotal, CompressedTotal: LongWord); + procedure WriteQWord(Value: QWord); + begin + FSection1ResetTable.Write(NToLE(Value), 8); + end; + procedure IncEntryCount; + var + OldPos: QWord; + Value: DWord; + begin + OldPos := FSection1ResetTable.Position; + FSection1ResetTable.Position := $4; + Value := LeToN(FSection1ResetTable.ReadDWord)+1; + FSection1ResetTable.Position := $4; + FSection1ResetTable.WriteDWord(NToLE(Value)); + FSection1ResetTable.Position := OldPos; + end; + procedure UpdateTotalSizes; + var + OldPos: QWord; + begin + OldPos := FSection1ResetTable.Position; + FSection1ResetTable.Position := $10; + WriteQWord(FReadCompressedSize); // size of read data that has been compressed + WriteQWord(CompressedTotal); + FSection1ResetTable.Position := OldPos; + end; +begin + if FSection1ResetTable.Size = 0 then begin + // Write the header + FSection1ResetTable.WriteDWord(NtoLE(DWord(2))); + FSection1ResetTable.WriteDWord(0); // number of entries. we will correct this with IncEntryCount + FSection1ResetTable.WriteDWord(NtoLE(DWord(8))); // Size of Entries (qword) + FSection1ResetTable.WriteDWord(NtoLE(DWord($28))); // Size of this header + WriteQWord(0); // Total Uncompressed Size + WriteQWord(0); // Total Compressed Size + WriteQWord(NtoLE($8000)); // Block Size + WriteQWord(0); // First Block start + end; + IncEntryCount; + UpdateTotalSizes; + WriteQWord(CompressedTotal); // Next Block Start + // We have to trim the last entry off when we are done because there is no next block in that case +end; + +{$IFDEF LZX_USETHREADS} +function TITSFWriter.LTGetData(Sender: TLZXCompressor; WantedByteCount: Integer; + Buffer: Pointer): Integer; +begin + Result := GetData(WantedByteCount, Buffer); + //WriteLn('Wanted ', WantedByteCount, ' got ', Result); +end; + +function TITSFWriter.LTIsEndOfFile(Sender: TLZXCompressor): Boolean; +begin + Result := AtEndOfData; +end; + +procedure TITSFWriter.LTChunkDone(Sender: TLZXCompressor; + CompressedSize: Integer; UncompressedSize: Integer; Buffer: Pointer); +begin + WriteCompressedData(CompressedSize, Buffer); +end; + +procedure TITSFWriter.LTMarkFrame(Sender: TLZXCompressor; + CompressedTotal: Integer; UncompressedTotal: Integer); +begin + MarkFrame(UncompressedTotal, CompressedTotal); + //WriteLn('Mark Frame C = ', CompressedTotal, ' U = ', UncompressedTotal); +end; +{$ENDIF} + + +constructor TITSFWriter.Create(AOutStream: TStream; FreeStreamOnDestroy: Boolean); +begin + if AOutStream = nil then Raise Exception.Create('TITSFWriter.OutStream Cannot be nil!'); + FOutStream := AOutStream; + FCurrentIndex := -1; + FCurrentStream := TMemoryStream.Create; + FInternalFiles := TFileEntryList.Create; + FSection0 := TMemoryStream.Create; + FSection1 := TMemoryStream.Create; + FSection1ResetTable := TMemoryStream.Create; + FDirectoryListings := TMemoryStream.Create; + FPostStream := TMemoryStream.Create;; + FDestroyStream := FreeStreamOnDestroy; + FFileNames := TStringList.Create; +end; + +destructor TITSFWriter.Destroy; +begin + if FDestroyStream then FOutStream.Free; + FInternalFiles.Free; + FCurrentStream.Free; + FSection0.Free; + FSection1.Free; + FSection1ResetTable.Free; + FDirectoryListings.Free; + FFileNames.Free; + inherited Destroy; +end; + +procedure TITSFWriter.Execute; +begin + InitITSFHeader; + FOutStream.Position := 0; + FSection1Size := 0; + + // write any internal files to FCurrentStream that we want in the compressed section + WriteInternalFilesBefore; + + // move back to zero so that we can start reading from zero :) + FReadCompressedSize := FCurrentStream.Size; + FCurrentStream.Position := 0; // when compressing happens, first the FCurrentStream is read + // before loading user files. So we can fill FCurrentStream with + // internal files first. + + // this gathers ALL files that should be in section1 (the compressed section) + StartCompressingStream; + FSection1.Size := FSection1Size; + + WriteInternalFilesAfter; + + //this creates all special files in the archive that start with ::DataSpace + WriteDataSpaceFiles(FSection0); + + // creates all directory listings including header + CreateDirectoryListings; + + // do this after we have compressed everything so that we know the values that must be written + InitHeaderSectionTable; + + // Now we can write everything to FOutStream + WriteHeader(FOutStream); + WriteDirectoryListings(FOutStream); + WriteSection0; //does NOT include section 1 even though section0.content IS section1 + WriteSection1; // writes section 1 to FOutStream +end; + +// this procedure is used to manually add files to compress to an internal stream that is +// processed before FileToCompress is called. Files added this way should not be +// duplicated in the FilesToCompress property. +procedure TITSFWriter.AddStreamToArchive(AFileName, APath: String; AStream: TStream; Compress: Boolean = True); +var + TargetStream: TStream; + Entry: TFileEntryRec; +begin + // in case AddStreamToArchive is used after we should be writing to the post stream + if FPostStreamActive then + begin + PostAddStreamToArchive(AFileName, APath, AStream, Compress); + Exit; + end; + if AStream = nil then Exit; + if Compress then + TargetStream := FCurrentStream + else + TargetStream := FSection0; + + Entry.Name := AFileName; + Entry.Path := APath; + Entry.Compressed := Compress; + Entry.DecompressedOffset := TargetStream.Position; + Entry.DecompressedSize := AStream.Size; + FileAdded(AStream,Entry); + FInternalFiles.AddEntry(Entry); + AStream.Position := 0; + TargetStream.CopyFrom(AStream, AStream.Size); +end; + +procedure TITSFWriter.PostAddStreamToArchive(AFileName, APath: String; + AStream: TStream; Compress: Boolean); +var + TargetStream: TStream; + Entry: TFileEntryRec; +begin + if AStream = nil then Exit; + if Compress then + TargetStream := FPostStream + else + TargetStream := FSection0; + + Entry.Name := AFileName; + Entry.Path := APath; + Entry.Compressed := Compress; + if not Compress then + Entry.DecompressedOffset := TargetStream.Position + else + Entry.DecompressedOffset := FReadCompressedSize + TargetStream.Position; + Entry.DecompressedSize := AStream.Size; + FInternalFiles.AddEntry(Entry); + AStream.Position := 0; + TargetStream.CopyFrom(AStream, AStream.Size); + FileAdded(AStream, Entry); +end; + +procedure TITSFWriter.StartCompressingStream; +var + {$IFNDEF LZX_USETHREADS} + LZXdata: Plzx_data; + WSize: LongInt; + {$ELSE} + Compressor: TLZXCompressor; + {$ENDIF} +begin + {$IFNDEF LZX_USETHREADS} + lzx_init(@LZXdata, LZX_WINDOW_SIZE, @_GetData, Self, @_AtEndOfData, + @_WriteCompressedData, Self, @_MarkFrame, Self); + + WSize := 1 shl LZX_WINDOW_SIZE; + while not AtEndOfData do begin + lzx_reset(LZXdata); + lzx_compress_block(LZXdata, WSize, True); + end; + + //we have to mark the last frame manually + MarkFrame(LZXdata^.len_uncompressed_input, LZXdata^.len_compressed_output); + + lzx_finish(LZXdata, nil); + {$ELSE} + Compressor := TLZXCompressor.Create(10); + Compressor.OnChunkDone :=@LTChunkDone; + Compressor.OnGetData :=@LTGetData; + Compressor.OnIsEndOfFile:=@LTIsEndOfFile; + Compressor.OnMarkFrame :=@LTMarkFrame; + Compressor.Execute(True); + //Sleep(20000); + Compressor.Free; + {$ENDIF} +end; + + procedure TChmWriter.WriteSystem; var Entry: TFileEntryRec; @@ -618,21 +1065,9 @@ begin PostAddStreamToArchive('#STRINGS', '/', FStringsStream); end; -procedure IterateWord(aword:TIndexedWord;State:pointer); -var i,cnt : integer; -begin - cnt:=pinteger(state)^; - for i := 0 to AWord.DocumentCount-1 do - Inc(cnt, AWord.GetLogicalDocument(i).NumberOfIndexEntries); - // was commented in original procedure, seems to list index entries per doc. - //WriteLn(AWord.TheWord,' documents = ', AWord.DocumentCount, ' h - pinteger(state)^:=cnt; -end; - procedure TChmWriter.WriteTOPICS; -var - FHits: Integer; - i: Integer; +//var + //FHits: Integer; begin if FTopicsStream.Size = 0 then Exit; @@ -801,95 +1236,75 @@ begin PostAddStreamToArchive('$FIftiMain', '/', FFiftiMainStream); end; -procedure TChmWriter.WriteREADMEFile; -const DISCLAIMER_STR = 'This archive was not made by the MS HTML Help Workshop(r)(tm) program.'; -var - Entry: TFileEntryRec; +procedure TChmWriter.WriteInternalFilesAfter; begin - // This procedure puts a file in the archive that says it wasn't compiled with the MS compiler - Entry.Compressed := False; - Entry.DecompressedOffset := FSection0.Position; - FSection0.Write(DISCLAIMER_STR, SizeOf(DISCLAIMER_STR)); - Entry.DecompressedSize := FSection0.Position - Entry.DecompressedOffset; - Entry.Path := '/'; - Entry.Name := '_#_README_#_'; //try to use a name that won't conflict with normal names - FInternalFiles.AddEntry(Entry); + // This creates and writes the #ITBITS (empty) file to section0 + WriteITBITS; + // This creates and writes the #SYSTEM file to section0 + WriteSystem; + + end; procedure TChmWriter.WriteFinalCompressedFiles; begin + inherited WriteFinalCompressedFiles; WriteTOPICS; WriteURL_STR_TBL; WriteSTRINGS; WriteFiftiMain; end; - -procedure TChmWriter.WriteSection0; +procedure TChmWriter.FileAdded(AStream: TStream; const AEntry: TFileEntryRec); begin - FSection0.Position := 0; - FOutStream.CopyFrom(FSection0, FSection0.Size); + inherited FileAdded(AStream, AEntry); + if FullTextSearch then + CheckFileMakeSearchable(AStream, AEntry); end; -procedure TChmWriter.WriteSection1; +procedure TChmWriter.WriteInternalFilesBefore; begin - WriteContentToStream(FOutStream, FSection1); + inherited WriteInternalFilesBefore; + WriteIVB; + WriteOBJINST; end; -procedure TChmWriter.WriteDataSpaceFiles(const AStream: TStream); -var - Entry: TFileEntryRec; +constructor TChmWriter.Create(AOutStream: TStream; FreeStreamOnDestroy: Boolean); begin - // This procedure will write all files starting with :: - Entry.Compressed := False; // None of these files are compressed - - // ::DataSpace/NameList - Entry.DecompressedOffset := FSection0.Position; - Entry.DecompressedSize := WriteNameListToStream(FSection0, [snUnCompressed,snMSCompressed]); - Entry.Path := '::DataSpace/'; - Entry.Name := 'NameList'; - FInternalFiles.AddEntry(Entry, False); - - // ::DataSpace/Storage/MSCompressed/ControlData - Entry.DecompressedOffset := FSection0.Position; - Entry.DecompressedSize := WriteControlDataToStream(FSection0, 2, 2, 1); - Entry.Path := '::DataSpace/Storage/MSCompressed/'; - Entry.Name := 'ControlData'; - FInternalFiles.AddEntry(Entry, False); - - // ::DataSpace/Storage/MSCompressed/SpanInfo - Entry.DecompressedOffset := FSection0.Position; - Entry.DecompressedSize := WriteSpanInfoToStream(FSection0, FReadCompressedSize); - Entry.Path := '::DataSpace/Storage/MSCompressed/'; - Entry.Name := 'SpanInfo'; - FInternalFiles.AddEntry(Entry, False); - - // ::DataSpace/Storage/MSCompressed/Transform/List - Entry.DecompressedOffset := FSection0.Position; - Entry.DecompressedSize := WriteTransformListToStream(FSection0); - Entry.Path := '::DataSpace/Storage/MSCompressed/Transform/'; - Entry.Name := 'List'; - FInternalFiles.AddEntry(Entry, False); - - // ::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/ - // ::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable - Entry.DecompressedOffset := FSection0.Position; - Entry.DecompressedSize := WriteResetTableToStream(FSection0, FSection1ResetTable); - Entry.Path := '::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/'; - Entry.Name := 'ResetTable'; - FInternalFiles.AddEntry(Entry, True); - - - // ::DataSpace/Storage/MSCompressed/Content do this last - Entry.DecompressedOffset := FSection0.Position; - Entry.DecompressedSize := FSection1Size; // we will write it directly to FOutStream later - Entry.Path := '::DataSpace/Storage/MSCompressed/'; - Entry.Name := 'Content'; - FInternalFiles.AddEntry(Entry, False); + inherited Create(AOutStream, FreeStreamOnDestroy); + FStringsStream := TmemoryStream.Create; + FTopicsStream := TMemoryStream.Create; + FURLSTRStream := TMemoryStream.Create; + FURLTBLStream := TMemoryStream.Create; + FFiftiMainStream := TMemoryStream.Create; + FIndexedFiles := TIndexedWordList.Create; + FAvlStrings := TAVLTree.Create(@CompareStrings); // dedupe strings + FAvlURLStr := TAVLTree.Create(@CompareUrlStrs); // dedupe urltbl + binindex must resolve URL to topicid + SpareString := TStringIndex.Create; // We need an object to search in avltree + SpareUrlStr := TUrlStrIndex.Create; // to avoid create/free circles we keep one in spare + // for searching purposes +end; +destructor TChmWriter.Destroy; +begin + if Assigned(FContextStream) then FContextStream.Free; + FIndexedFiles.Free; + FStringsStream.Free; + FTopicsStream.Free; + FURLSTRStream.Free; + FURLTBLStream.Free; + FFiftiMainStream.Free; + SpareString.free; + SpareUrlStr.free; + FAvlUrlStr.FreeAndClear; + FAvlUrlStr.Free; + FAvlStrings.FreeAndClear; + FAvlStrings.Free; + inherited Destroy; end; + function TChmWriter.AddString(AString: String): LongWord; var NextBlock: DWord; @@ -987,158 +1402,7 @@ begin FURLTBLStream.WriteDWord(NtoLE(UrlIndex)); end; -function _AtEndOfData(arg: pointer): LongBool; cdecl; -begin - Result := TChmWriter(arg).AtEndOfData; -end; - -function TChmWriter.AtEndOfData: LongBool; -begin - Result := ForceExit or (FCurrentIndex >= FFileNames.Count-1); - if Result then - Result := Integer(FCurrentStream.Position) >= Integer(FCurrentStream.Size)-1; -end; - -function _GetData(arg: pointer; Count: LongInt; Buffer: Pointer): LongInt; cdecl; -begin - Result := TChmWriter(arg).GetData(Count, PByte(Buffer)); -end; - -function TChmWriter.GetData(Count: LongInt; Buffer: PByte): LongInt; -var - FileEntry: TFileEntryRec; -begin - Result := 0; - while (Result < Count) and (not AtEndOfData) do begin - Inc(Result, FCurrentStream.Read(Buffer[Result], Count-Result)); - if (Result < Count) and (not AtEndOfData) - then begin - // the current file has been read. move to the next file in the list - FCurrentStream.Position := 0; - Inc(FCurrentIndex); - ForceExit := OnGetFileData(FFileNames[FCurrentIndex], FileEntry.Path, FileEntry.Name, FCurrentStream); - FileEntry.DecompressedSize := FCurrentStream.Size; - FileEntry.DecompressedOffset := FReadCompressedSize; //269047723;//to test writing really large numbers - FileEntry.Compressed := True; - - if FullTextSearch then - CheckFileMakeSearchable(FCurrentStream, FileEntry); - - FInternalFiles.AddEntry(FileEntry); - // So the next file knows it's offset - Inc(FReadCompressedSize, FileEntry.DecompressedSize); - FCurrentStream.Position := 0; - end; - - // this is intended for programs to add perhaps a file - // after all the other files have been added. - if (AtEndOfData) - and (FCurrentStream <> FPostStream) then - begin - FPostStreamActive := True; - if Assigned(FOnLastFile) then - FOnLastFile(Self); - FCurrentStream.Free; - WriteFinalCompressedFiles; - FCurrentStream := FPostStream; - FCurrentStream.Position := 0; - Inc(FReadCompressedSize, FCurrentStream.Size); - end; - end; -end; - -function _WriteCompressedData(arg: pointer; Count: LongInt; Buffer: Pointer): LongInt; cdecl; -begin - Result := TChmWriter(arg).WriteCompressedData(Count, Buffer); -end; - -function TChmWriter.WriteCompressedData(Count: Longint; Buffer: Pointer): LongInt; -begin - // we allocate a MB at a time to limit memory reallocation since this - // writes usually 2 bytes at a time - if (FSection1 is TMemoryStream) and (FSection1.Position >= FSection1.Size-1) then begin - FSection1.Size := FSection1.Size+$100000; - end; - Result := FSection1.Write(Buffer^, Count); - Inc(FSection1Size, Result); -end; -procedure _MarkFrame(arg: pointer; UncompressedTotal, CompressedTotal: LongWord); cdecl; -begin - TChmWriter(arg).MarkFrame(UncompressedTotal, CompressedTotal); -end; - -procedure TChmWriter.MarkFrame(UnCompressedTotal, CompressedTotal: LongWord); - procedure WriteQWord(Value: QWord); - begin - FSection1ResetTable.Write(NToLE(Value), 8); - end; - procedure IncEntryCount; - var - OldPos: QWord; - Value: DWord; - begin - OldPos := FSection1ResetTable.Position; - FSection1ResetTable.Position := $4; - Value := LeToN(FSection1ResetTable.ReadDWord)+1; - FSection1ResetTable.Position := $4; - FSection1ResetTable.WriteDWord(NToLE(Value)); - FSection1ResetTable.Position := OldPos; - end; - procedure UpdateTotalSizes; - var - OldPos: QWord; - begin - OldPos := FSection1ResetTable.Position; - FSection1ResetTable.Position := $10; - WriteQWord(FReadCompressedSize); // size of read data that has been compressed - WriteQWord(CompressedTotal); - FSection1ResetTable.Position := OldPos; - end; -begin - if FSection1ResetTable.Size = 0 then begin - // Write the header - FSection1ResetTable.WriteDWord(NtoLE(DWord(2))); - FSection1ResetTable.WriteDWord(0); // number of entries. we will correct this with IncEntryCount - FSection1ResetTable.WriteDWord(NtoLE(DWord(8))); // Size of Entries (qword) - FSection1ResetTable.WriteDWord(NtoLE(DWord($28))); // Size of this header - WriteQWord(0); // Total Uncompressed Size - WriteQWord(0); // Total Compressed Size - WriteQWord(NtoLE($8000)); // Block Size - WriteQWord(0); // First Block start - end; - IncEntryCount; - UpdateTotalSizes; - WriteQWord(CompressedTotal); // Next Block Start - // We have to trim the last entry off when we are done because there is no next block in that case -end; - -{$IFDEF LZX_USETHREADS} -function TChmWriter.LTGetData(Sender: TLZXCompressor; WantedByteCount: Integer; - Buffer: Pointer): Integer; -begin - Result := GetData(WantedByteCount, Buffer); - //WriteLn('Wanted ', WantedByteCount, ' got ', Result); -end; - -function TChmWriter.LTIsEndOfFile(Sender: TLZXCompressor): Boolean; -begin - Result := AtEndOfData; -end; - -procedure TChmWriter.LTChunkDone(Sender: TLZXCompressor; - CompressedSize: Integer; UncompressedSize: Integer; Buffer: Pointer); -begin - WriteCompressedData(CompressedSize, Buffer); -end; - -procedure TChmWriter.LTMarkFrame(Sender: TLZXCompressor; - CompressedTotal: Integer; UncompressedTotal: Integer); -begin - MarkFrame(UncompressedTotal, CompressedTotal); - //WriteLn('Mark Frame C = ', CompressedTotal, ' U = ', UncompressedTotal); -end; -{$ENDIF} procedure TChmWriter.CheckFileMakeSearchable(AStream: TStream; AFileEntry: TFileEntryRec); @@ -1192,105 +1456,6 @@ begin Result := FTopicsStream.Size div 16; end; -constructor TChmWriter.Create(OutStream: TStream; FreeStreamOnDestroy: Boolean); -begin - if OutStream = nil then Raise Exception.Create('TChmWriter.OutStream Cannot be nil!'); - FCurrentStream := TMemoryStream.Create; - FCurrentIndex := -1; - FOutStream := OutStream; - FInternalFiles := TFileEntryList.Create; - FStringsStream := TmemoryStream.Create; - FTopicsStream := TMemoryStream.Create; - FURLSTRStream := TMemoryStream.Create; - FURLTBLStream := TMemoryStream.Create; - FFiftiMainStream := TMemoryStream.Create; - FSection0 := TMemoryStream.Create; - FSection1 := TMemoryStream.Create; - FSection1ResetTable := TMemoryStream.Create; - FDirectoryListings := TMemoryStream.Create; - FPostStream := TMemoryStream.Create;; - FDestroyStream := FreeStreamOnDestroy; - FFileNames := TStringList.Create; - FIndexedFiles := TIndexedWordList.Create; - FAvlStrings := TAVLTree.Create(@CompareStrings); // dedupe strings - FAvlURLStr := TAVLTree.Create(@CompareUrlStrs); // dedupe urltbl + binindex must resolve URL to topicid - SpareString := TStringIndex.Create; // We need an object to search in avltree - SpareUrlStr := TUrlStrIndex.Create; // to avoid create/free circles we keep one in spare - // for searching purposes -end; - -destructor TChmWriter.Destroy; -begin - if FDestroyStream then FOutStream.Free; - if Assigned(FContextStream) then FContextStream.Free; - FInternalFiles.Free; - FCurrentStream.Free; - FStringsStream.Free; - FTopicsStream.Free; - FURLSTRStream.Free; - FURLTBLStream.Free; - FFiftiMainStream.Free; - FSection0.Free; - FSection1.Free; - FSection1ResetTable.Free; - FDirectoryListings.Free; - FFileNames.Free; - FIndexedFiles.Free; - SpareString.free; - SpareUrlStr.free; - FAvlUrlStr.FreeAndClear; - FAvlUrlStr.Free; - FAvlStrings.FreeAndClear; - FAvlStrings.Free; - inherited Destroy; -end; - -procedure TChmWriter.Execute; -begin - InitITSFHeader; - FOutStream.Position := 0; - FSection1Size := 0; - - // write any internal files to FCurrentStream that we want in the compressed section - WriteIVB; - - // written to Section0 (uncompressed) - WriteREADMEFile; - - WriteOBJINST; - - // move back to zero so that we can start reading from zero :) - FReadCompressedSize := FCurrentStream.Size; - FCurrentStream.Position := 0; // when compressing happens, first the FCurrentStream is read - // before loading user files. So we can fill FCurrentStream with - // internal files first. - - // this gathers ALL files that should be in section1 (the compressed section) - StartCompressingStream; - FSection1.Size := FSection1Size; - - // This creates and writes the #ITBITS (empty) file to section0 - WriteITBITS; - // This creates and writes the #SYSTEM file to section0 - WriteSystem; - - - //this creates all special files in the archive that start with ::DataSpace - WriteDataSpaceFiles(FSection0); - - // creates all directory listings including header - CreateDirectoryListings; - - // do this after we have compressed everything so that we know the values that must be written - InitHeaderSectionTable; - - // Now we can write everything to FOutStream - WriteHeader(FOutStream); - WriteDirectoryListings(FOutStream); - WriteSection0; //does NOT include section 1 even though section0.content IS section1 - WriteSection1; // writes section 1 to FOutStream -end; - procedure TChmWriter.AppendTOC(AStream: TStream); begin FHasTOC := True; @@ -1848,23 +2013,23 @@ begin inc(blocknr); // Fixup header. hdr.ident[0]:=chr($3B); hdr.ident[1]:=chr($29); - hdr.flags :=NToLE($2); // bit $2 is always 1, bit $0400 1 if dir? (always on) - hdr.blocksize :=NToLE(defblocksize); // size of blocks (2048) + hdr.flags :=NToLE(word($2)); // bit $2 is always 1, bit $0400 1 if dir? (always on) + hdr.blocksize :=NToLE(word(defblocksize)); // size of blocks (2048) hdr.dataformat :=AlwaysX44; // "X44" always the same, see specs. hdr.unknown0 :=NToLE(0); // always 0 - hdr.lastlstblock :=NToLE(ListingBlocks-1); // index of last listing block in the file; - hdr.indexrootblock :=NToLE(blocknr-1); // Index of the root block in the file. - hdr.unknown1 :=NToLE(-1); // always -1 + hdr.lastlstblock :=NToLE(dword(ListingBlocks-1)); // index of last listing block in the file; + hdr.indexrootblock :=NToLE(dword(blocknr-1)); // Index of the root block in the file. + hdr.unknown1 :=NToLE(dword(-1)); // always -1 hdr.nrblock :=NToLE(blocknr); // Number of blocks hdr.treedepth :=NToLE(TreeDepth); // The depth of the tree of blocks (1 if no index blocks, 2 one level of index blocks, ...) hdr.nrkeywords :=NToLE(Totalentries); // number of keywords in the file. - hdr.codepage :=NToLE(1252); // Windows code page identifier (usually 1252 - Windows 3.1 US (ANSI)) + hdr.codepage :=NToLE(dword(1252)); // Windows code page identifier (usually 1252 - Windows 3.1 US (ANSI)) hdr.lcid :=NToLE(0); // ???? LCID from the HHP file. if not chw then - hdr.ischm :=NToLE(1) // 0 if this a BTREE and is part of a CHW file, 1 if it is a BTree and is part of a CHI or CHM file + hdr.ischm :=NToLE(dword(1)) // 0 if this a BTREE and is part of a CHW file, 1 if it is a BTree and is part of a CHI or CHM file else hdr.ischm :=NToLE(0); - hdr.unknown2 :=NToLE(10031); // Unknown. Almost always 10031. Also 66631 (accessib.chm, ieeula.chm, iesupp.chm, iexplore.chm, msoe.chm, mstask.chm, ratings.chm, wab.chm). + hdr.unknown2 :=NToLE(dword(10031)); // Unknown. Almost always 10031. Also 66631 (accessib.chm, ieeula.chm, iesupp.chm, iexplore.chm, msoe.chm, mstask.chm, ratings.chm, wab.chm). hdr.unknown3 :=NToLE(0); // unknown 0 hdr.unknown4 :=NToLE(0); // unknown 0 hdr.unknown5 :=NToLE(0); // unknown 0 @@ -1920,65 +2085,6 @@ begin end; -// this procedure is used to manually add files to compress to an internal stream that is -// processed before FileToCompress is called. Files added this way should not be -// duplicated in the FilesToCompress property. -procedure TChmWriter.AddStreamToArchive(AFileName, APath: String; AStream: TStream; Compress: Boolean = True); -var - TargetStream: TStream; - Entry: TFileEntryRec; -begin - // in case AddStreamToArchive is used after we should be writing to the post stream - if FPostStreamActive then - begin - PostAddStreamToArchive(AFileName, APath, AStream, Compress); - Exit; - end; - if AStream = nil then Exit; - if Compress then - TargetStream := FCurrentStream - else - TargetStream := FSection0; - - Entry.Name := AFileName; - Entry.Path := APath; - Entry.Compressed := Compress; - Entry.DecompressedOffset := TargetStream.Position; - Entry.DecompressedSize := AStream.Size; - if FullTextSearch then - CheckFileMakeSearchable(AStream, Entry); // Must check before we add it to the list so we know if the name needs to be added to #STRINGS - FInternalFiles.AddEntry(Entry); - AStream.Position := 0; - TargetStream.CopyFrom(AStream, AStream.Size); -end; - -procedure TChmWriter.PostAddStreamToArchive(AFileName, APath: String; - AStream: TStream; Compress: Boolean); -var - TargetStream: TStream; - Entry: TFileEntryRec; -begin - if AStream = nil then Exit; - if Compress then - TargetStream := FPostStream - else - TargetStream := FSection0; - - Entry.Name := AFileName; - Entry.Path := APath; - Entry.Compressed := Compress; - if not Compress then - Entry.DecompressedOffset := TargetStream.Position - else - Entry.DecompressedOffset := FReadCompressedSize + TargetStream.Position; - Entry.DecompressedSize := AStream.Size; - FInternalFiles.AddEntry(Entry); - AStream.Position := 0; - TargetStream.CopyFrom(AStream, AStream.Size); - if FullTextSearch then - CheckFileMakeSearchable(AStream, Entry); -end; - procedure TChmWriter.AddContext(AContext: DWord; ATopic: String); var Offset: DWord; @@ -1994,40 +2100,5 @@ begin FContextStream.WriteDWord(Offset); end; -procedure TChmWriter.StartCompressingStream; -var - {$IFNDEF LZX_USETHREADS} - LZXdata: Plzx_data; - WSize: LongInt; - {$ELSE} - Compressor: TLZXCompressor; - {$ENDIF} -begin - {$IFNDEF LZX_USETHREADS} - lzx_init(@LZXdata, LZX_WINDOW_SIZE, @_GetData, Self, @_AtEndOfData, - @_WriteCompressedData, Self, @_MarkFrame, Self); - - WSize := 1 shl LZX_WINDOW_SIZE; - while not AtEndOfData do begin - lzx_reset(LZXdata); - lzx_compress_block(LZXdata, WSize, True); - end; - - //we have to mark the last frame manually - MarkFrame(LZXdata^.len_uncompressed_input, LZXdata^.len_compressed_output); - - lzx_finish(LZXdata, nil); - {$ELSE} - Compressor := TLZXCompressor.Create(10); - Compressor.OnChunkDone :=@LTChunkDone; - Compressor.OnGetData :=@LTGetData; - Compressor.OnIsEndOfFile:=@LTIsEndOfFile; - Compressor.OnMarkFrame :=@LTMarkFrame; - Compressor.Execute(True); - //Sleep(20000); - Compressor.Free; - {$ENDIF} -end; - end. |
