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 |
|
---|