| 1 | unit BGRAMemDirectory;
|
|---|
| 2 |
|
|---|
| 3 | {$mode objfpc}{$H+}
|
|---|
| 4 |
|
|---|
| 5 | interface
|
|---|
| 6 |
|
|---|
| 7 | uses
|
|---|
| 8 | Classes, SysUtils, BGRAMultiFileType, fgl;
|
|---|
| 9 |
|
|---|
| 10 | const
|
|---|
| 11 | MemDirectoryFileHeader = 'TMemDirectory'#26#0#0;
|
|---|
| 12 | MemDirectoryEntry_FlagDirectory = 1; //entry is a directory
|
|---|
| 13 | MemDirectoryEntry_FlagCompressed = 2; //the stream is compressed
|
|---|
| 14 | MemDirectoryEntry_FlagSmallEntryPacked = $8000; //name and size <= 255
|
|---|
| 15 |
|
|---|
| 16 | type
|
|---|
| 17 | TMemDirectory = class;
|
|---|
| 18 | TEntryFilename = BGRAMultiFileType.TEntryFilename;
|
|---|
| 19 |
|
|---|
| 20 | type
|
|---|
| 21 | TMemDirectoryPath = specialize TFPGList<TEntryFilename>;
|
|---|
| 22 |
|
|---|
| 23 | { TMemDirectoryEntry }
|
|---|
| 24 |
|
|---|
| 25 | TMemDirectoryEntry = class(TMultiFileEntry)
|
|---|
| 26 | private
|
|---|
| 27 | FStream: TStream;
|
|---|
| 28 | function GetIsCompressed: boolean;
|
|---|
| 29 | function GetCompressedSize: int64;
|
|---|
| 30 | function GetIsDirectory: boolean;
|
|---|
| 31 | procedure SetIsCompressed(AValue: boolean);
|
|---|
| 32 | procedure LoadExtraFromEmbeddedStream(ADataStream: TStream; AStartPos: int64);
|
|---|
| 33 | procedure SaveToEmbeddedStream(AEntryStream, ADataStream: TStream; AStartPos: int64; out uncompressedSize: int64);
|
|---|
| 34 | protected
|
|---|
| 35 | FFlags: Word;
|
|---|
| 36 | FName,FExtension: utf8String;
|
|---|
| 37 | FUncompressedSize: int64;
|
|---|
| 38 | FEmbeddedStreamPos: int64;
|
|---|
| 39 | FMemDirectory: TMemDirectory;
|
|---|
| 40 | function GetName: utf8string; override;
|
|---|
| 41 | procedure SetName(AValue: utf8string); override;
|
|---|
| 42 | function GetFileSize: int64; override;
|
|---|
| 43 | function GetExtension: utf8string; override;
|
|---|
| 44 | function InternalCopyTo({%H-}ADestination: TStream): int64;
|
|---|
| 45 | public
|
|---|
| 46 | function CopyTo({%H-}ADestination: TStream): int64; override;
|
|---|
| 47 | constructor Create(AContainer: TMultiFileContainer; AFilename: TEntryFilename; AUncompressedStream: TStream; AOwnStream: boolean); overload;
|
|---|
| 48 | constructor CreateDirectory(AContainer: TMultiFileContainer; AFilename: TEntryFilename);
|
|---|
| 49 | constructor CreateFromData(AContainer: TMultiFileContainer; AFilename: TEntryFilename; AStream: TStream; AOwnStream: boolean; AUncompressedSize: int64; AFlags: Word);
|
|---|
| 50 | destructor Destroy; override;
|
|---|
| 51 | property EmbeddedStreamPos: int64 read FEmbeddedStreamPos write FEmbeddedStreamPos;
|
|---|
| 52 | property IsCompressed: boolean read GetIsCompressed write SetIsCompressed;
|
|---|
| 53 | property IsDirectory: boolean read GetIsDirectory;
|
|---|
| 54 | property CompressedSize: int64 read GetCompressedSize;
|
|---|
| 55 | property Flags: Word read FFlags;
|
|---|
| 56 | property MemDirectory: TMemDirectory read FMemDirectory;
|
|---|
| 57 | end;
|
|---|
| 58 |
|
|---|
| 59 | TMemDirectory = class(TMultiFileContainer)
|
|---|
| 60 | private
|
|---|
| 61 | FParentDirectory: TMemDirectory;
|
|---|
| 62 | function GetEntryCompressed(AIndex: integer): boolean;
|
|---|
| 63 | function GetIsDirectory(AIndex: integer): boolean;
|
|---|
| 64 | function GetDirectory(AIndex: integer): TMemDirectory;
|
|---|
| 65 | procedure SetEntryCompressed(AIndex: integer; AValue: boolean);
|
|---|
| 66 | protected
|
|---|
| 67 | function CreateEntry(AName: utf8string; AExtension: utf8string; AContent: TStream): TMultiFileEntry; override;
|
|---|
| 68 | function SplitPath(APath: utf8string): TMemDirectoryPath;
|
|---|
| 69 | public
|
|---|
| 70 | constructor Create(AParentDirectory: TMemDirectory = nil);
|
|---|
| 71 | procedure LoadFromStream(AStream: TStream); override;
|
|---|
| 72 | class function CheckHeader(AStream: TStream): boolean;
|
|---|
| 73 | procedure LoadFromEmbeddedStream(ARootStream, ADataStream: TStream; AStartPos: int64);
|
|---|
| 74 | procedure SaveToStream(ADestination: TStream); override;
|
|---|
| 75 | procedure SaveToEmbeddedStream(ARootDest, ADataDest: TStream; AStartPos: int64);
|
|---|
| 76 | function AddDirectory(AName: utf8string; AExtension: utf8string= ''; ACaseSensitive: boolean= true): integer;
|
|---|
| 77 | function FindPath(APath: utf8String; ACaseSensitive: boolean = true): TMemDirectory;
|
|---|
| 78 | function FindEntry(APath: utf8String; ACaseSensitive: boolean = true): TMemDirectoryEntry;
|
|---|
| 79 | procedure CopyTo(ADest: TMemDirectory; ARecursive: boolean);
|
|---|
| 80 | property IsEntryCompressed[AIndex: integer]: boolean read GetEntryCompressed write SetEntryCompressed;
|
|---|
| 81 | property Directory[AIndex: integer]: TMemDirectory read GetDirectory;
|
|---|
| 82 | property IsDirectory[AIndex: integer]: boolean read GetIsDirectory;
|
|---|
| 83 | property ParentDirectory: TMemDirectory read FParentDirectory;
|
|---|
| 84 | end;
|
|---|
| 85 |
|
|---|
| 86 | implementation
|
|---|
| 87 |
|
|---|
| 88 | uses zstream, BGRAUTF8, strutils;
|
|---|
| 89 |
|
|---|
| 90 | type
|
|---|
| 91 | TDirEntryRecord = packed record
|
|---|
| 92 | Flags: Word;
|
|---|
| 93 | FilenameSize: Word;
|
|---|
| 94 | Offset: int64;
|
|---|
| 95 | end;
|
|---|
| 96 |
|
|---|
| 97 | { TMemDirectory }
|
|---|
| 98 |
|
|---|
| 99 | function TMemDirectory.GetEntryCompressed(AIndex: integer): boolean;
|
|---|
| 100 | begin
|
|---|
| 101 | result := (Entry[AIndex] as TMemDirectoryEntry).IsCompressed;
|
|---|
| 102 | end;
|
|---|
| 103 |
|
|---|
| 104 | function TMemDirectory.GetIsDirectory(AIndex: integer): boolean;
|
|---|
| 105 | begin
|
|---|
| 106 | result := (Entry[AIndex] as TMemDirectoryEntry).IsDirectory;
|
|---|
| 107 | end;
|
|---|
| 108 |
|
|---|
| 109 | function TMemDirectory.GetDirectory(AIndex: integer): TMemDirectory;
|
|---|
| 110 | begin
|
|---|
| 111 | result := (Entry[AIndex] as TMemDirectoryEntry).MemDirectory;
|
|---|
| 112 | end;
|
|---|
| 113 |
|
|---|
| 114 | procedure TMemDirectory.SetEntryCompressed(AIndex: integer; AValue: boolean);
|
|---|
| 115 | begin
|
|---|
| 116 | (Entry[AIndex] as TMemDirectoryEntry).IsCompressed := AValue;
|
|---|
| 117 | end;
|
|---|
| 118 |
|
|---|
| 119 | function TMemDirectory.CreateEntry(AName: utf8string; AExtension: utf8string;
|
|---|
| 120 | AContent: TStream): TMultiFileEntry;
|
|---|
| 121 | begin
|
|---|
| 122 | result := TMemDirectoryEntry.Create(self, EntryFilename(AName, AExtension), AContent, true);
|
|---|
| 123 | end;
|
|---|
| 124 |
|
|---|
| 125 | procedure TMemDirectory.LoadFromStream(AStream: TStream);
|
|---|
| 126 | var rootPos, rootSize: integer;
|
|---|
| 127 | header: string;
|
|---|
| 128 | rootStream: TStream;
|
|---|
| 129 | startPos: Int64;
|
|---|
| 130 | begin
|
|---|
| 131 | startPos := AStream.Position;
|
|---|
| 132 | setlength(header, length(MemDirectoryFileHeader));
|
|---|
| 133 | AStream.ReadBuffer(header[1], length(header));
|
|---|
| 134 | if header<>MemDirectoryFileHeader then
|
|---|
| 135 | raise exception.Create('Invalid header');
|
|---|
| 136 | rootPos := LEReadInt64(AStream);
|
|---|
| 137 | if rootPos = 0 then
|
|---|
| 138 | raise exception.Create('Invalid root offset');
|
|---|
| 139 | rootSize := LEReadInt64(AStream);
|
|---|
| 140 | if rootSize <= 4 then
|
|---|
| 141 | raise exception.Create('Invalid root size');
|
|---|
| 142 | AStream.Position:= rootPos + startPos;
|
|---|
| 143 | rootStream:= TMemoryStream.Create;
|
|---|
| 144 | try
|
|---|
| 145 | rootStream.CopyFrom(AStream, rootSize);
|
|---|
| 146 | LoadFromEmbeddedStream(rootStream, AStream, startPos);
|
|---|
| 147 | finally
|
|---|
| 148 | rootStream.Free;
|
|---|
| 149 | end;
|
|---|
| 150 | end;
|
|---|
| 151 |
|
|---|
| 152 | class function TMemDirectory.CheckHeader(AStream: TStream): boolean;
|
|---|
| 153 | var
|
|---|
| 154 | startPos: Int64;
|
|---|
| 155 | header: string;
|
|---|
| 156 | begin
|
|---|
| 157 | startPos := AStream.Position;
|
|---|
| 158 | setlength(header, length(MemDirectoryFileHeader));
|
|---|
| 159 | AStream.Read(header[1], length(header));
|
|---|
| 160 | result := (header=MemDirectoryFileHeader);
|
|---|
| 161 | AStream.Position:= startPos;
|
|---|
| 162 | end;
|
|---|
| 163 |
|
|---|
| 164 | procedure TMemDirectory.LoadFromEmbeddedStream(ARootStream, ADataStream: TStream;
|
|---|
| 165 | AStartPos: int64);
|
|---|
| 166 | var
|
|---|
| 167 | nbEntries,i: LongInt;
|
|---|
| 168 | entryRec: TDirEntryRecord;
|
|---|
| 169 | filename: string;
|
|---|
| 170 | entryData: TStream;
|
|---|
| 171 | newEntry: TMemDirectoryEntry;
|
|---|
| 172 | compressedSize, uncompressedSize: Int64;
|
|---|
| 173 |
|
|---|
| 174 | begin
|
|---|
| 175 | Clear;
|
|---|
| 176 | ARootStream.Position := 0;
|
|---|
| 177 | nbEntries := LEReadLongint(ARootStream);
|
|---|
| 178 | for i := 1 to nbEntries do
|
|---|
| 179 | begin
|
|---|
| 180 | ARootStream.ReadBuffer({%H-}entryRec, sizeof(entryRec));
|
|---|
| 181 | entryRec.Offset:= LEtoN(entryRec.Offset);
|
|---|
| 182 | entryRec.Flags:= LEtoN(entryRec.Flags);
|
|---|
| 183 | entryRec.FilenameSize:= LEtoN(entryRec.FilenameSize);
|
|---|
| 184 |
|
|---|
| 185 | if (entryRec.Flags and MemDirectoryEntry_FlagSmallEntryPacked) <> 0 then
|
|---|
| 186 | begin
|
|---|
| 187 | entryRec.Flags := entryRec.Flags xor MemDirectoryEntry_FlagSmallEntryPacked;
|
|---|
| 188 | compressedSize := entryRec.FilenameSize shr 8;
|
|---|
| 189 | uncompressedSize := compressedSize;
|
|---|
| 190 | entryRec.FilenameSize := entryRec.FilenameSize and 255;
|
|---|
| 191 | end else
|
|---|
| 192 | begin
|
|---|
| 193 | compressedSize := LEReadInt64(ARootStream);
|
|---|
| 194 | uncompressedSize := LEReadInt64(ARootStream);
|
|---|
| 195 | end;
|
|---|
| 196 |
|
|---|
| 197 | setlength(filename, entryRec.FilenameSize);
|
|---|
| 198 | if length(filename)> 0 then
|
|---|
| 199 | ARootStream.ReadBuffer(filename[1], entryRec.FilenameSize);
|
|---|
| 200 |
|
|---|
| 201 | ADataStream.Position:= entryRec.Offset + AStartPos;
|
|---|
| 202 | entryData := TMemoryStream.Create;
|
|---|
| 203 | try
|
|---|
| 204 | if compressedSize <> 0 then
|
|---|
| 205 | entryData.CopyFrom(ADataStream, compressedSize);
|
|---|
| 206 | newEntry := TMemDirectoryEntry.CreateFromData(self, EntryFilename(filename), entryData, true,
|
|---|
| 207 | uncompressedSize, entryRec.Flags);
|
|---|
| 208 | newEntry.LoadExtraFromEmbeddedStream(ADataStream, AStartPos);
|
|---|
| 209 | AddEntry(newEntry);
|
|---|
| 210 | entryData := nil;
|
|---|
| 211 | finally
|
|---|
| 212 | entryData.Free;
|
|---|
| 213 | end;
|
|---|
| 214 | end;
|
|---|
| 215 | end;
|
|---|
| 216 |
|
|---|
| 217 | procedure TMemDirectory.SaveToStream(ADestination: TStream);
|
|---|
| 218 | var rootPos,rootSize: integer;
|
|---|
| 219 | header: string;
|
|---|
| 220 | rootRecPos, startPos, endPos: int64;
|
|---|
| 221 | rootStream: TStream;
|
|---|
| 222 | begin
|
|---|
| 223 | startPos := ADestination.Position;
|
|---|
| 224 | header := MemDirectoryFileHeader;
|
|---|
| 225 | ADestination.WriteBuffer(header[1], length(header));
|
|---|
| 226 |
|
|---|
| 227 | rootRecPos := ADestination.Position;
|
|---|
| 228 | LEWriteInt64(ADestination,0); //root pos
|
|---|
| 229 | LEWriteInt64(ADestination,0); //root size
|
|---|
| 230 |
|
|---|
| 231 | rootStream := TMemoryStream.Create;
|
|---|
| 232 | try
|
|---|
| 233 | SaveToEmbeddedStream(rootStream, ADestination, startPos);
|
|---|
| 234 | rootStream.Position := 0;
|
|---|
| 235 | rootPos := ADestination.Position - startPos;
|
|---|
| 236 | rootSize := rootStream.Size;
|
|---|
| 237 | ADestination.CopyFrom(rootStream, rootStream.Size);
|
|---|
| 238 | FreeAndNil(rootStream);
|
|---|
| 239 | endPos := ADestination.Position;
|
|---|
| 240 | ADestination.Position := rootRecPos;
|
|---|
| 241 | LEWriteInt64(ADestination, rootPos);
|
|---|
| 242 | LEWriteInt64(ADestination, rootSize);
|
|---|
| 243 | ADestination.Position := endPos;
|
|---|
| 244 | finally
|
|---|
| 245 | rootStream.Free;
|
|---|
| 246 | end;
|
|---|
| 247 | end;
|
|---|
| 248 |
|
|---|
| 249 | procedure TMemDirectory.SaveToEmbeddedStream(ARootDest, ADataDest: TStream;
|
|---|
| 250 | AStartPos: int64);
|
|---|
| 251 | var
|
|---|
| 252 | entryRec: TDirEntryRecord;
|
|---|
| 253 | entryStream: TMemoryStream;
|
|---|
| 254 | curEntry: TMemDirectoryEntry;
|
|---|
| 255 | filename: string;
|
|---|
| 256 | i: Integer;
|
|---|
| 257 | uncompressedSize: int64;
|
|---|
| 258 | begin
|
|---|
| 259 | LEWriteLongint(ARootDest, Count);
|
|---|
| 260 | entryStream := TMemoryStream.Create;
|
|---|
| 261 | try
|
|---|
| 262 | for i := 0 to Count-1 do
|
|---|
| 263 | begin
|
|---|
| 264 | curEntry := Entry[i] as TMemDirectoryEntry;
|
|---|
| 265 | entryStream.Clear;
|
|---|
| 266 | curEntry.SaveToEmbeddedStream(entryStream, ADataDest, AStartPos, uncompressedSize);
|
|---|
| 267 |
|
|---|
| 268 | entryRec.Offset:= ADataDest.Position - AStartPos;
|
|---|
| 269 | entryRec.Offset:= NtoLE(entryRec.Offset);
|
|---|
| 270 | if curEntry.Extension <> '' then
|
|---|
| 271 | filename := curEntry.Name+'.'+curEntry.Extension
|
|---|
| 272 | else
|
|---|
| 273 | filename := curEntry.Name;
|
|---|
| 274 |
|
|---|
| 275 | if ((curEntry.Flags and MemDirectoryEntry_FlagCompressed)=0) and
|
|---|
| 276 | (Length(filename)<=255) and (entryStream.Size<=255) then
|
|---|
| 277 | begin
|
|---|
| 278 | entryRec.Flags:= curEntry.Flags or MemDirectoryEntry_FlagSmallEntryPacked;
|
|---|
| 279 | entryRec.Flags:= NtoLE(entryRec.Flags);
|
|---|
| 280 | entryRec.FilenameSize:= length(filename) + (entryStream.Size shl 8);
|
|---|
| 281 | entryRec.FilenameSize := NtoLE(entryRec.FilenameSize);
|
|---|
| 282 | ARootDest.WriteBuffer(entryRec, sizeof(entryRec));
|
|---|
| 283 | end else
|
|---|
| 284 | begin
|
|---|
| 285 | entryRec.Flags:= curEntry.Flags;
|
|---|
| 286 | entryRec.Flags:= NtoLE(entryRec.Flags);
|
|---|
| 287 | entryRec.FilenameSize:= length(filename);
|
|---|
| 288 | entryRec.FilenameSize := NtoLE(entryRec.FilenameSize);
|
|---|
| 289 | ARootDest.WriteBuffer(entryRec, sizeof(entryRec));
|
|---|
| 290 | LEWriteInt64(ARootDest, entryStream.Size);
|
|---|
| 291 | LEWriteInt64(ARootDest, uncompressedSize);
|
|---|
| 292 | end;
|
|---|
| 293 |
|
|---|
| 294 | if filename <> '' then
|
|---|
| 295 | ARootDest.WriteBuffer(filename[1], length(filename));
|
|---|
| 296 |
|
|---|
| 297 | entryStream.Position:= 0;
|
|---|
| 298 | ADataDest.CopyFrom(entryStream, entryStream.Size);
|
|---|
| 299 | end;
|
|---|
| 300 | finally
|
|---|
| 301 | entryStream.Free;
|
|---|
| 302 | end;
|
|---|
| 303 | end;
|
|---|
| 304 |
|
|---|
| 305 | function TMemDirectory.AddDirectory(AName: utf8string; AExtension: utf8string;
|
|---|
| 306 | ACaseSensitive: boolean): integer;
|
|---|
| 307 | var
|
|---|
| 308 | newEntry: TMemDirectoryEntry;
|
|---|
| 309 | begin
|
|---|
| 310 | result := IndexOf(AName,AExtension,ACaseSensitive);
|
|---|
| 311 | if result <> -1 then
|
|---|
| 312 | begin
|
|---|
| 313 | if not IsDirectory[result] then
|
|---|
| 314 | raise exception.Create('There is already a file with this name and extension');
|
|---|
| 315 | exit;
|
|---|
| 316 | end;
|
|---|
| 317 | newEntry := TMemDirectoryEntry.CreateDirectory(self, EntryFilename(AName, AExtension));
|
|---|
| 318 | result := AddEntry(newEntry);
|
|---|
| 319 | end;
|
|---|
| 320 |
|
|---|
| 321 | function TMemDirectory.FindPath(APath: utf8String; ACaseSensitive: boolean): TMemDirectory;
|
|---|
| 322 | var
|
|---|
| 323 | path: TMemDirectoryPath;
|
|---|
| 324 | idxPath: integer;
|
|---|
| 325 | idxSub: LongInt;
|
|---|
| 326 | begin
|
|---|
| 327 | path := SplitPath(APath);
|
|---|
| 328 | result := self;
|
|---|
| 329 | if path.Items[0].IsEmpty then
|
|---|
| 330 | begin
|
|---|
| 331 | idxPath := 1;
|
|---|
| 332 | while Assigned(result.ParentDirectory) do result := result.ParentDirectory;
|
|---|
| 333 | end
|
|---|
| 334 | else
|
|---|
| 335 | idxPath := 0;
|
|---|
| 336 |
|
|---|
| 337 | while idxPath < path.Count do
|
|---|
| 338 | begin
|
|---|
| 339 | idxSub := result.IndexOf(path[idxPath], ACaseSensitive);
|
|---|
| 340 | if idxSub= -1 then
|
|---|
| 341 | begin
|
|---|
| 342 | result := nil;
|
|---|
| 343 | break;
|
|---|
| 344 | end;
|
|---|
| 345 | result := result.Directory[idxSub];
|
|---|
| 346 | inc(idxPath);
|
|---|
| 347 | end;
|
|---|
| 348 |
|
|---|
| 349 | path.Free;
|
|---|
| 350 | end;
|
|---|
| 351 |
|
|---|
| 352 | function TMemDirectory.FindEntry(APath: utf8String; ACaseSensitive: boolean): TMemDirectoryEntry;
|
|---|
| 353 | var
|
|---|
| 354 | path: TMemDirectoryPath;
|
|---|
| 355 | idxPath: integer;
|
|---|
| 356 | idxSub, idxEntry: LongInt;
|
|---|
| 357 | curDir: TMemDirectory;
|
|---|
| 358 | begin
|
|---|
| 359 | path := SplitPath(APath);
|
|---|
| 360 | curDir := self;
|
|---|
| 361 | if path.Items[0].IsEmpty then
|
|---|
| 362 | begin
|
|---|
| 363 | idxPath := 1;
|
|---|
| 364 | while Assigned(curDir.ParentDirectory) do curDir := curDir.ParentDirectory;
|
|---|
| 365 | end
|
|---|
| 366 | else
|
|---|
| 367 | idxPath := 0;
|
|---|
| 368 |
|
|---|
| 369 | while idxPath < path.Count-1 do
|
|---|
| 370 | begin
|
|---|
| 371 | idxSub := curDir.IndexOf(path[idxPath], ACaseSensitive);
|
|---|
| 372 | if idxSub= -1 then
|
|---|
| 373 | begin
|
|---|
| 374 | curDir := nil;
|
|---|
| 375 | break;
|
|---|
| 376 | end;
|
|---|
| 377 | curDir := curDir.Directory[idxSub];
|
|---|
| 378 | inc(idxPath);
|
|---|
| 379 | end;
|
|---|
| 380 |
|
|---|
| 381 | if Assigned(curDir) and (idxPath < path.Count) then
|
|---|
| 382 | begin
|
|---|
| 383 | idxEntry := curDir.IndexOf(path[idxPath], ACaseSensitive);
|
|---|
| 384 | if idxEntry = -1 then
|
|---|
| 385 | result := nil
|
|---|
| 386 | else
|
|---|
| 387 | result := curDir.Entry[idxEntry] as TMemDirectoryEntry;
|
|---|
| 388 | end
|
|---|
| 389 | else
|
|---|
| 390 | result := nil;
|
|---|
| 391 |
|
|---|
| 392 | path.Free;
|
|---|
| 393 | end;
|
|---|
| 394 |
|
|---|
| 395 | procedure TMemDirectory.CopyTo(ADest: TMemDirectory; ARecursive: boolean);
|
|---|
| 396 | var
|
|---|
| 397 | i, idxDir: Integer;
|
|---|
| 398 | entryContent: TMemoryStream;
|
|---|
| 399 | begin
|
|---|
| 400 | for i := 0 to Count-1 do
|
|---|
| 401 | if IsDirectory[i] and ARecursive then
|
|---|
| 402 | begin
|
|---|
| 403 | idxDir := ADest.AddDirectory(Entry[i].Name,Entry[i].Extension);
|
|---|
| 404 | Directory[i].CopyTo(ADest.Directory[idxDir], true);
|
|---|
| 405 | end else
|
|---|
| 406 | begin
|
|---|
| 407 | entryContent := TMemoryStream.Create;
|
|---|
| 408 | Entry[i].CopyTo(entryContent);
|
|---|
| 409 | ADest.Add(Entry[i].Name,Entry[i].Extension,entryContent,false,true);
|
|---|
| 410 | end;
|
|---|
| 411 | end;
|
|---|
| 412 |
|
|---|
| 413 | function TMemDirectory.SplitPath(APath: utf8string): TMemDirectoryPath;
|
|---|
| 414 | var idx,idxSlash: integer;
|
|---|
| 415 | begin
|
|---|
| 416 | result := TMemDirectoryPath.Create;
|
|---|
| 417 | idx := 1;
|
|---|
| 418 | repeat
|
|---|
| 419 | idxSlash := PosEx('/',APath,idx);
|
|---|
| 420 | if idxSlash = 0 then
|
|---|
| 421 | begin
|
|---|
| 422 | result.Add(EntryFilename(copy(APath, idx, length(APath)-idx+1)));
|
|---|
| 423 | break;
|
|---|
| 424 | end else
|
|---|
| 425 | begin
|
|---|
| 426 | result.Add(EntryFilename(copy(APath, idx, idxSlash-idx)));
|
|---|
| 427 | idx := idxSlash+1;
|
|---|
| 428 | end;
|
|---|
| 429 | until false;
|
|---|
| 430 | end;
|
|---|
| 431 |
|
|---|
| 432 | constructor TMemDirectory.Create(AParentDirectory: TMemDirectory);
|
|---|
| 433 | begin
|
|---|
| 434 | inherited Create;
|
|---|
| 435 | FParentDirectory := AParentDirectory;
|
|---|
| 436 | end;
|
|---|
| 437 |
|
|---|
| 438 | { TMemDirectoryEntry }
|
|---|
| 439 |
|
|---|
| 440 | function TMemDirectoryEntry.GetIsCompressed: boolean;
|
|---|
| 441 | begin
|
|---|
| 442 | result := (FFlags and MemDirectoryEntry_FlagCompressed) <> 0;
|
|---|
| 443 | end;
|
|---|
| 444 |
|
|---|
| 445 | function TMemDirectoryEntry.GetCompressedSize: int64;
|
|---|
| 446 | begin
|
|---|
| 447 | if not IsDirectory and Assigned(FStream) then
|
|---|
| 448 | result := FStream.Size
|
|---|
| 449 | else
|
|---|
| 450 | result := 0;
|
|---|
| 451 | end;
|
|---|
| 452 |
|
|---|
| 453 | function TMemDirectoryEntry.GetIsDirectory: boolean;
|
|---|
| 454 | begin
|
|---|
| 455 | result := (FFlags and MemDirectoryEntry_FlagDirectory) <> 0;
|
|---|
| 456 | end;
|
|---|
| 457 |
|
|---|
| 458 | procedure TMemDirectoryEntry.SetIsCompressed(AValue: boolean);
|
|---|
| 459 | var compressedStream,decompressed: TMemoryStream;
|
|---|
| 460 | compression: Tcompressionstream;
|
|---|
| 461 | begin
|
|---|
| 462 | if AValue = IsCompressed then exit;
|
|---|
| 463 |
|
|---|
| 464 | if Assigned(FStream) then
|
|---|
| 465 | begin
|
|---|
| 466 | if AValue then //compress
|
|---|
| 467 | begin
|
|---|
| 468 | compressedStream := TMemoryStream.Create;
|
|---|
| 469 | compression := nil;
|
|---|
| 470 | try
|
|---|
| 471 | compression := Tcompressionstream.create(cldefault, compressedStream, true);
|
|---|
| 472 | FStream.Position := 0;
|
|---|
| 473 | compression.CopyFrom(FStream,FStream.Size);
|
|---|
| 474 | FStream.Free;
|
|---|
| 475 | FStream := compressedStream;
|
|---|
| 476 | compressedStream := nil;
|
|---|
| 477 | FFlags := FFlags xor MemDirectoryEntry_FlagCompressed;
|
|---|
| 478 | finally
|
|---|
| 479 | compression.Free;
|
|---|
| 480 | compressedStream.Free;
|
|---|
| 481 | end;
|
|---|
| 482 | end else
|
|---|
| 483 | begin //decompress
|
|---|
| 484 | decompressed := TMemoryStream.Create;
|
|---|
| 485 | try
|
|---|
| 486 | InternalCopyTo(decompressed);
|
|---|
| 487 | FStream.Free;
|
|---|
| 488 | FStream := decompressed;
|
|---|
| 489 | decompressed := nil;
|
|---|
| 490 | FFlags := FFlags xor MemDirectoryEntry_FlagCompressed;
|
|---|
| 491 | finally
|
|---|
| 492 | decompressed.Free;
|
|---|
| 493 | end;
|
|---|
| 494 | end;
|
|---|
| 495 | end else
|
|---|
| 496 | FFlags := FFlags xor MemDirectoryEntry_FlagCompressed;
|
|---|
| 497 | end;
|
|---|
| 498 |
|
|---|
| 499 | function TMemDirectoryEntry.GetName: utf8string;
|
|---|
| 500 | begin
|
|---|
| 501 | result := FName;
|
|---|
| 502 | end;
|
|---|
| 503 |
|
|---|
| 504 | procedure TMemDirectoryEntry.SetName(AValue: utf8string);
|
|---|
| 505 | begin
|
|---|
| 506 | while AValue[length(AValue)] = '.' do delete(AValue, length(AValue), 1);
|
|---|
| 507 | FName := AValue;
|
|---|
| 508 | end;
|
|---|
| 509 |
|
|---|
| 510 | function TMemDirectoryEntry.GetFileSize: int64;
|
|---|
| 511 | begin
|
|---|
| 512 | if IsDirectory then
|
|---|
| 513 | result := 0
|
|---|
| 514 | else
|
|---|
| 515 | Result:= FUncompressedSize;
|
|---|
| 516 | end;
|
|---|
| 517 |
|
|---|
| 518 | function TMemDirectoryEntry.GetExtension: utf8string;
|
|---|
| 519 | begin
|
|---|
| 520 | Result:= FExtension;
|
|---|
| 521 | end;
|
|---|
| 522 |
|
|---|
| 523 | function TMemDirectoryEntry.InternalCopyTo(ADestination: TStream): int64;
|
|---|
| 524 | var
|
|---|
| 525 | decomp: Tdecompressionstream;
|
|---|
| 526 | begin
|
|---|
| 527 | if not Assigned(FStream) then exit(0);
|
|---|
| 528 | if IsCompressed then
|
|---|
| 529 | begin
|
|---|
| 530 | FStream.Position := 0;
|
|---|
| 531 | decomp := Tdecompressionstream.Create(FStream,true);
|
|---|
| 532 | try
|
|---|
| 533 | result := ADestination.CopyFrom(decomp,FUncompressedSize);
|
|---|
| 534 | finally
|
|---|
| 535 | decomp.Free;
|
|---|
| 536 | end;
|
|---|
| 537 | end else
|
|---|
| 538 | begin
|
|---|
| 539 | FStream.Position := 0;
|
|---|
| 540 | result := ADestination.CopyFrom(FStream, FStream.Size);
|
|---|
| 541 | end;
|
|---|
| 542 | end;
|
|---|
| 543 |
|
|---|
| 544 | function TMemDirectoryEntry.CopyTo(ADestination: TStream): int64;
|
|---|
| 545 | begin
|
|---|
| 546 | if IsDirectory then exit(0);
|
|---|
| 547 | result := InternalCopyTo(ADestination);
|
|---|
| 548 | end;
|
|---|
| 549 |
|
|---|
| 550 | constructor TMemDirectoryEntry.Create(AContainer: TMultiFileContainer; AFilename: TEntryFilename;
|
|---|
| 551 | AUncompressedStream: TStream; AOwnStream: boolean);
|
|---|
| 552 | begin
|
|---|
| 553 | CreateFromData(AContainer, AFilename, AUncompressedStream, AOwnStream, AUncompressedStream.Size, 0);
|
|---|
| 554 | end;
|
|---|
| 555 |
|
|---|
| 556 | constructor TMemDirectoryEntry.CreateFromData(AContainer: TMultiFileContainer; AFilename: TEntryFilename;
|
|---|
| 557 | AStream: TStream; AOwnStream: boolean;
|
|---|
| 558 | AUncompressedSize: int64; AFlags: Word);
|
|---|
| 559 | begin
|
|---|
| 560 | inherited Create(AContainer);
|
|---|
| 561 | Name := AFilename.Name;
|
|---|
| 562 | FExtension:= AFilename.Extension;
|
|---|
| 563 | if AOwnStream then
|
|---|
| 564 | FStream := AStream
|
|---|
| 565 | else
|
|---|
| 566 | begin
|
|---|
| 567 | FStream := TMemoryStream.Create;
|
|---|
| 568 | AStream.Position:= 0;
|
|---|
| 569 | FStream.CopyFrom(AStream, AStream.Size);
|
|---|
| 570 | end;
|
|---|
| 571 | FUncompressedSize:= AUncompressedSize;
|
|---|
| 572 | FFlags:= AFlags;
|
|---|
| 573 | FMemDirectory := nil;
|
|---|
| 574 | end;
|
|---|
| 575 |
|
|---|
| 576 | procedure TMemDirectoryEntry.SaveToEmbeddedStream(AEntryStream, ADataStream: TStream;
|
|---|
| 577 | AStartPos: int64; out uncompressedSize: int64);
|
|---|
| 578 | var
|
|---|
| 579 | entryStartPos: Int64;
|
|---|
| 580 | begin
|
|---|
| 581 | if IsDirectory then
|
|---|
| 582 | begin
|
|---|
| 583 | if not Assigned(FMemDirectory) then
|
|---|
| 584 | raise exception.Create('Directory not allocated');
|
|---|
| 585 | FreeAndNil(FStream);
|
|---|
| 586 | IsCompressed:= false;
|
|---|
| 587 | entryStartPos := AEntryStream.Position;
|
|---|
| 588 | FMemDirectory.SaveToEmbeddedStream(AEntryStream, ADataStream, AStartPos);
|
|---|
| 589 | uncompressedSize:= AEntryStream.Position - entryStartPos;
|
|---|
| 590 | end else
|
|---|
| 591 | if Assigned(FStream) then
|
|---|
| 592 | begin
|
|---|
| 593 | FStream.Position:= 0;
|
|---|
| 594 | AEntryStream.CopyFrom(FStream, FStream.Size);
|
|---|
| 595 | uncompressedSize:= FUncompressedSize;
|
|---|
| 596 | end;
|
|---|
| 597 | end;
|
|---|
| 598 |
|
|---|
| 599 | procedure TMemDirectoryEntry.LoadExtraFromEmbeddedStream(ADataStream: TStream;
|
|---|
| 600 | AStartPos: int64);
|
|---|
| 601 | begin
|
|---|
| 602 | if IsDirectory and Assigned(FStream) then
|
|---|
| 603 | begin
|
|---|
| 604 | IsCompressed:= false;
|
|---|
| 605 | if not Assigned(FMemDirectory) then
|
|---|
| 606 | FMemDirectory := TMemDirectory.Create(Container as TMemDirectory);
|
|---|
| 607 | FMemDirectory.LoadFromEmbeddedStream(FStream, ADataStream, AStartPos);
|
|---|
| 608 | FreeAndNil(FStream);
|
|---|
| 609 | end;
|
|---|
| 610 | end;
|
|---|
| 611 |
|
|---|
| 612 | constructor TMemDirectoryEntry.CreateDirectory(AContainer: TMultiFileContainer;
|
|---|
| 613 | AFilename: TEntryFilename);
|
|---|
| 614 | begin
|
|---|
| 615 | Name := AFilename.Name;
|
|---|
| 616 | FExtension:= AFilename.Extension;
|
|---|
| 617 | FStream := nil;
|
|---|
| 618 | FUncompressedSize:= 0;
|
|---|
| 619 | FFlags := MemDirectoryEntry_FlagDirectory;
|
|---|
| 620 | FContainer := AContainer;
|
|---|
| 621 | FMemDirectory := TMemDirectory.Create(Container as TMemDirectory);
|
|---|
| 622 | end;
|
|---|
| 623 |
|
|---|
| 624 | destructor TMemDirectoryEntry.Destroy;
|
|---|
| 625 | begin
|
|---|
| 626 | FStream.Free;
|
|---|
| 627 | FMemDirectory.Free;
|
|---|
| 628 | inherited Destroy;
|
|---|
| 629 | end;
|
|---|
| 630 |
|
|---|
| 631 | end.
|
|---|
| 632 |
|
|---|