source: trunk/Packages/bgrabitmap/bgramultifiletype.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 12.5 KB
Line 
1unit BGRAMultiFileType;
2
3{$mode objfpc}{$H+}
4{$MODESWITCH ADVANCEDRECORDS}
5
6interface
7
8uses
9 Classes, SysUtils, fgl;
10
11type
12
13 { TEntryFilename }
14
15 TEntryFilename = record
16 private
17 FExtension: utf8string;
18 FName: utf8string;
19 function GetFilename: utf8string;
20 function GetIsEmpty: boolean;
21 procedure SetExtension(AValue: utf8string);
22 procedure SetFilename(AValue: utf8string);
23 procedure SetName(AValue: utf8string);
24 public
25 class operator =(const AValue1,AValue2: TEntryFilename): boolean;
26 property Filename: utf8string read GetFilename write SetFilename;
27 property Name: utf8string read FName write SetName;
28 property Extension: utf8string read FExtension write SetExtension;
29 property IsEmpty: boolean read GetIsEmpty;
30 end;
31
32function EntryFilename(AName,AExtension: string): TEntryFilename; overload;
33function EntryFilename(AFilename: string): TEntryFilename; overload;
34
35type
36 TMultiFileContainer = class;
37
38 { TMultiFileEntry }
39
40 TMultiFileEntry = class
41 protected
42 FContainer: TMultiFileContainer;
43 function GetName: utf8string; virtual; abstract;
44 procedure SetName(AValue: utf8string); virtual; abstract;
45 function GetFileSize: int64; virtual;
46 function GetExtension: utf8string; virtual;
47 public
48 constructor Create(AContainer: TMultiFileContainer);
49 function CopyTo({%H-}ADestination: TStream): int64; virtual;
50 property Name: utf8string read GetName write SetName;
51 property Extension: utf8string read GetExtension;
52 property FileSize: int64 read GetFileSize;
53 property Container: TMultiFileContainer read FContainer;
54 end;
55
56 TMultiFileEntryList = specialize TFPGList<TMultiFileEntry>;
57
58 { TMultiFileContainer }
59
60 TMultiFileContainer = class
61 private
62 FEntries: TMultiFileEntryList;
63 protected
64 procedure Init; virtual;
65 function AddEntry(AEntry: TMultiFileEntry; AIndex: integer = -1): integer;
66 function GetCount: integer;
67 function GetEntry(AIndex: integer): TMultiFileEntry;
68 function CreateEntry(AName: utf8string; AExtension: utf8string; AContent: TStream): TMultiFileEntry; virtual; abstract;
69 function GetRawString(AIndex: integer): RawByteString;
70 function GetRawStringByFilename(AFilename: string): RawByteString;
71 procedure SetRawString(AIndex: integer; AValue: RawByteString);
72 procedure SetRawStringByFilename(AFilename: string; AValue: RawByteString);
73 public
74 constructor Create; overload;
75 constructor Create(AFilename: utf8string); overload;
76 constructor Create(AStream: TStream); overload;
77 constructor Create(AStream: TStream; AStartPos: Int64); overload;
78 function Add(AName: utf8string; AExtension: utf8string; AContent: TStream; AOverwrite: boolean = false; AOwnStream: boolean = true): integer; overload;
79 function Add(AName: utf8string; AExtension: utf8string; AContent: RawByteString; AOverwrite: boolean = false): integer; overload;
80 function Add(AFilename: TEntryFilename; AContent: TStream; AOverwrite: boolean = false; AOwnStream: boolean = true): integer; overload;
81 function Add(AFilename: TEntryFilename; AContent: RawByteString; AOverwrite: boolean = false): integer; overload;
82 procedure Clear; virtual;
83 destructor Destroy; override;
84 procedure LoadFromFile(AFilename: utf8string);
85 procedure LoadFromStream(AStream: TStream); virtual; abstract;
86 procedure LoadFromResource(AFilename: string); virtual;
87 procedure SaveToFile(AFilename: utf8string);
88 procedure SaveToStream(ADestination: TStream); virtual; abstract;
89 procedure Remove(AEntry: TMultiFileEntry); virtual;
90 procedure Delete(AIndex: integer); overload; virtual;
91 function Delete(AName: utf8string; AExtension: utf8string; ACaseSensitive: boolean = True): boolean; overload;
92 function Delete(AFilename: TEntryFilename; ACaseSensitive: boolean = True): boolean; overload;
93 function IndexOf(AEntry: TMultiFileEntry): integer; overload;
94 function IndexOf(AName: utf8string; AExtenstion: utf8string; ACaseSensitive: boolean = True): integer; overload; virtual;
95 function IndexOf(AFilename: TEntryFilename; ACaseSensitive: boolean = True): integer; overload;
96 property Count: integer read GetCount;
97 property Entry[AIndex: integer]: TMultiFileEntry read GetEntry;
98 property RawString[AIndex: integer]: RawByteString read GetRawString write SetRawString;
99 property RawStringByFilename[AFilename: string]: RawByteString read GetRawStringByFilename write SetRawStringByFilename;
100 end;
101
102implementation
103
104uses BGRAUTF8, strutils, BGRABitmapTypes;
105
106{ TEntryFilename }
107
108function TEntryFilename.GetFilename: utf8string;
109begin
110 if Extension = '' then
111 result := Name
112 else
113 result := Name+'.'+Extension;
114end;
115
116function TEntryFilename.GetIsEmpty: boolean;
117begin
118 result := (FName='') and (FExtension = '');
119end;
120
121procedure TEntryFilename.SetExtension(AValue: utf8string);
122var
123 i: Integer;
124begin
125 if FExtension=AValue then Exit;
126 for i := 1 to length(AValue) do
127 if AValue[i] in ['.','/'] then
128 raise Exception.Create('Invalid extension');
129 FExtension:=AValue;
130end;
131
132procedure TEntryFilename.SetFilename(AValue: utf8string);
133var
134 idxDot: SizeInt;
135begin
136 idxDot := RPos('.',AValue);
137 if idxDot = 0 then
138 begin
139 Name := AValue;
140 Extension := '';
141 end
142 else
143 begin
144 Name := copy(AValue,1,idxDot-1);
145 Extension := copy(AValue,idxDot+1,length(AValue)-idxDot);
146 end;
147end;
148
149procedure TEntryFilename.SetName(AValue: utf8string);
150var
151 i: Integer;
152begin
153 if FName=AValue then Exit;
154 for i := 1 to length(AValue) do
155 if AValue[i] = '/' then
156 raise Exception.Create('Invalid name');
157 FName:=AValue;
158end;
159
160function EntryFilename(AName, AExtension: string): TEntryFilename;
161begin
162 result.Name := AName;
163 result.Extension:= AExtension;
164end;
165
166function EntryFilename(AFilename: string): TEntryFilename;
167begin
168 result.Filename:= AFilename;
169end;
170
171class operator TEntryFilename.=(const AValue1, AValue2: TEntryFilename): boolean;
172begin
173 result := (AValue1.Name = AValue2.Name) and (AValue1.Extension = AValue2.Extension);
174end;
175
176{ TMultiFileEntry }
177
178function TMultiFileEntry.GetFileSize: int64;
179begin
180 result := 0;
181end;
182
183function TMultiFileEntry.GetExtension: utf8string;
184begin
185 result := '';
186end;
187
188constructor TMultiFileEntry.Create(AContainer: TMultiFileContainer);
189begin
190 FContainer := AContainer;
191end;
192
193function TMultiFileEntry.CopyTo(ADestination: TStream): int64;
194begin
195 result := 0;
196end;
197
198{ TMultiFileContainer }
199
200function TMultiFileContainer.GetCount: integer;
201begin
202 if Assigned(FEntries) then
203 result := FEntries.Count
204 else
205 result := 0;
206end;
207
208function TMultiFileContainer.GetEntry(AIndex: integer): TMultiFileEntry;
209begin
210 result := FEntries[AIndex];
211end;
212
213function TMultiFileContainer.GetRawString(AIndex: integer): RawByteString;
214var s: TStringStream;
215begin
216 s := TStringStream.Create('');
217 try
218 Entry[AIndex].CopyTo(s);
219 result := s.DataString;
220 finally
221 s.Free;
222 end;
223end;
224
225function TMultiFileContainer.GetRawStringByFilename(AFilename: string
226 ): RawByteString;
227var
228 idx: Integer;
229begin
230 idx := IndexOf(EntryFilename(AFilename));
231 if idx = -1 then
232 result := ''
233 else
234 result := GetRawString(idx);
235end;
236
237procedure TMultiFileContainer.SetRawString(AIndex: integer;
238 AValue: RawByteString);
239begin
240 with Entry[AIndex] do
241 Add(Name, Extension, AValue, true);
242end;
243
244procedure TMultiFileContainer.SetRawStringByFilename(AFilename: string;
245 AValue: RawByteString);
246var
247 f: TEntryFilename;
248begin
249 f := EntryFilename(AFilename);
250 Add(f.Name,f.Extension,AValue,true);
251end;
252
253procedure TMultiFileContainer.Init;
254begin
255 FEntries := TMultiFileEntryList.Create;
256end;
257
258function TMultiFileContainer.AddEntry(AEntry: TMultiFileEntry; AIndex: integer): integer;
259begin
260 if not Assigned(FEntries) then
261 raise exception.Create('Entry list not created');
262 if (AIndex >= 0) and (AIndex < FEntries.Count) then
263 begin
264 FEntries.Insert(AIndex, AEntry);
265 result := AIndex;
266 end
267 else
268 result := FEntries.Add(AEntry);
269end;
270
271constructor TMultiFileContainer.Create;
272begin
273 Init;
274end;
275
276constructor TMultiFileContainer.Create(AFilename: utf8string);
277begin
278 Init;
279 LoadFromFile(AFilename);
280end;
281
282constructor TMultiFileContainer.Create(AStream: TStream);
283begin
284 Init;
285 LoadFromStream(AStream);
286end;
287
288constructor TMultiFileContainer.Create(AStream: TStream; AStartPos: Int64);
289begin
290 Init;
291 AStream.Position := AStartPos;
292 LoadFromStream(AStream);
293end;
294
295function TMultiFileContainer.Add(AName: utf8string; AExtension: utf8string;
296 AContent: TStream; AOverwrite: boolean; AOwnStream: boolean): integer;
297var
298 index: Integer;
299 newEntry: TMultiFileEntry;
300 contentCopy: TMemoryStream;
301begin
302 index := IndexOf(AName,AExtension);
303 if index <> -1 then
304 begin
305 if AOverwrite then
306 Delete(index)
307 else
308 raise Exception.Create('Duplicate entry');
309 end;
310 if not AOwnStream then
311 begin
312 AContent.Position:= 0;
313 contentCopy := TMemoryStream.Create;
314 contentCopy.CopyFrom(AContent, AContent.Size);
315 newEntry := CreateEntry(AName, AExtension, contentCopy);
316 end else
317 newEntry := CreateEntry(AName, AExtension, AContent);
318 if Assigned(newEntry) then
319 result := AddEntry(newEntry, index)
320 else
321 raise exception.Create('Unable to create entry');
322end;
323
324function TMultiFileContainer.Add(AName: utf8string; AExtension: utf8string;
325 AContent: RawByteString; AOverwrite: boolean): integer;
326var stream: TMemoryStream;
327begin
328 stream := TMemoryStream.Create;
329 if length(AContent) > 0 then stream.Write(AContent[1],length(AContent));
330 result := Add(AName,AExtension,stream,AOverwrite);
331end;
332
333function TMultiFileContainer.Add(AFilename: TEntryFilename; AContent: TStream;
334 AOverwrite: boolean; AOwnStream: boolean): integer;
335begin
336 result := Add(AFilename.Name,AFilename.Extension, AContent, AOverwrite, AOwnStream);
337end;
338
339function TMultiFileContainer.Add(AFilename: TEntryFilename;
340 AContent: RawByteString; AOverwrite: boolean): integer;
341begin
342 result := Add(AFilename.Name,AFilename.Extension, AContent, AOverwrite);
343end;
344
345destructor TMultiFileContainer.Destroy;
346begin
347 Clear;
348 FreeAndNil(FEntries);
349 inherited Destroy;
350end;
351
352procedure TMultiFileContainer.LoadFromFile(AFilename: utf8string);
353var stream: TFileStream;
354begin
355 stream := TFileStream.Create(Utf8ToAnsi(AFilename), fmOpenRead);
356 LoadFromStream(stream);
357 stream.Free;
358end;
359
360procedure TMultiFileContainer.LoadFromResource(AFilename: string);
361var
362 stream: TStream;
363begin
364 stream := BGRAResource.GetResourceStream(AFilename);
365 try
366 LoadFromStream(stream);
367 finally
368 stream.Free;
369 end;
370end;
371
372procedure TMultiFileContainer.SaveToFile(AFilename: utf8string);
373var stream: TFileStream;
374begin
375 stream := TFileStream.Create(Utf8ToAnsi(AFilename), fmCreate);
376 SaveToStream(stream);
377 stream.Free;
378end;
379
380procedure TMultiFileContainer.Remove(AEntry: TMultiFileEntry);
381var
382 index: Integer;
383begin
384 index := IndexOf(AEntry);
385 if index = -1 then
386 raise exception.Create('Entry not found');
387 Delete(index);
388end;
389
390procedure TMultiFileContainer.Delete(AIndex: integer);
391begin
392 if (AIndex >= 0) and (AIndex < Count) then
393 begin
394 Entry[AIndex].Free;
395 FEntries.Delete(AIndex);
396 end else
397 raise ERangeError.Create('Index out of bounds');
398end;
399
400function TMultiFileContainer.Delete(AName: utf8string; AExtension: utf8string;
401 ACaseSensitive: boolean): boolean;
402var
403 index: Integer;
404begin
405 index := IndexOf(AName, AExtension, ACaseSensitive);
406 if index = -1 then
407 result := false
408 else
409 begin
410 Delete(index);
411 result := true;
412 end;
413end;
414
415function TMultiFileContainer.Delete(AFilename: TEntryFilename;
416 ACaseSensitive: boolean): boolean;
417begin
418 result := Delete(AFilename.Name,AFilename.Extension,ACaseSensitive);
419end;
420
421function TMultiFileContainer.IndexOf(AEntry: TMultiFileEntry): integer;
422begin
423 result := FEntries.IndexOf(AEntry);
424end;
425
426function TMultiFileContainer.IndexOf(AName: utf8string; AExtenstion: utf8string; ACaseSensitive: boolean): integer;
427var
428 i: Integer;
429begin
430 if ACaseSensitive then
431 begin
432 for i := 0 to Count-1 do
433 if (Entry[i].Name = AName) and (UTF8CompareText(Entry[i].Extension,AExtenstion) = 0) then
434 begin
435 result := i;
436 exit;
437 end;
438 end else
439 for i := 0 to Count-1 do
440 if (UTF8CompareText(Entry[i].Name,AName) = 0) and (UTF8CompareText(Entry[i].Extension,AExtenstion) = 0) then
441 begin
442 result := i;
443 exit;
444 end;
445 result := -1;
446end;
447
448function TMultiFileContainer.IndexOf(AFilename: TEntryFilename;
449 ACaseSensitive: boolean): integer;
450begin
451 result := IndexOf(AFilename.Name,AFilename.Extension,ACaseSensitive);
452end;
453
454procedure TMultiFileContainer.Clear;
455var
456 i: Integer;
457begin
458 for i := 0 to FEntries.Count-1 do
459 FEntries.Items[i].Free;
460 FEntries.Clear;
461end;
462
463end.
464
Note: See TracBrowser for help on using the repository browser.