source: trunk/Packages/bgrabitmap/bgralazresource.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 10.5 KB
Line 
1unit BGRALazResource;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, BGRAMultiFileType;
9
10type
11 { TLazResourceEntry }
12
13 TLazResourceEntry = class(TMultiFileEntry)
14 private
15 procedure Serialize(ADestination: TStream);
16 protected
17 FName: utf8string;
18 FValueType: utf8string;
19 FContent: TStream;
20 function GetName: utf8string; override;
21 procedure SetName(AValue: utf8string); override;
22 function GetExtension: utf8string; override;
23 function GetFileSize: int64; override;
24 public
25 constructor Create(AContainer: TMultiFileContainer; AName: utf8string; AValueType: utf8string; AContent: TStream);
26 destructor Destroy; override;
27 function CopyTo(ADestination: TStream): int64; override;
28 end;
29
30 { TFormDataEntry }
31
32 TFormDataEntry = class(TLazResourceEntry)
33 protected
34 FTextContent: TStream;
35 procedure RequireTextContent;
36 function GetExtension: utf8string; override;
37 function GetFileSize: int64; override;
38 public
39 constructor Create(AContainer: TMultiFileContainer; AName: utf8string; ABinaryContent: TStream);
40 destructor Destroy; override;
41 function CopyTo(ADestination: TStream): int64; override;
42 end;
43
44 { TLazResourceContainer }
45
46 TLazResourceContainer = class(TMultiFileContainer)
47 protected
48 function CreateEntry(AName: utf8string; AExtension: utf8string; AContent: TStream): TMultiFileEntry; override;
49 public
50 procedure LoadFromStream(AStream: TStream); override;
51 procedure SaveToStream(ADestination: TStream); override;
52 end;
53
54implementation
55
56uses LResources, BGRAUTF8;
57
58{ TFormDataEntry }
59
60procedure TFormDataEntry.RequireTextContent;
61begin
62 if FTextContent = nil then
63 begin
64 FTextContent:= TMemoryStream.Create;
65 FContent.Position:= 0;
66 LRSObjectBinaryToText(FContent, FTextContent);
67 end;
68end;
69
70function TFormDataEntry.GetExtension: utf8string;
71begin
72 Result:= 'lfm';
73end;
74
75function TFormDataEntry.GetFileSize: int64;
76begin
77 RequireTextContent;
78 Result:= FTextContent.Size;
79end;
80
81constructor TFormDataEntry.Create(AContainer: TMultiFileContainer;
82 AName: utf8string; ABinaryContent: TStream);
83begin
84 inherited Create(AContainer,AName,'FORMDATA',ABinaryContent);
85end;
86
87destructor TFormDataEntry.Destroy;
88begin
89 FreeAndNil(FTextContent);
90 inherited Destroy;
91end;
92
93function TFormDataEntry.CopyTo(ADestination: TStream): int64;
94begin
95 RequireTextContent;
96 if FTextContent.Size = 0 then
97 result := 0
98 else
99 begin
100 FTextContent.Position:= 0;
101 result := ADestination.CopyFrom(FTextContent,FTextContent.Size);
102 end;
103end;
104
105{ TLazResourceEntry }
106
107procedure TLazResourceEntry.Serialize(ADestination: TStream);
108begin
109 FContent.Position := 0;
110 BinaryToLazarusResourceCode(FContent, ADestination, Name, FValueType);
111end;
112
113function TLazResourceEntry.GetName: utf8string;
114begin
115 Result:= FName;
116end;
117
118procedure TLazResourceEntry.SetName(AValue: utf8string);
119begin
120 if AValue = FName then exit;
121 if Container.IndexOf(AVAlue, Extension) <> -1 then
122 raise Exception.Create('Name is already used for this extension');
123 FName := AValue;
124end;
125
126function TLazResourceEntry.GetExtension: utf8string;
127begin
128 Result:= FValueType;
129end;
130
131function TLazResourceEntry.GetFileSize: int64;
132begin
133 Result:= FContent.Size;
134end;
135
136destructor TLazResourceEntry.Destroy;
137begin
138 FreeAndNil(FContent);
139 inherited Destroy;
140end;
141
142constructor TLazResourceEntry.Create(AContainer: TMultiFileContainer; AName: utf8string; AValueType: utf8string;
143 AContent: TStream);
144begin
145 inherited Create(AContainer);
146 FName := AName;
147 FValueType := UTF8UpperCase(AValueType);
148 FContent := AContent;
149end;
150
151function TLazResourceEntry.CopyTo(ADestination: TStream): int64;
152begin
153 if FContent.Size = 0 then
154 result := 0
155 else
156 begin
157 FContent.Position:= 0;
158 result := ADestination.CopyFrom(FContent, FContent.Size);
159 end;
160end;
161
162{ TLazResourceContainer }
163
164procedure TLazResourceContainer.LoadFromStream(AStream: TStream);
165const
166 entryStart = 'LazarusResources.Add(';
167 entryEnd = ');';
168 whiteSpace = [' ',#9,#10,#13,#26];
169var
170 fileContent: String;
171 filePos : integer;
172
173 procedure SkipWhitespace;
174 begin
175 while (filePos <= length(fileContent)) and (fileContent[filePos] in whiteSpace) do inc(filePos);
176 end;
177
178 procedure SkipComma;
179 begin
180 SkipWhitespace;
181 if (filePos <= length(fileContent)) and (fileContent[filePos] = ',') then
182 inc(filePos)
183 else
184 raise Exception.Create('Comma expected');
185 end;
186
187 function ParseString(ignoreCommas: boolean): TStream;
188 var
189 expectPlus: boolean;
190
191 procedure AppendChar(c: char);
192 begin
193 result.WriteByte(ord(c));
194 end;
195
196 function ParseNumber: integer;
197 var numberStart, errPos: integer;
198 s: String;
199 begin
200 numberStart:= filePos;
201 if (filePos <= length(fileContent)) and (fileContent[filePos] = '$') then
202 begin
203 inc(filePos);
204 while (filePos <= length(fileContent)) and (fileContent[filePos] in['0'..'9','a'..'f','A'..'F']) do inc(filePos);
205 end else
206 begin
207 while (filePos <= length(fileContent)) and (fileContent[filePos] in['0'..'9']) do inc(filePos);
208 end;
209 s := copy(fileContent,numberStart,filePos-numberStart);
210 val(s, result, errPos);
211 if errPos <> 0 then
212 raise exception.Create('Invalid number "' + s + '"');
213 end;
214
215 function ParseStringPart: boolean;
216 var charCode: integer;
217 begin
218 SkipWhitespace;
219 if filePos <= length(fileContent) then
220 begin
221 if expectPlus then
222 if fileContent[filePos] <> '+' then
223 begin
224 result := false;
225 expectPlus := false;
226 exit;
227 end else
228 inc(filePos);
229
230 case fileContent[filePos] of
231 '+': raise exception.Create('Unexpected "+"');
232 '''': begin
233 inc(filePos);
234 while (filePos <= length(fileContent)) do
235 begin
236 if fileContent[filePos] = '''' then
237 begin
238 inc(filePos);
239 if (filePos <= length(fileContent)) and (fileContent[filePos] = '''') then
240 begin
241 AppendChar('''');
242 inc(filePos);
243 end
244 else break;
245 end else
246 if fileContent[filePos] in[#10,#13] then
247 raise Exception.Create('Unexpected end of line')
248 else
249 begin
250 AppendChar(fileContent[filePos]);
251 inc(filePos);
252 end;
253 end;
254 if (filePos <= length(fileContent)) and (fileContent[filePos] = '#') then
255 expectPlus := false
256 else
257 expectPlus := true;
258 result := true;
259 end;
260 '#': begin
261 inc(filePos);
262 charCode := ParseNumber;
263 if (charCode < 0) or (charCode > 255) then
264 raise exception.Create('Character code out of bounds');
265 AppendChar(chr(charCode));
266 if (filePos <= length(fileContent)) and (fileContent[filePos] in['#','''']) then
267 expectPlus := false
268 else
269 expectPlus := true;
270 result := true;
271 end;
272 else
273 begin
274 result := false;
275 expectPlus := false;
276 end;
277 end;
278 end
279 else
280 begin
281 result := false;
282 expectPlus := false;
283 end;
284 end;
285
286 begin
287 result := TMemoryStream.Create;
288 expectPlus := false;
289 if not ParseStringPart then raise exception.Create('Expecting string');
290 repeat
291 if ignoreCommas then
292 begin
293 SkipWhitespace;
294 if (filePos <= length(fileContent)) and (fileContent[filePos] = ',') then
295 begin
296 inc(filePos);
297 expectPlus := false;
298 end;
299 end;
300 until not ParseStringPart;
301 end;
302
303 procedure ReadContent;
304 var
305 bytesRead: integer;
306 begin
307 setlength(fileContent,AStream.Size-AStream.Position);
308 bytesRead := AStream.Read(fileContent[1],length(fileContent));
309 setlength(fileContent, bytesRead);
310 filePos := 1;
311 end;
312
313 function StreamToUTF8String(AStream: TStream): utf8String;
314 begin
315 setlength(result, AStream.Size);
316 AStream.Position := 0;
317 AStream.Read(result[1], length(result));
318 AStream.Free;
319 end;
320
321var
322 entryName: utf8string;
323 entryType: utf8string;
324 entryContent: TStream;
325 inArray: boolean;
326
327begin
328 Clear;
329 ReadContent;
330 while filePos <= length(fileContent) do
331 begin
332 if (upcase(fileContent[filePos]) = upcase(entryStart[1])) and
333 (CompareText(copy(fileContent,filePos,length(entryStart)),entryStart)=0) then
334 begin
335 inc(filePos, length(entryStart));
336 entryName := StreamToUTF8String(ParseString(false));
337 SkipComma;
338 entryType := StreamToUTF8String(ParseString(false));
339 SkipComma;
340
341 SkipWhitespace;
342 if (filePos <= length(fileContent)) and (fileContent[filePos] = '[') then
343 begin
344 inArray := true;
345 inc(filePos);
346 end else
347 inArray := false;
348 entryContent := ParseString(inArray);
349 SkipWhitespace;
350 if inArray then
351 begin
352 if (filePos <= length(fileContent)) and (fileContent[filePos] = ']') then
353 inc(filePos)
354 else
355 raise exception.Create('Expecting "]"');
356 end;
357
358 if entryType = 'FORMDATA' then
359 AddEntry(TFormDataEntry.Create(self,entryName,entryContent))
360 else
361 AddEntry(TLazResourceEntry.Create(self,entryName,entryType,entryContent));
362
363 if (filePos+length(entryEnd)-1 <= length(fileContent)) and (CompareText(copy(fileContent,filePos,length(entryEnd)),entryEnd)=0) then
364 inc(filePos,length(entryEnd))
365 else
366 raise exception.Create('Expecting "'+entryEnd+'"');
367 end else
368 if fileContent[filePos] in whiteSpace then
369 inc(filePos)
370 else
371 raise exception.Create('Unexpected character "'+fileContent[filePos]+'"');
372 end;
373end;
374
375function TLazResourceContainer.CreateEntry(AName: utf8string; AExtension: utf8string;
376 AContent: TStream): TMultiFileEntry;
377var
378 binContent: TMemoryStream;
379begin
380 if UTF8CompareText(AExtension,'lfm')=0 then
381 begin
382 binContent := TMemoryStream.Create;
383 try
384 AContent.Position:= 0;
385 LRSObjectTextToBinary(AContent, binContent);
386 result := TFormDataEntry.Create(self,AName,binContent);
387 except
388 on ex:Exception do
389 begin
390 binContent.Free;
391 result := nil;
392 end;
393 end;
394 AContent.Free;
395 end
396 else
397 result := TLazResourceEntry.Create(self,AName,UTF8UpperCase(AExtension),AContent);
398end;
399
400procedure TLazResourceContainer.SaveToStream(ADestination: TStream);
401var
402 i: Integer;
403begin
404 for i := 0 to Count-1 do
405 TLazResourceEntry(Entry[i]).Serialize(ADestination);
406end;
407
408end.
409
Note: See TracBrowser for help on using the repository browser.