Browse Source

* Fixed a potential bug where a value would not change if a contition wasn't met
* Made TITSFReader friendlier for inherited classes
* Small fix for threaded lzx compressor

git-svn-id: trunk@15547 -

andrew 15 years ago
parent
commit
a0f62a1336

+ 1 - 3
packages/chm/src/chmbase.pas

@@ -36,8 +36,6 @@ type
     Unknown_1: LongWord;
     Unknown_1: LongWord;
     TimeStamp: LongWord; //bigendian
     TimeStamp: LongWord; //bigendian
     LanguageID: LongWord;
     LanguageID: LongWord;
-    Guid1: TGuid;
-    Guid2: TGuid;
   end;
   end;
   TITSFHeaderEntry = record
   TITSFHeaderEntry = record
     PosFromZero: QWord;
     PosFromZero: QWord;
@@ -78,7 +76,7 @@ type
     Unknown5: LongInt; // = -1
     Unknown5: LongInt; // = -1
   end;
   end;
   
   
-  TPMGchunktype = (ctPMGL, ctPMGI, ctUnknown);
+  TDirChunkType = (ctPMGL, ctPMGI, ctAOLL, ctAOLI, ctUnknown);
   
   
   TPMGListChunk = record
   TPMGListChunk = record
     PMGLsig: array [0..3] of char;
     PMGLsig: array [0..3] of char;

+ 47 - 32
packages/chm/src/chmreader.pas

@@ -54,25 +54,26 @@ type
   protected
   protected
     fStream: TStream;
     fStream: TStream;
     fFreeStreamOnDestroy: Boolean;
     fFreeStreamOnDestroy: Boolean;
-    fChmHeader: TITSFHeader;
+    fITSFHeader: TITSFHeader;
     fHeaderSuffix: TITSFHeaderSuffix;
     fHeaderSuffix: TITSFHeaderSuffix;
     fDirectoryHeader: TITSPHeader;
     fDirectoryHeader: TITSPHeader;
     fDirectoryHeaderPos: QWord;
     fDirectoryHeaderPos: QWord;
     fDirectoryHeaderLength: QWord;
     fDirectoryHeaderLength: QWord;
     fDirectoryEntriesStartPos: QWord;
     fDirectoryEntriesStartPos: QWord;
-    fDirectoryEntries: array of TPMGListChunkEntry;
     fCachedEntry: TPMGListChunkEntry; //contains the last entry found by ObjectExists
     fCachedEntry: TPMGListChunkEntry; //contains the last entry found by ObjectExists
     fDirectoryEntriesCount: LongWord;
     fDirectoryEntriesCount: LongWord;
+    procedure ReadHeader; virtual;
+    procedure ReadHeaderEntries; virtual;
+    function  GetChunkType(Stream: TMemoryStream; ChunkIndex: LongInt): TDirChunkType;
+    procedure GetSections(out Sections: TStringList);
   private
   private
-    procedure ReadHeader;
-    function  GetChunkType(Stream: TMemoryStream; ChunkIndex: LongInt): TPMGchunktype;
     function  GetDirectoryChunk(Index: Integer; OutStream: TStream): Integer;
     function  GetDirectoryChunk(Index: Integer; OutStream: TStream): Integer;
     function  ReadPMGLchunkEntryFromStream(Stream: TMemoryStream; var PMGLEntry: TPMGListChunkEntry): Boolean;
     function  ReadPMGLchunkEntryFromStream(Stream: TMemoryStream; var PMGLEntry: TPMGListChunkEntry): Boolean;
     function  ReadPMGIchunkEntryFromStream(Stream: TMemoryStream; var PMGIEntry: TPMGIIndexChunkEntry): Boolean;
     function  ReadPMGIchunkEntryFromStream(Stream: TMemoryStream; var PMGIEntry: TPMGIIndexChunkEntry): Boolean;
     procedure LookupPMGLchunk(Stream: TMemoryStream; out PMGLChunk: TPMGListChunk);
     procedure LookupPMGLchunk(Stream: TMemoryStream; out PMGLChunk: TPMGListChunk);
     procedure LookupPMGIchunk(Stream: TMemoryStream; out PMGIChunk: TPMGIIndexChunk);
     procedure LookupPMGIchunk(Stream: TMemoryStream; out PMGIChunk: TPMGIIndexChunk);
 
 
-    procedure GetSections(out Sections: TStringList);
+
     function  GetBlockFromSection(SectionPrefix: String; StartPos: QWord; BlockLength: QWord): TMemoryStream;
     function  GetBlockFromSection(SectionPrefix: String; StartPos: QWord; BlockLength: QWord): TMemoryStream;
     function  FindBlocksFromUnCompressedAddr(var ResetTableEntry: TPMGListChunkEntry;
     function  FindBlocksFromUnCompressedAddr(var ResetTableEntry: TPMGListChunkEntry;
        out CompressedSize: QWord; out UnCompressedSize: QWord; out LZXResetTable: TLZXResetTableArr): QWord;  // Returns the blocksize
        out CompressedSize: QWord; out UnCompressedSize: QWord; out LZXResetTable: TLZXResetTableArr): QWord;  // Returns the blocksize
@@ -82,10 +83,10 @@ type
   public
   public
     ChmLastError: LongInt;
     ChmLastError: LongInt;
     function IsValidFile: Boolean;
     function IsValidFile: Boolean;
-    procedure GetCompleteFileList(ForEach: TFileEntryForEach);
-    function ObjectExists(Name: String): QWord; // zero if no. otherwise it is the size of the object
+    procedure GetCompleteFileList(ForEach: TFileEntryForEach; AIncludeInternalFiles: Boolean = True); virtual;
+    function ObjectExists(Name: String): QWord; virtual; // zero if no. otherwise it is the size of the object
                                                 // NOTE directories will return zero size even if they exist
                                                 // NOTE directories will return zero size even if they exist
-    function GetObject(Name: String): TMemoryStream; // YOU must Free the stream
+    function GetObject(Name: String): TMemoryStream; virtual; // YOU must Free the stream
     property CachedEntry: TPMGListChunkEntry read fCachedEntry;
     property CachedEntry: TPMGListChunkEntry read fCachedEntry;
   end;
   end;
   
   
@@ -181,7 +182,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-function ChunkType(Stream: TMemoryStream): TPMGchunktype;
+function ChunkType(Stream: TMemoryStream): TDirChunkType;
 var
 var
   ChunkID: array[0..3] of char;
   ChunkID: array[0..3] of char;
 begin
 begin
@@ -189,39 +190,46 @@ begin
   if Stream.Size< 4 then exit;
   if Stream.Size< 4 then exit;
   Move(Stream.Memory^, ChunkId[0], 4);
   Move(Stream.Memory^, ChunkId[0], 4);
   if ChunkID = 'PMGL' then Result := ctPMGL
   if ChunkID = 'PMGL' then Result := ctPMGL
-  else if ChunkID = 'PMGI' then Result := ctPMGI;
+  else if ChunkID = 'PMGI' then Result := ctPMGI
+  else if ChunkID = 'AOLL' then Result := ctAOLL
+  else if ChunkID = 'AOLI' then Result := ctAOLI;
 end;
 end;
 
 
 { TITSFReader }
 { TITSFReader }
 
 
 procedure TITSFReader.ReadHeader;
 procedure TITSFReader.ReadHeader;
-var
-fHeaderEntries: array [0..1] of TITSFHeaderEntry;
 begin
 begin
-  fStream.Position := 0;
-  fStream.Read(fChmHeader,SizeOf(fChmHeader));
+  fStream.Read(fITSFHeader,SizeOf(fITSFHeader));
 
 
   // Fix endian issues
   // Fix endian issues
   {$IFDEF ENDIAN_BIG}
   {$IFDEF ENDIAN_BIG}
-  fChmHeader.Version := LEtoN(fChmHeader.Version);
-  fChmHeader.HeaderLength := LEtoN(fChmHeader.HeaderLength);
+  fITSFHeader.Version := LEtoN(fITSFHeader.Version);
+  fITSFHeader.HeaderLength := LEtoN(fITSFHeader.HeaderLength);
   //Unknown_1
   //Unknown_1
-  fChmHeader.TimeStamp := BEtoN(fChmHeader.TimeStamp);//bigendian
-  fChmHeader.LanguageID := LEtoN(fChmHeader.LanguageID);
-  //Guid1
-  //Guid2
+  fITSFHeader.TimeStamp := BEtoN(fITSFHeader.TimeStamp);//bigendian
+  fITSFHeader.LanguageID := LEtoN(fITSFHeader.LanguageID);
   {$ENDIF}
   {$ENDIF}
+
+  if fITSFHeader.Version < 4 then
+   fStream.Seek(SizeOf(TGuid)*2, soCurrent);
   
   
   if not IsValidFile then Exit;
   if not IsValidFile then Exit;
   
   
+  ReadHeaderEntries;
+end;
+
+procedure TITSFReader.ReadHeaderEntries;
+var
+fHeaderEntries: array [0..1] of TITSFHeaderEntry;
+begin
   // Copy EntryData into memory
   // Copy EntryData into memory
   fStream.Read(fHeaderEntries[0], SizeOf(fHeaderEntries));
   fStream.Read(fHeaderEntries[0], SizeOf(fHeaderEntries));
 
 
-  if fChmHeader.Version > 2 then
+  if fITSFHeader.Version = 3 then
     fStream.Read(fHeaderSuffix.Offset, SizeOf(QWord));
     fStream.Read(fHeaderSuffix.Offset, SizeOf(QWord));
   fHeaderSuffix.Offset := LEtoN(fHeaderSuffix.Offset);
   fHeaderSuffix.Offset := LEtoN(fHeaderSuffix.Offset);
   // otherwise this is set in fill directory entries
   // otherwise this is set in fill directory entries
-  
+
   fStream.Position := LEtoN(fHeaderEntries[1].PosFromZero);
   fStream.Position := LEtoN(fHeaderEntries[1].PosFromZero);
   fDirectoryHeaderPos := LEtoN(fHeaderEntries[1].PosFromZero);
   fDirectoryHeaderPos := LEtoN(fHeaderEntries[1].PosFromZero);
   fStream.Read(fDirectoryHeader, SizeOf(fDirectoryHeader));
   fStream.Read(fDirectoryHeader, SizeOf(fDirectoryHeader));
@@ -509,7 +517,7 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-function TITSFReader.GetChunkType(Stream: TMemoryStream; ChunkIndex: LongInt): TPMGchunktype;
+function TITSFReader.GetChunkType(Stream: TMemoryStream; ChunkIndex: LongInt): TDirChunkType;
 var
 var
   Sig: array[0..3] of char;
   Sig: array[0..3] of char;
 begin
 begin
@@ -518,7 +526,9 @@ begin
 
 
   Stream.Read(Sig, 4);
   Stream.Read(Sig, 4);
   if Sig = 'PMGL' then Result := ctPMGL
   if Sig = 'PMGL' then Result := ctPMGL
-  else if Sig = 'PMGI' then Result := ctPMGI;
+  else if Sig = 'PMGI' then Result := ctPMGI
+  else if Sig = 'AOLL' then Result := ctAOLL
+  else if Sig = 'AOLI' then Result := ctAOLI;
 end;
 end;
 
 
 function TITSFReader.GetDirectoryChunk(Index: Integer; OutStream: TStream): Integer;
 function TITSFReader.GetDirectoryChunk(Index: Integer; OutStream: TStream): Integer;
@@ -599,6 +609,7 @@ end;
 constructor TITSFReader.Create(AStream: TStream; FreeStreamOnDestroy: Boolean);
 constructor TITSFReader.Create(AStream: TStream; FreeStreamOnDestroy: Boolean);
 begin
 begin
   fStream := AStream;
   fStream := AStream;
+  fStream.Position := 0;
   fFreeStreamOnDestroy := FreeStreamOnDestroy;
   fFreeStreamOnDestroy := FreeStreamOnDestroy;
   ReadHeader;
   ReadHeader;
   if not IsValidFile then Exit;
   if not IsValidFile then Exit;
@@ -606,7 +617,6 @@ end;
 
 
 destructor TITSFReader.Destroy;
 destructor TITSFReader.Destroy;
 begin
 begin
-  SetLength(fDirectoryEntries, 0);
   if fFreeStreamOnDestroy then FreeAndNil(fStream);
   if fFreeStreamOnDestroy then FreeAndNil(fStream);
 
 
   inherited Destroy;
   inherited Destroy;
@@ -615,13 +625,15 @@ end;
 function TITSFReader.IsValidFile: Boolean;
 function TITSFReader.IsValidFile: Boolean;
 begin
 begin
   if (fStream = nil) then ChmLastError := ERR_STREAM_NOT_ASSIGNED
   if (fStream = nil) then ChmLastError := ERR_STREAM_NOT_ASSIGNED
-  else if (fChmHeader.ITSFsig <> 'ITSF') then ChmLastError := ERR_NOT_VALID_FILE
-  else if (fChmHeader.Version <> 2) and (fChmHeader.Version <> 3) then
+  else if (fITSFHeader.ITSFsig <> 'ITSF') then ChmLastError := ERR_NOT_VALID_FILE
+  //else if (fITSFHeader.Version <> 2) and (fITSFHeader.Version <> 3)
+  else if not (fITSFHeader.Version in [2..4])
+  then
     ChmLastError := ERR_NOT_SUPPORTED_VERSION;
     ChmLastError := ERR_NOT_SUPPORTED_VERSION;
   Result := ChmLastError = ERR_NO_ERR;
   Result := ChmLastError = ERR_NO_ERR;
 end;
 end;
 
 
-procedure TITSFReader.GetCompleteFileList(ForEach: TFileEntryForEach);
+procedure TITSFReader.GetCompleteFileList(ForEach: TFileEntryForEach; AIncludeInternalFiles: Boolean = True);
 var
 var
   ChunkStream: TMemoryStream;
   ChunkStream: TMemoryStream;
   I : Integer;
   I : Integer;
@@ -662,7 +674,12 @@ begin
          Entry.DecompressedLength := GetCompressedInteger(ChunkStream);
          Entry.DecompressedLength := GetCompressedInteger(ChunkStream);
          if ChunkStream.Position > CutOffPoint then Break; // we have entered the quickref section
          if ChunkStream.Position > CutOffPoint then Break; // we have entered the quickref section
          fCachedEntry := Entry; // if the caller trys to get this data we already know where it is :)
          fCachedEntry := Entry; // if the caller trys to get this data we already know where it is :)
-         ForEach(Entry.Name, Entry.ContentOffset, Entry.DecompressedLength, Entry.ContentSection);
+         if  (Length(Entry.Name) = 1)
+         or (AIncludeInternalFiles
+              or
+             ((Length(Entry.Name) > 1) and (not(Entry.Name[2] in ['#','$',':']))))
+         then
+          ForEach(Entry.Name, Entry.ContentOffset, Entry.DecompressedLength, Entry.ContentSection);
        end;
        end;
      end;
      end;
     {$IFDEF CHM_DEBUG_CHUNKS}
     {$IFDEF CHM_DEBUG_CHUNKS}
@@ -841,7 +858,7 @@ begin
   end
   end
   else begin // we have to get it from ::DataSpace/Storage/[MSCompressed,Uncompressed]/ControlData
   else begin // we have to get it from ::DataSpace/Storage/[MSCompressed,Uncompressed]/ControlData
     GetSections(SectionNames);
     GetSections(SectionNames);
-    FmtStr(SectionName, '::DataSpace/Storage/%s/',[SectionNames[Entry.ContentSection-1]]);
+    FmtStr(SectionName, '::DataSpace/Storage/%s/',[SectionNames[Entry.ContentSection]]);
     Result := GetBlockFromSection(SectionName, Entry.ContentOffset, Entry.DecompressedLength);
     Result := GetBlockFromSection(SectionName, Entry.ContentOffset, Entry.DecompressedLength);
     SectionNames.Free;
     SectionNames.Free;
   end;
   end;
@@ -1235,8 +1252,6 @@ begin
     {$ENDIF}
     {$ENDIF}
     Sections.Add(WString);
     Sections.Add(WString);
   end;
   end;
-  // the sections are sorted alphabetically, this way section indexes will jive
-  Sections.Sort;
   Stream.Free;
   Stream.Free;
 end;
 end;
 
 

+ 7 - 3
packages/chm/src/chmwriter.pas

@@ -241,8 +241,6 @@ begin
     Unknown_1 := NToLE(DWord(1));
     Unknown_1 := NToLE(DWord(1));
     TimeStamp:= NToBE(MilliSecondOfTheDay(Now)); //bigendian
     TimeStamp:= NToBE(MilliSecondOfTheDay(Now)); //bigendian
     LanguageID := NToLE(DWord($0409)); // English / English_US
     LanguageID := NToLE(DWord($0409)); // English / English_US
-    Guid1 := ITSFHeaderGUID;
-    Guid2 := ITSFHeaderGUID;
   end;
   end;
 end;
 end;
 
 
@@ -314,6 +312,12 @@ end;
 procedure TITSFWriter.WriteHeader(Stream: TStream);
 procedure TITSFWriter.WriteHeader(Stream: TStream);
 begin
 begin
   Stream.Write(ITSFHeader, SizeOf(TITSFHeader));
   Stream.Write(ITSFHeader, SizeOf(TITSFHeader));
+
+  if ITSFHeader.Version < 4 then
+  begin
+    Stream.Write(ITSFHeaderGUID, SizeOf(TGuid));
+    Stream.Write(ITSFHeaderGUID, SizeOf(TGuid));
+  end;
   Stream.Write(HeaderSection0Table, SizeOf(TITSFHeaderEntry));
   Stream.Write(HeaderSection0Table, SizeOf(TITSFHeaderEntry));
   Stream.Write(HeaderSection1Table, SizeOf(TITSFHeaderEntry));
   Stream.Write(HeaderSection1Table, SizeOf(TITSFHeaderEntry));
   Stream.Write(HeaderSuffix, SizeOf(TITSFHeaderSuffix));
   Stream.Write(HeaderSuffix, SizeOf(TITSFHeaderSuffix));
@@ -897,7 +901,7 @@ begin
 
 
   lzx_finish(LZXdata, nil);
   lzx_finish(LZXdata, nil);
   {$ELSE}
   {$ELSE}
-  Compressor := TLZXCompressor.Create(10);
+  Compressor := TLZXCompressor.Create(4);
   Compressor.OnChunkDone  :=@LTChunkDone;
   Compressor.OnChunkDone  :=@LTChunkDone;
   Compressor.OnGetData    :=@LTGetData;
   Compressor.OnGetData    :=@LTGetData;
   Compressor.OnIsEndOfFile:=@LTIsEndOfFile;
   Compressor.OnIsEndOfFile:=@LTIsEndOfFile;

+ 9 - 3
packages/chm/src/lzxcompressthread.pas

@@ -249,7 +249,7 @@ begin
     FMasterThread.Resume;
     FMasterThread.Resume;
     if WaitForFinish then
     if WaitForFinish then
       While Running do
       While Running do
-        CheckSynchronize(50);
+        CheckSynchronize(10);
 end;
 end;
 
 
 { TLZXMasterThread }
 { TLZXMasterThread }
@@ -263,6 +263,7 @@ function TLZXMasterThread.BlockDone(Worker: TLZXWorkerThread; ABlock: PLZXFinish
 begin
 begin
   Lock;
   Lock;
   REsult := True;
   REsult := True;
+
   FCompressor.BlockIsFinished(ABlock);
   FCompressor.BlockIsFinished(ABlock);
   if DataRemains then
   if DataRemains then
     QueueThread(Worker)
     QueueThread(Worker)
@@ -349,7 +350,8 @@ begin
 
 
   Thread.CompressData(FBlockNumber);
   Thread.CompressData(FBlockNumber);
   Inc(FBlockNumber);
   Inc(FBlockNumber);
-  Thread.Resume;
+  if Thread.Suspended then
+    Thread.Resume;
   UnLockTmpData;
   UnLockTmpData;
 end;
 end;
 
 
@@ -370,7 +372,7 @@ begin
   //Suspend;
   //Suspend;
   while Working do
   while Working do
   begin
   begin
-      Sleep(50);
+      Sleep(0);
   end;
   end;
   FRunning:= False;
   FRunning:= False;
 end;
 end;
@@ -489,12 +491,16 @@ begin
   while not Terminated do
   while not Terminated do
   begin
   begin
     lzx_reset(LZXdata);
     lzx_reset(LZXdata);
+
     lzx_compress_block(LZXdata, WSize, True);
     lzx_compress_block(LZXdata, WSize, True);
 
 
     MasterThread.Synchronize(@NotifyMasterDone);
     MasterThread.Synchronize(@NotifyMasterDone);
 
 
     if ShouldSuspend then
     if ShouldSuspend then
+    begin
       Suspend;
       Suspend;
+    end;
+
   end;
   end;
 end;
 end;
 
 

+ 1 - 5
packages/chm/src/paslzxcomp.pas

@@ -285,9 +285,8 @@ begin
 	  if (leaves[leaves_left].freq <> 1) then begin
 	  if (leaves[leaves_left].freq <> 1) then begin
             leaves[leaves_left].freq := leaves[leaves_left].freq shr 1;
             leaves[leaves_left].freq := leaves[leaves_left].freq shr 1;
             codes_too_long := 0;
             codes_too_long := 0;
-            Inc(leaves_left);
           end;
           end;
-
+          Inc(leaves_left);
         end;
         end;
         if codes_too_long <> 0 then
         if codes_too_long <> 0 then
           raise Exception.Create('!codes_too_long');
           raise Exception.Create('!codes_too_long');
@@ -994,7 +993,6 @@ begin
   Fillchar(lzxd^.length_freq_table[0], NUM_SECONDARY_LENGTHS * sizeof(longint), 0);
   Fillchar(lzxd^.length_freq_table[0], NUM_SECONDARY_LENGTHS * sizeof(longint), 0);
   Fillchar(lzxd^.main_freq_table[0], lzxd^.main_tree_size * sizeof(longint), 0);
   Fillchar(lzxd^.main_freq_table[0], lzxd^.main_tree_size * sizeof(longint), 0);
   Fillchar(lzxd^.aligned_freq_table[0], LZX_ALIGNED_SIZE * sizeof(longint), 0);
   Fillchar(lzxd^.aligned_freq_table[0], LZX_ALIGNED_SIZE * sizeof(longint), 0);
-
   while ((lzxd^.left_in_block<>0) and ((lz_left_to_process(lzxd^.lzi)<>0) or not(lzxd^.at_eof(lzxd^.in_arg)))) do begin
   while ((lzxd^.left_in_block<>0) and ((lz_left_to_process(lzxd^.lzi)<>0) or not(lzxd^.at_eof(lzxd^.in_arg)))) do begin
     lz_compress(lzxd^.lzi, lzxd^.left_in_block);
     lz_compress(lzxd^.lzi, lzxd^.left_in_block);
 
 
@@ -1002,7 +1000,6 @@ begin
       lzxd^.left_in_frame := LZX_FRAME_SIZE;
       lzxd^.left_in_frame := LZX_FRAME_SIZE;
     end;
     end;
     
     
-    if lzxd^.at_eof(lzxd^.in_arg) then Sleep(500);
     if ((lzxd^.subdivide<0)
     if ((lzxd^.subdivide<0)
       or (lzxd^.left_in_block = 0)
       or (lzxd^.left_in_block = 0)
       or ((lz_left_to_process(lzxd^.lzi) = 0) and lzxd^.at_eof(lzxd^.in_arg))) then begin
       or ((lz_left_to_process(lzxd^.lzi) = 0) and lzxd^.at_eof(lzxd^.in_arg))) then begin
@@ -1023,7 +1020,6 @@ begin
 	lzx_write_bits(lzxd, 1, 0);
 	lzx_write_bits(lzxd, 1, 0);
 	lzxd^.need_1bit_header := 0;
 	lzxd^.need_1bit_header := 0;
       end;
       end;
-
       //* handle extra bits */
       //* handle extra bits */
       uncomp_bits := 0;
       uncomp_bits := 0;
       comp_bits := 0;
       comp_bits := 0;