123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- { Copyright (C) <2005> <Andrew Haines> chmtypes.pas
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- 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. See the GNU Library General Public License
- for more details.
- You should have received a copy of the GNU Library General Public License
- along with this library; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- }
- {
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- }
- unit chmtypes;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils;
-
- type
- TSectionName = (snMSCompressed, snUnCompressed);
-
- TSectionNames = set of TSectionName;
-
- { TDirectoryChunk }
- TDirectoryChunk = class(TObject)
- private
- FHeaderSize: Integer;
- FQuickRefEntries: Word;
- Buffer: array[0..$1000-1] of byte;
- CurrentPos: Integer;
- FItemCount: Word;
- FClearCount: Integer;
- public
- function CanHold(ASize: Integer): Boolean;
- function FreeSpace: Integer;
- procedure WriteHeader(AHeader: Pointer);
- procedure WriteEntry(Size: Integer; Data: Pointer);
- procedure WriteChunkToStream(Stream: TStream); overload;
- procedure Clear;
- property ItemCount: Word read FItemCount;
- constructor Create(AHeaderSize: Integer);
- end;
-
- { TPMGIDirectoryChunk }
- TPMGIDirectoryChunk = class(TDirectoryChunk)
- private
- FChunkLevelCount: Integer;
- FParentChunk: TPMGIDirectoryChunk;
- public
- procedure WriteChunkToStream(Stream: TStream; var AIndex: Integer; Final: Boolean = False); overload;
- property ParentChunk: TPMGIDirectoryChunk read FParentChunk write FParentChunk;
- property ChunkLevelCount: Integer read FChunkLevelCount write FChunkLevelCount;
- end;
- PFileEntryRec = ^TFileEntryRec;
- TFileEntryRec = record
- Path: String;
- Name: String;
- DecompressedOffset: QWord;
- DecompressedSize: QWord;
- Compressed: Boolean; // True means it goes in section1 False means section0
- end;
- { TFileEntryList }
- TFileEntryList = class(TList)
- private
- FPaths: TStringList;
- function GetFileEntry(Index: Integer): TFileEntryRec;
- procedure SetFileEntry(Index: Integer; const AValue: TFileEntryRec);
- public
- function AddEntry(AFileEntry: TFileEntryRec; CheckPathIsAdded: Boolean = True): Integer;
- procedure Delete(Index: Integer);
- property FileEntry[Index: Integer]: TFileEntryRec read GetFileEntry write SetFileEntry;
- procedure Sort;
- constructor Create;
- destructor Destroy; override;
- end;
- implementation
- uses chmbase;
- { TDirectoryChunk }
- function TDirectoryChunk.CanHold(ASize: Integer): Boolean;
- begin
- Result := CurrentPos < $1000-1 - ASize - (SizeOf(Word) * (FQuickRefEntries+2));
- end;
- function TDirectoryChunk.FreeSpace: Integer;
- begin
- Result := $1000 - CurrentPos;
- end;
- procedure TDirectoryChunk.WriteHeader(AHeader: Pointer);
- begin
- Move(AHeader^, Buffer[0], FHeaderSize);
- end;
- procedure TDirectoryChunk.WriteEntry(Size: Integer; Data: Pointer);
- var
- ReversePos: Integer;
- Value: Word;
- begin
- if not CanHold(Size) then Raise Exception.Create('Trying to write past the end of the buffer');
- Move(Data^, Buffer[CurrentPos], Size);
- Inc(CurrentPos, Size);
- Inc(FItemCount);
-
- // now put a quickref entry if needed
- if ItemCount mod 5 = 0 then begin
- Inc(FQuickRefEntries);
- ReversePos := ($1000-1) - SizeOf(Word) - (SizeOf(Word)*FQuickRefEntries);
- Value := NtoLE(Word(CurrentPos - Size));
- Move(Value, Buffer[ReversePos], SizeOf(Word));
- end;
- end;
- procedure TDirectoryChunk.WriteChunkToStream(Stream: TStream);
- var
- ReversePos: Integer;
- begin
- ReversePos := $1000-1 - SizeOf(Word);
- FItemCount := NtoLE(ItemCount);
- Move(ItemCount, Buffer[ReversePos], SizeOf(Word));
- Stream.Write(Buffer[0], $1000);
- {$IFDEF DEBUG_CHM_CHUNKS}
- WriteLn('Writing ', Copy(PChar(@Buffer[0]),0,4),' ChunkToStream');
- {$ENDIF}
- end;
- procedure TDirectoryChunk.Clear;
- begin
- FillChar(Buffer, $1000, 0);
- FItemCount := 0;
- CurrentPos := FHeaderSize;
- FQuickRefEntries := 0;
- Inc(FClearCount);
- end;
- constructor TDirectoryChunk.Create(AHeaderSize: Integer);
- begin
- FHeaderSize := AHeaderSize;
- CurrentPos := FHeaderSize;
- end;
- { TFileEntryList }
- function TFileEntryList.GetFileEntry(Index: Integer): TFileEntryRec;
- begin
- Result := PFileEntryRec(Items[Index])^;
- end;
- procedure TFileEntryList.SetFileEntry(Index: Integer; const AValue: TFileEntryRec);
- begin
- PFileEntryRec(Items[Index])^ := AValue;
- end;
- function TFileEntryList.AddEntry(AFileEntry: TFileEntryRec; CheckPathIsAdded: Boolean = True): Integer;
- var
- TmpEntry: PFileEntryRec;
- begin
- New(TmpEntry);
- //WriteLn('Adding: ', AFileEntry.Path+AFileEntry.Name,' Size = ', AFileEntry.DecompressedSize,' Offset = ', AFileEntry.DecompressedOffset);
- if CheckPathIsAdded and (FPaths.IndexOf(AFileEntry.Path) < 0) then begin
- // all paths are included in the list of files in section 0 with a size and offset of 0
- FPaths.Add(AFileEntry.Path);
- TmpEntry^.Path := AFileEntry.Path;
- TmpEntry^.Name := '';
- TmpEntry^.DecompressedOffset := 0;
- TmpEntry^.DecompressedSize := 0;
- TmpEntry^.Compressed := False;
- (Self as TList).Add(TmpEntry);
- New(TmpEntry);
- end;
- TmpEntry^ := AFileEntry;
- Result := (Self as TList).Add(TmpEntry);
- end;
- procedure TFileEntryList.Delete(Index: Integer);
- begin
- Dispose(PFileEntryRec(Items[Index]));
- Inherited Delete(Index);
- end;
- function FileEntrySortFunc(Item1, Item2: PFileEntryRec): Integer;
- var
- Str1, Str2: String;
- begin
- Str1 := Item1^.Path + Item1^.Name;
- Str2 := Item2^.Path + Item2^.Name;
- Result := ChmCompareText(Str1, Str2);
- end;
- procedure TFileEntryList.Sort;
- begin
- Inherited Sort(TListSortCompare(@FileEntrySortFunc));
- end;
- constructor TFileEntryList.Create;
- begin
- Inherited Create;
- FPaths := TStringList.Create;
- end;
- destructor TFileEntryList.Destroy;
- var
- I: Integer;
- begin
- for I := Count-1 downto 0 do
- Delete(I);
- FPaths.Free;
- inherited Destroy;
- end;
- { TPMGIDirectoryChunk }
- procedure TPMGIDirectoryChunk.WriteChunkToStream(Stream: TStream; var AIndex: Integer
- ; Final: Boolean = False);
- var
- NewBuffer: array[0..512] of byte;
- EntryLength,
- WriteSize: Integer;
- OldPos, NewPos, NewStart: Int64;
- procedure FinishBlock;
- var
- Header: TPMGIIndexChunk;
- begin
- Inc(AIndex);
- Header.PMGIsig := 'PMGI';
- Header.UnusedSpace := FParentChunk.FreeSpace;
- FParentChunk.WriteHeader(@Header);
- FParentChunk.WriteChunkToStream(Stream, AIndex, Final);
- FParentChunk.Clear;
- end;
- begin
- if FItemCount < 1 then begin
- WriteLn('WHAT ARE YOU DOING!!');
- Dec(AIndex);
- Exit;
- end;
- OldPos := Stream.Position;
- WriteChunkToStream(Stream);
- NewPos := Stream.Position;
- Inc(FChunkLevelCount);
-
- if Final and (ChunkLevelCount < 2) then begin
- FParentChunk.Free;
- FParentChunk := nil;
- Exit;
- end;
- if FParentChunk = nil then FParentChunk := TPMGIDirectoryChunk.Create(FHeaderSize);
- NewStart := OldPos+FHeaderSize;
- Stream.Position := NewStart;
- EntryLength := GetCompressedInteger(Stream);
- WriteSize := (Stream.Position - NewStart) + EntryLength;
- Move(Buffer[FHeaderSize], NewBuffer[0], WriteSize);
- Inc(WriteSize, WriteCompressedInteger(@NewBuffer[WriteSize], AIndex));
- Stream.Position := NewPos;
- if not FParentChunk.CanHold(WriteSize) then begin
- FinishBlock;
- end;
-
- FParentChunk.WriteEntry(WriteSize, @NewBuffer[0]);
- if Final then FinishBlock;
- //WriteLn(ChunkLevelCount);
- end;
- end.
|