source: trunk/Packages/bgrabitmap/bgrawinresource.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 34.1 KB
Line 
1unit BGRAWinResource;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, BGRAMultiFileType, BGRABitmapTypes, BGRAReadBMP;
9
10const
11 RT_CURSOR = 1;
12 RT_BITMAP = 2;
13 RT_ICON = 3;
14
15 RT_MENU = 4;
16 RT_DIALOG = 5;
17 RT_STRING = 6;
18 RT_FONTDIR = 7;
19 RT_FONT = 8;
20 RT_ACCELERATOR = 9;
21 RT_RCDATA = 10;
22 RT_MESSAGETABLE = 11;
23
24 RT_GROUP = 11;
25 RT_GROUP_CURSOR = RT_GROUP + RT_CURSOR;
26 RT_GROUP_ICON = RT_GROUP + RT_ICON;
27
28 RT_VERSION = 16;
29 RT_ANICURSOR = 21;
30 RT_ANIICON = 22;
31 RT_HTML = 23;
32 RT_MANIFEST = 24;
33
34 ICON_OR_CURSOR_FILE_ICON_TYPE = 1;
35 ICON_OR_CURSOR_FILE_CURSOR_TYPE = 2;
36
37type
38 TNameOrId = record
39 Id: integer;
40 Name: utf8string;
41 end;
42
43 { TResourceInfo }
44
45 TResourceInfo = object
46 DataVersion: DWord;
47 MemoryFlags: Word;
48 LanguageId: Word;
49 Version: DWord;
50 Characteristics: DWord;
51 procedure SwapIfNecessary;
52 end;
53
54 TWinResourceContainer = class;
55
56 { TCustomResourceEntry }
57
58 TCustomResourceEntry = class(TMultiFileEntry)
59 private
60 class function GetNextEntry(AContainer: TMultiFileContainer; AStream: TStream): TCustomResourceEntry;
61 procedure Serialize(ADestination: TStream);
62 protected
63 FTypeNameOrId: TNameOrId;
64 FEntryNameOrId: TNameOrId;
65 FResourceInfo: TResourceInfo;
66 FReferenceCount: integer;
67 function GetName: utf8string; override;
68 procedure SetName(AValue: utf8string); override;
69 function GetId: integer;
70 procedure SetId(AValue: integer);
71 function GetTypeId: integer;
72 function GetTypeName: utf8string;
73 procedure IncrementReferences; virtual;
74 procedure DecrementReferences; virtual;
75 procedure SerializeHeader(ADestination: TStream); virtual;
76 procedure SerializeData(ADestination: TStream); virtual; abstract;
77 function GetDataSize: integer; virtual; abstract;
78 function GetLanguageId: integer;
79 procedure SetLanguageId(AValue: integer);
80 public
81 constructor Create(AContainer: TMultiFileContainer; ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
82 property Id: integer read GetId write SetId;
83 property TypeName: utf8string read GetTypeName;
84 property TypeId: integer read GetTypeId;
85 property LanguageId: integer read GetLanguageId write SetLanguageId;
86 end;
87
88 { TUnformattedResourceEntry }
89
90 TUnformattedResourceEntry = class(TCustomResourceEntry)
91 protected
92 FDataStream: TStream;
93 function GetFileSize: int64; override;
94 function GetDataSize: integer; override;
95 procedure SerializeData(ADestination: TStream); override;
96 function GetExtension: utf8string; override;
97 public
98 constructor Create(AContainer: TMultiFileContainer; ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo; ADataStream: TStream);
99 destructor Destroy; override;
100 function CopyTo(ADestination: TStream): int64; override;
101 end;
102
103 { TBitmapResourceEntry }
104
105 TBitmapResourceEntry = class(TUnformattedResourceEntry)
106 protected
107 function GetFileSize: int64; override;
108 function GetExtension: utf8string; override;
109 public
110 constructor Create(AContainer: TMultiFileContainer; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo; ADataStream: TStream);
111 function CopyTo(ADestination: TStream): int64; override;
112 procedure CopyFrom(ASource: TStream);
113 end;
114
115 { TGroupIconHeader }
116
117 TGroupIconHeader = object
118 Reserved, ResourceType, ImageCount: Word;
119 procedure SwapIfNecessary;
120 end;
121 TGroupIconDirEntry = packed record
122 Width, Height, Colors, Reserved: byte;
123 //stored in little endian
124 case byte of
125 0: (Variable: DWord; ImageSize: DWord; ImageId: Word);
126 1: (Planes, BitsPerPixel: Word);
127 2: (HotSpotX, HotSpotY: Word);
128 end;
129 TIconFileDirEntry = packed record
130 Width, Height, Colors, Reserved: byte;
131 //stored in little endian
132 case byte of
133 0: (Variable: DWord; ImageSize: DWord; ImageOffset: DWord);
134 1: (Planes, BitsPerPixel: Word);
135 2: (HotSpotX, HotSpotY: Word);
136 end;
137
138 { TGroupIconOrCursorEntry }
139
140 TGroupIconOrCursorEntry = class(TCustomResourceEntry)
141 private
142 function GetNbIcons: integer;
143 protected
144 FGroupIconHeader: TGroupIconHeader;
145 FDirectory: packed array of TGroupIconDirEntry;
146 function GetFileSize: int64; override;
147 function GetDataSize: integer; override;
148 procedure SerializeData(ADestination: TStream); override;
149 procedure IncrementReferences; override;
150 procedure DecrementReferences; override;
151 function ExpectedResourceType: word; virtual; abstract;
152 public
153 constructor Create(AContainer: TMultiFileContainer; ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo; ADataStream: TStream);
154 constructor Create(AContainer: TMultiFileContainer; ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
155 procedure Clear;
156 function CopyTo(ADestination: TStream): int64; override;
157 procedure CopyFrom(ASource: TStream);
158 property NbIcons: integer read GetNbIcons;
159 end;
160
161 { TGroupIconEntry }
162
163 TGroupIconEntry = class(TGroupIconOrCursorEntry)
164 protected
165 function GetExtension: utf8string; override;
166 function ExpectedResourceType: word; override;
167 public
168 constructor Create(AContainer: TMultiFileContainer; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo; ADataStream: TStream);
169 constructor Create(AContainer: TMultiFileContainer; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
170 end;
171
172 { TGroupCursorEntry }
173
174 TGroupCursorEntry = class(TGroupIconOrCursorEntry)
175 protected
176 function GetExtension: utf8string; override;
177 function ExpectedResourceType: word; override;
178 public
179 constructor Create(AContainer: TMultiFileContainer; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo; ADataStream: TStream);
180 constructor Create(AContainer: TMultiFileContainer; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
181 end;
182
183 { TWinResourceContainer }
184
185 TWinResourceContainer = class(TMultiFileContainer)
186 private
187 function InternalFind(const AEntry: TNameOrId; const AType: TNameOrId; ALanguageId: integer = 0): TCustomResourceEntry;
188 procedure AddHidden(AEntry: TCustomResourceEntry);
189 function GetMaxId(AType: TNameOrId): integer;
190 procedure IncrementReferenceOf(ANameId, ATypeId: integer);
191 procedure DecrementReferenceOf(ANameId, ATypeId: integer);
192 protected
193 FHiddenEntries: TMultiFileEntryList;
194 procedure Init; override;
195 procedure ClearHiddenEntries;
196 procedure RemoveHidden(AEntry: TCustomResourceEntry);
197 function CreateEntry(AName: utf8string; AExtension: utf8string; AContent: TStream; ALanguageId: integer): TMultiFileEntry; overload;
198 function CreateEntry(AName: utf8string; AExtension: utf8string; AContent: TStream): TMultiFileEntry; override;
199 public
200 procedure Clear; override;
201 destructor Destroy; override;
202 procedure Delete(AIndex: integer); override;
203 procedure LoadFromStream(AStream: TStream); override;
204 function IndexOf(AName: utf8string; AExtenstion: utf8string; ACaseSensitive: boolean = True): integer; override;
205 function IndexOf(AName: utf8string; AExtenstion: utf8string; ALanguageId: integer; ACaseSensitive: boolean = True): integer; overload;
206 procedure SaveToStream(ADestination: TStream); override;
207 end;
208
209implementation
210
211uses Math, BGRAUTF8;
212
213operator =(const ANameOrId1, ANameOrId2: TNameOrId): boolean;
214begin
215 if (ANameOrId1.Id < 0) then
216 result := (ANameOrId2.Id < 0) and (ANameOrId2.Name = ANameOrId1.Name)
217 else
218 result := ANameOrId2.Id = ANameOrId1.Id;
219end;
220
221function NameOrId(AName: string): TNameOrId; overload;
222begin
223 result.Id := -1;
224 result.Name := AName;
225end;
226
227function NameOrId(AId: integer): TNameOrId; overload;
228begin
229 result.Id := AId;
230 result.Name := IntToStr(AId);
231end;
232
233{ TGroupCursorEntry }
234
235function TGroupCursorEntry.GetExtension: utf8string;
236begin
237 Result:= 'cur';
238end;
239
240function TGroupCursorEntry.ExpectedResourceType: word;
241begin
242 result := ICON_OR_CURSOR_FILE_CURSOR_TYPE;
243end;
244
245constructor TGroupCursorEntry.Create(AContainer: TMultiFileContainer;
246 AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
247 ADataStream: TStream);
248begin
249 inherited Create(AContainer,NameOrId(RT_GROUP_CURSOR),AEntryNameOrId,AResourceInfo,ADataStream);
250end;
251
252constructor TGroupCursorEntry.Create(AContainer: TMultiFileContainer;
253 AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
254begin
255 inherited Create(AContainer,NameOrId(RT_GROUP_CURSOR),AEntryNameOrId,AResourceInfo);
256end;
257
258{ TGroupIconEntry }
259
260function TGroupIconEntry.GetExtension: utf8string;
261begin
262 Result:= 'ico';
263end;
264
265function TGroupIconEntry.ExpectedResourceType: word;
266begin
267 result := ICON_OR_CURSOR_FILE_ICON_TYPE;
268end;
269
270constructor TGroupIconEntry.Create(AContainer: TMultiFileContainer;
271 AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
272 ADataStream: TStream);
273begin
274 inherited Create(AContainer,NameOrId(RT_GROUP_ICON),AEntryNameOrId,AResourceInfo,ADataStream);
275end;
276
277constructor TGroupIconEntry.Create(AContainer: TMultiFileContainer;
278 AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
279begin
280 inherited Create(AContainer,NameOrId(RT_GROUP_ICON),AEntryNameOrId,AResourceInfo);
281end;
282
283{ TGroupIconHeader }
284
285procedure TGroupIconHeader.SwapIfNecessary;
286begin
287 Reserved := LEtoN(Reserved);
288 ResourceType := LEtoN(ResourceType);
289 ImageCount := LEtoN(ImageCount);
290end;
291
292{ TGroupIconOrCursorEntry }
293
294function TGroupIconOrCursorEntry.GetNbIcons: integer;
295begin
296 result := FGroupIconHeader.ImageCount;
297end;
298
299function TGroupIconOrCursorEntry.GetFileSize: int64;
300var
301 i: Integer;
302begin
303 Result:= sizeof(FGroupIconHeader) + sizeof(TIconFileDirEntry)*NbIcons;
304 for i := 0 to NbIcons-1 do
305 Result += LEtoN(FDirectory[i].ImageSize);
306end;
307
308function TGroupIconOrCursorEntry.GetDataSize: integer;
309begin
310 result := sizeof(FGroupIconHeader) + sizeof(TGroupIconDirEntry)*NbIcons;
311end;
312
313procedure TGroupIconOrCursorEntry.SerializeData(ADestination: TStream);
314begin
315 FGroupIconHeader.SwapIfNecessary;
316 try
317 ADestination.WriteBuffer(FGroupIconHeader, sizeof(FGroupIconHeader));
318 finally
319 FGroupIconHeader.SwapIfNecessary;
320 end;
321 ADestination.WriteBuffer(FDirectory[0], sizeof(TGroupIconDirEntry)*NbIcons);
322end;
323
324procedure TGroupIconOrCursorEntry.IncrementReferences;
325var
326 i: Integer;
327begin
328 for i := 0 to NbIcons-1 do
329 TWinResourceContainer(Container).IncrementReferenceOf(LEtoN(FDirectory[i].ImageId), TypeId - RT_GROUP);
330end;
331
332procedure TGroupIconOrCursorEntry.DecrementReferences;
333var
334 i: Integer;
335begin
336 for i := 0 to NbIcons-1 do
337 TWinResourceContainer(Container).DecrementReferenceOf(LEtoN(FDirectory[i].ImageId), TypeId - RT_GROUP);
338end;
339
340constructor TGroupIconOrCursorEntry.Create(AContainer: TMultiFileContainer;
341 ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
342 ADataStream: TStream);
343begin
344 inherited Create(AContainer,ATypeNameOrId,AEntryNameOrId,AResourceInfo);
345
346 ADataStream.ReadBuffer(FGroupIconHeader, sizeof(FGroupIconHeader));
347 FGroupIconHeader.SwapIfNecessary;
348 if FGroupIconHeader.ResourceType <> ExpectedResourceType then
349 raise exception.Create('Unexpected group type');
350
351 if ADataStream.Position + NbIcons*sizeof(TGroupIconDirEntry) > ADataStream.Size then
352 raise exception.Create('Directory dimension mismatch');
353 setlength(FDirectory, NbIcons);
354 ADataStream.ReadBuffer(FDirectory[0], NbIcons*sizeof(TGroupIconDirEntry));
355 ADataStream.Free;
356end;
357
358constructor TGroupIconOrCursorEntry.Create(AContainer: TMultiFileContainer;
359 ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId;
360 const AResourceInfo: TResourceInfo);
361begin
362 inherited Create(AContainer,ATypeNameOrId,AEntryNameOrId,AResourceInfo);
363
364 FGroupIconHeader.Reserved := 0;
365 FGroupIconHeader.ResourceType := ExpectedResourceType;
366 FGroupIconHeader.ImageCount := 0;
367end;
368
369procedure TGroupIconOrCursorEntry.Clear;
370begin
371 DecrementReferences;
372 FDirectory := nil;
373 FGroupIconHeader.ImageCount := 0;
374end;
375
376function TGroupIconOrCursorEntry.CopyTo(ADestination: TStream): int64;
377var
378 fileDir: packed array of TIconFileDirEntry;
379 offset, written, i: integer;
380 iconEntry: TCustomResourceEntry;
381 iconEntrySize: DWord;
382 iconData: TMemoryStream;
383 copyCount: Int64;
384 subType: TNameOrId;
385
386 procedure FillZero(ACount: integer);
387 var
388 Zero: packed array[0..255] of byte;
389 begin
390 if ACount <= 0 then exit;
391 FillChar({%H-}Zero, Sizeof(Zero), 0);
392 while ACount > 0 do
393 begin
394 ADestination.WriteBuffer(Zero, Min(ACount, sizeof(Zero)));
395 Dec(ACount, Min(ACount, sizeof(Zero)));
396 end;
397 end;
398
399begin
400 result:= 0;
401 FGroupIconHeader.SwapIfNecessary;
402 try
403 ADestination.WriteBuffer(FGroupIconHeader, sizeof(FGroupIconHeader));
404 finally
405 FGroupIconHeader.SwapIfNecessary;
406 end;
407 Inc(result, sizeof(FGroupIconHeader));
408
409 offset := result+sizeof(TIconFileDirEntry)*NbIcons;
410 setlength(fileDir, NbIcons);
411 for i := 0 to NbIcons-1 do
412 begin
413 move(FDirectory[i], fileDir[i], 12);
414 fileDir[i].ImageOffset := NtoLE(offset);
415 inc(offset, fileDir[i].ImageSize);
416 end;
417
418 ADestination.WriteBuffer(fileDir[0], sizeof(TIconFileDirEntry)*NbIcons);
419 inc(result, sizeof(TIconFileDirEntry)*NbIcons);
420
421 subType := NameOrId(TypeId - RT_GROUP);
422 for i := 0 to NbIcons-1 do
423 begin
424 iconEntry := (Container as TWinResourceContainer).InternalFind(NameOrId(LEtoN(FDirectory[i].ImageId)),subType); //no language for icons
425 iconEntrySize := LEtoN(FDirectory[i].ImageSize);
426 if iconEntry = nil then
427 FillZero(iconEntrySize) else
428 begin
429 iconData := TMemoryStream.Create;
430 try
431 iconEntry.CopyTo(IconData);
432 iconData.Position:= 0;
433 copyCount := Min(IconData.Size, iconEntrySize);
434 if copyCount > 0 then written := ADestination.CopyFrom(IconData, copyCount)
435 else written := 0;
436 FillZero(iconEntrySize-written);
437 finally
438 IconData.Free;
439 end;
440 end;
441 result += iconEntrySize;
442 end;
443end;
444
445procedure TGroupIconOrCursorEntry.CopyFrom(ASource: TStream);
446var
447 tempGroup: TGroupIconHeader;
448 fileDir: packed array of TIconFileDirEntry;
449 iconStream: array of TMemoryStream;
450 startPos: int64;
451 maxId, i: integer;
452 iconEntry: TUnformattedResourceEntry;
453 resourceInfo: TResourceInfo;
454 subType: TNameOrId;
455begin
456 startPos := ASource.Position;
457 ASource.ReadBuffer({%H-}tempGroup, sizeof(tempGroup));
458 tempGroup.SwapIfNecessary;
459 if tempGroup.ResourceType <> ExpectedResourceType then
460 raise exception.Create('Unexpected resource type');
461
462 if ASource.Position + sizeof(TIconFileDirEntry)*tempGroup.ImageCount > ASource.Size then
463 raise exception.Create('Directory dimension mismatch');
464
465 setlength(fileDir, tempGroup.ImageCount);
466 ASource.ReadBuffer(fileDir[0], sizeof(TIconFileDirEntry)*tempGroup.ImageCount);
467
468 try
469 setlength(iconStream, tempGroup.ImageCount);
470 for i := 0 to tempGroup.ImageCount-1 do
471 begin
472 ASource.Position:= startPos + LEtoN(fileDir[i].ImageOffset);
473 iconStream[i] := TMemoryStream.Create;
474 iconStream[i].CopyFrom(ASource, LEtoN(fileDir[i].ImageSize));
475 end;
476
477 subType := NameOrId(self.TypeId - RT_GROUP);
478 maxId := TWinResourceContainer(Container).GetMaxId(subType);
479
480 Clear;
481 FGroupIconHeader.ImageCount := tempGroup.ImageCount;
482 setlength(FDirectory, tempGroup.ImageCount);
483 fillchar({%H-}resourceInfo,sizeof(resourceInfo),0);
484 for i := 0 to tempGroup.ImageCount-1 do
485 begin
486 move(fileDir[i], FDirectory[i], 12);
487 inc(maxId);
488 FDirectory[i].ImageId := maxId;
489 iconEntry := TUnformattedResourceEntry.Create(Container, subType, NameOrId(maxId), resourceInfo, iconStream[i]);
490 iconStream[i] := nil;
491 TWinResourceContainer(Container).AddHidden(iconEntry);
492 end;
493
494 finally
495 for i := 0 to high(iconStream) do
496 iconStream[i].Free;
497 iconStream := nil;
498 end;
499end;
500
501{ TBitmapResourceEntry }
502
503function TBitmapResourceEntry.GetFileSize: int64;
504begin
505 result := sizeof(TBitMapFileHeader)+FDataStream.Size;
506end;
507
508function TBitmapResourceEntry.GetExtension: utf8string;
509begin
510 Result:= 'bmp';
511end;
512
513constructor TBitmapResourceEntry.Create(AContainer: TMultiFileContainer;
514 AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
515 ADataStream: TStream);
516begin
517 inherited Create(AContainer, NameOrId(RT_BITMAP), AEntryNameOrId, AResourceInfo, ADataStream);
518end;
519
520function TBitmapResourceEntry.CopyTo(ADestination: TStream): int64;
521var fileHeader: TBitMapFileHeader;
522begin
523 result := 0;
524 FDataStream.Position := 0;
525 fileHeader := MakeBitmapFileHeader(FDataStream);
526 ADestination.WriteBuffer(fileHeader, sizeof(fileHeader));
527 result += sizeof(fileHeader);
528 FDataStream.Position := 0;
529 result += ADestination.CopyFrom(FDataStream, FDataStream.Size);
530end;
531
532procedure TBitmapResourceEntry.CopyFrom(ASource: TStream);
533var
534 fileHeader: TBitMapFileHeader;
535 dataSize: integer;
536begin
537 ASource.ReadBuffer({%H-}fileHeader, sizeof(fileHeader));
538 if fileHeader.bfType <> Word('BM') then
539 raise exception.Create('Invalid file header');
540 dataSize := LEtoN(fileHeader.bfSize) - sizeof(fileHeader);
541 if ASource.Position + dataSize > ASource.Size then
542 raise exception.Create('Invalid file size');
543
544 FDataStream.Free;
545 FDataStream := TMemoryStream.Create;
546 FDataStream.CopyFrom(ASource, dataSize);
547end;
548
549{ TUnformattedResourceEntry }
550
551function TUnformattedResourceEntry.GetFileSize: int64;
552begin
553 Result:= FDataStream.Size;
554end;
555
556function TUnformattedResourceEntry.GetDataSize: integer;
557begin
558 result := FDataStream.Size;
559end;
560
561procedure TUnformattedResourceEntry.SerializeData(ADestination: TStream);
562begin
563 if FDataStream.Size > 0 then
564 begin
565 FDataStream.Position := 0;
566 ADestination.CopyFrom(FDataStream, FDataStream.Size);
567 end;
568end;
569
570function TUnformattedResourceEntry.GetExtension: utf8string;
571var format: TBGRAImageFormat;
572begin
573 case TypeId of
574 RT_MANIFEST: result := 'manifest';
575 RT_HTML: result := 'html';
576 RT_RCDATA:
577 begin
578 FDataStream.Position:= 0;
579 format := DetectFileFormat(FDataStream);
580 if format = ifUnknown then
581 result := 'dat'
582 else
583 result := SuggestImageExtension(format);
584 end;
585 RT_ANICURSOR: result := 'ani';
586 else
587 if TypeName = 'ANICURSOR' then
588 result := 'ani'
589 else
590 result := '';
591 end;
592end;
593
594constructor TUnformattedResourceEntry.Create(AContainer: TMultiFileContainer;
595 ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId;
596 const AResourceInfo: TResourceInfo; ADataStream: TStream);
597begin
598 inherited Create(AContainer,ATypeNameOrId,AEntryNameOrId,AResourceInfo);
599 FDataStream := ADataStream;
600end;
601
602destructor TUnformattedResourceEntry.Destroy;
603begin
604 FreeAndNil(FDataStream);
605 inherited Destroy;
606end;
607
608function TUnformattedResourceEntry.CopyTo(ADestination: TStream): int64;
609begin
610 if FDataStream.Size > 0 then
611 begin
612 FDataStream.Position := 0;
613 result := ADestination.CopyFrom(FDataStream, FDataStream.Size)
614 end
615 else
616 result := 0;
617end;
618
619{ TResourceInfo }
620
621procedure TResourceInfo.SwapIfNecessary;
622begin
623 DataVersion := LEtoN(DataVersion);
624 MemoryFlags := LEtoN(MemoryFlags);
625 LanguageId := LEtoN(LanguageId);
626 Version := LEtoN(Version);
627 Characteristics := LEtoN(Characteristics);
628end;
629
630{ TCustomResourceEntry }
631
632function TCustomResourceEntry.GetId: integer;
633begin
634 result := FEntryNameOrId.Id;
635end;
636
637function TCustomResourceEntry.GetTypeId: integer;
638begin
639 result := FTypeNameOrId.Id;
640end;
641
642function GetDWord(var ASource: PByte; var ARemainingBytes: Integer): DWord;
643begin
644 if ARemainingBytes >= 4 then
645 begin
646 result := LEtoN(PDWord(ASource)^);
647 inc(ASource, 4);
648 dec(ARemainingBytes, 4);
649 end else
650 begin
651 result := 0;
652 inc(ASource, ARemainingBytes);
653 ARemainingBytes:= 0;
654 end;
655end;
656
657function GetWord(var ASource: PByte; var ARemainingBytes: Integer): Word;
658begin
659 if ARemainingBytes >= 2 then
660 begin
661 result := LEtoN(PWord(ASource)^);
662 inc(ASource, 2);
663 dec(ARemainingBytes, 2);
664 end else
665 begin
666 result := 0;
667 inc(ASource, ARemainingBytes);
668 ARemainingBytes:= 0;
669 end;
670end;
671
672function GetNameOrId(var ASource: PByte; var ARemainingBytes: Integer): TNameOrId;
673var curChar: Word;
674 pstart: PByte;
675begin
676 pstart := ASource;
677 curChar := GetWord(ASource,ARemainingBytes);
678 if curChar = $ffff then
679 begin
680 result.Id := GetWord(ASource,ARemainingBytes);
681 result.Name := IntToStr(result.Id);
682 end else
683 begin
684 while curChar <> 0 do
685 curChar := GetWord(ASource,ARemainingBytes);
686 result.Id := -1;
687 result.Name := UTF8Encode(WideCharLenToString(PWideChar(pstart), (ASource-pstart) div 2 -1));
688 end;
689end;
690
691function TCustomResourceEntry.GetLanguageId: integer;
692begin
693 result := FResourceInfo.LanguageId;
694end;
695
696class function TCustomResourceEntry.GetNextEntry(AContainer: TMultiFileContainer; AStream: TStream): TCustomResourceEntry;
697var
698 entrySize, headerSize, remaining, padding: Integer;
699 headerData: Pointer;
700 pHeaderData: PByte;
701 typeNameOrId: TNameOrId;
702 entryNameOrId: TNameOrId;
703 info: TResourceInfo;
704 dataStream: TMemoryStream;
705 dummy: DWord;
706begin
707 result := nil;
708 if AStream.Position + 16 < AStream.Size then
709 begin
710 entrySize := LEtoN(AStream.ReadDWord);
711 headerSize := LEtoN(AStream.ReadDWord);
712 if headerSize < 16 then
713 raise exception.Create('Header too small');
714 remaining := ((headerSize-8) + 3) and not 3;
715 if AStream.Position + remaining + entrySize > AStream.Size then
716 raise exception.Create('Data would be outside of stream');
717
718 GetMem(headerData, remaining);
719 try
720 AStream.ReadBuffer(headerData^, remaining);
721 pHeaderData := PByte(headerData);
722 typeNameOrId := GetNameOrId(pHeaderData, remaining);
723 entryNameOrId := GetNameOrId(pHeaderData, remaining);
724 padding := (4 - ((pHeaderData-PByte(headerData)) and 3)) and 3;
725 inc(pHeaderData, padding);
726 dec(remaining, padding);
727
728 FillChar({%H-}info, SizeOf(info), 0);
729 Move(pHeaderData^, info, Min(Sizeof(info), remaining));
730 info.SwapIfNecessary;
731
732 dataStream := TMemoryStream.Create;
733 if entrySize > 0 then dataStream.CopyFrom(AStream, entrySize);
734 padding := ((entrySize+3) and not 3) - entrySize;
735 if padding > 0 then AStream.Read({%H-}dummy, padding);
736 finally
737 FreeMem(headerData);
738 end;
739
740 dataStream.Position := 0;
741 case typeNameOrId.Id of
742 RT_BITMAP: result := TBitmapResourceEntry.Create(AContainer,entryNameOrId,info,dataStream);
743 RT_GROUP_ICON: result := TGroupIconEntry.Create(AContainer,entryNameOrId,info,dataStream);
744 RT_GROUP_CURSOR: result := TGroupCursorEntry.Create(AContainer,entryNameOrId,info,dataStream);
745 else
746 result := TUnformattedResourceEntry.Create(AContainer,typeNameOrId,entryNameOrId,info,dataStream);
747 end;
748 end;
749end;
750
751procedure WriteNameOrId(ADestination: TStream; ANameOrId: TNameOrId);
752var buffer: PUnicodeChar;
753 maxLen,actualLen: integer;
754begin
755 if ANameOrId.Id < 0 then
756 begin
757 maxLen := length(ANameOrId.Name)*2 + 1;
758 getmem(buffer, maxLen*sizeof(UnicodeChar));
759 try
760 fillchar(buffer^, maxLen*sizeof(UnicodeChar), 0);
761 actualLen := Utf8ToUnicode(buffer, maxLen, @ANameOrId.Name[1], length(ANameOrId.Name));
762 ADestination.WriteBuffer(buffer^, actualLen*sizeof(UnicodeChar));
763 finally
764 freemem(buffer);
765 end;
766 end else
767 begin
768 ADestination.WriteWord($ffff);
769 ADestination.WriteWord(NtoLE(Word(ANameOrId.Id)));
770 end;
771end;
772
773procedure TCustomResourceEntry.Serialize(ADestination: TStream);
774var zero: DWord;
775 padding: integer;
776begin
777 SerializeHeader(ADestination);
778 SerializeData(ADestination);
779 padding := (4-(GetDataSize and 3)) and 3;
780 if padding > 0 then
781 begin
782 zero := 0;
783 ADestination.WriteBuffer(zero, padding);
784 end;
785end;
786
787procedure TCustomResourceEntry.SetLanguageId(AValue: integer);
788begin
789 if (AValue >= 0) and (AValue <= 65535) then
790 begin
791 if AValue = LanguageId then exit;
792 if FTypeNameOrId.Id >= 0 then
793 begin
794 if TWinResourceContainer(Container).InternalFind(FEntryNameOrId, FTypeNameOrId, AValue) <> nil then
795 raise exception.Create('Language id already used for this resource');
796 end else
797 raise exception.Create('Language id cannot be specified for custom types');
798 FEntryNameOrId.Id := AValue;
799 FEntryNameOrId.Name := IntToStr(AValue);
800 end
801 else
802 raise ERangeError.Create('Id out of bounds');
803end;
804
805procedure TCustomResourceEntry.SerializeHeader(ADestination: TStream);
806var
807 entryHeader: record
808 EntrySize: integer;
809 HeaderSize: integer;
810 end;
811 headerStream: TMemoryStream;
812begin
813 entryHeader.EntrySize := LEtoN(GetDataSize);
814 headerStream := TMemoryStream.Create;
815 try
816 WriteNameOrId(headerStream,FTypeNameOrId);
817 WriteNameOrId(headerStream,FEntryNameOrId);
818 if headerStream.Position and 3 = 2 then headerStream.WriteWord(0);
819 FResourceInfo.SwapIfNecessary;
820 try
821 headerStream.WriteBuffer(FResourceInfo, sizeof(FResourceInfo));
822 finally
823 FResourceInfo.SwapIfNecessary;
824 end;
825 entryHeader.HeaderSize := LEtoN(integer(headerStream.Size+8));
826 headerStream.Position:= 0;
827 ADestination.WriteBuffer(entryHeader, sizeof(entryHeader));
828 ADestination.CopyFrom(headerStream, headerStream.Size);
829 if headerStream.Size and 3 = 2 then ADestination.WriteWord(0);
830 finally
831 headerStream.Free;
832 end;
833end;
834
835constructor TCustomResourceEntry.Create(AContainer: TMultiFileContainer;
836 ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId;
837 const AResourceInfo: TResourceInfo);
838begin
839 inherited Create(AContainer);
840 FTypeNameOrId := ATypeNameOrId;
841 FEntryNameOrId := AEntryNameOrId;
842 FResourceInfo := AResourceInfo;
843end;
844
845procedure TCustomResourceEntry.SetId(AValue: integer);
846begin
847 if (AValue >= 0) and (AValue <= 65535) then
848 begin
849 if AValue = FEntryNameOrId.Id then exit;
850 if TWinResourceContainer(Container).InternalFind(NameOrId(AValue), FTypeNameOrId, LanguageId) <> nil then
851 raise exception.Create('Id already used for this resource type');
852 FEntryNameOrId.Id := AValue;
853 FEntryNameOrId.Name := IntToStr(AValue);
854 end
855 else
856 raise ERangeError.Create('Id out of bounds');
857end;
858
859function TCustomResourceEntry.GetName: utf8string;
860begin
861 Result:= FEntryNameOrId.Name;
862end;
863
864procedure TCustomResourceEntry.SetName(AValue: utf8string);
865begin
866 if FEntryNameOrId = NameOrId(AValue) then exit;
867 if TWinResourceContainer(Container).InternalFind(NameOrId(AValue), FTypeNameOrId, LanguageId) <> nil then
868 raise exception.Create('Name already used for this resource type');
869 FEntryNameOrId.Name := AValue;
870 FEntryNameOrId.Id := -1;
871end;
872
873function TCustomResourceEntry.GetTypeName: utf8string;
874begin
875 result := FTypeNameOrId.Name;
876end;
877
878procedure TCustomResourceEntry.IncrementReferences;
879begin
880 //nothing
881end;
882
883procedure TCustomResourceEntry.DecrementReferences;
884begin
885 //nothing
886end;
887
888{ TWinResourceContainer }
889
890procedure TWinResourceContainer.LoadFromStream(AStream: TStream);
891var curEntry: TCustomResourceEntry;
892 i: Integer;
893begin
894 Clear;
895 repeat
896 curEntry := TCustomResourceEntry.GetNextEntry(self, AStream);
897 if curEntry <> nil then
898 begin
899 if curEntry.TypeId in [RT_ICON,RT_CURSOR] then
900 FHiddenEntries.Add(curEntry)
901 else
902 AddEntry(curEntry);
903 end;
904 until curEntry = nil;
905 for i := 0 to Count-1 do
906 TCustomResourceEntry(Entry[i]).IncrementReferences;
907end;
908
909function TWinResourceContainer.IndexOf(AName: utf8string; AExtenstion: utf8string; ACaseSensitive: boolean): integer;
910begin
911 result := IndexOf(AName, AExtenstion, 0, ACaseSensitive);
912end;
913
914function TWinResourceContainer.IndexOf(AName: utf8string; AExtenstion: utf8string;
915 ALanguageId: integer; ACaseSensitive: boolean): integer;
916var
917 i: Integer;
918 entryId, errPos: integer;
919begin
920 if AExtenstion = '' then
921 begin
922 result := -1;
923 exit;
924 end;
925 if ACaseSensitive then
926 begin
927 for i := 0 to Count-1 do
928 if (TCustomResourceEntry(Entry[i]).FEntryNameOrId.Id < 0) and
929 (TCustomResourceEntry(Entry[i]).FEntryNameOrId.Name = AName) and
930 (UTF8CompareText(Entry[i].Extension,AExtenstion) = 0) and
931 (TCustomResourceEntry(Entry[i]).LanguageId = ALanguageId) then
932 begin
933 result := i;
934 exit;
935 end;
936 end else
937 for i := 0 to Count-1 do
938 if (TCustomResourceEntry(Entry[i]).FEntryNameOrId.Id < 0) and
939 (UTF8CompareText(TCustomResourceEntry(Entry[i]).FEntryNameOrId.Name,AName) = 0) and
940 (UTF8CompareText(Entry[i].Extension,AExtenstion) = 0) and
941 (TCustomResourceEntry(Entry[i]).LanguageId = ALanguageId) then
942 begin
943 result := i;
944 exit;
945 end;
946 val(AName, entryId, errPos);
947 if (errPos = 0) and (entryId >= 0) then
948 begin
949 for i := 0 to Count-1 do
950 if (TCustomResourceEntry(Entry[i]).FEntryNameOrId.Id = entryId) and
951 (UTF8CompareText(Entry[i].Extension,AExtenstion) = 0) and
952 (TCustomResourceEntry(Entry[i]).LanguageId = ALanguageId) then
953 begin
954 result := i;
955 exit;
956 end;
957 end;
958 result := -1;
959end;
960
961procedure TWinResourceContainer.Init;
962begin
963 inherited Init;
964 FHiddenEntries := TMultiFileEntryList.Create;
965end;
966
967procedure TWinResourceContainer.ClearHiddenEntries;
968var i: integer;
969begin
970 if Assigned(FHiddenEntries) then
971 begin
972 for i := 0 to FHiddenEntries.Count-1 do
973 FHiddenEntries[i].Free;
974 FHiddenEntries.Clear;
975 end;
976end;
977
978procedure TWinResourceContainer.RemoveHidden(AEntry: TCustomResourceEntry);
979var
980 index: LongInt;
981begin
982 if Assigned(FHiddenEntries) then
983 begin
984 index := FHiddenEntries.IndexOf(AEntry);
985 if index <> -1 then
986 begin
987 AEntry.Free;
988 FHiddenEntries.Delete(index);
989 end;
990 end;
991end;
992
993function TWinResourceContainer.CreateEntry(AName: utf8string; AExtension: utf8string;
994 AContent: TStream; ALanguageId: integer): TMultiFileEntry;
995var
996 resourceInfo: TResourceInfo;
997 entryName: TNameOrId;
998 errPos: integer;
999begin
1000 FillChar({%H-}resourceInfo, sizeof(resourceInfo), 0);
1001 resourceInfo.LanguageId := ALanguageId;
1002 val(AName, entryName.Id, errPos);
1003 if (errPos = 0) and (entryName.Id >= 0) then
1004 entryName.Name := IntToStr(entryName.Id)
1005 else
1006 begin
1007 entryName.Id := -1;
1008 entryName.Name := AName;
1009 end;
1010
1011 case UTF8LowerCase(AExtension) of
1012 'ico': begin
1013 result := TGroupIconEntry.Create(self, entryName, resourceInfo);
1014 AContent.Position:= 0;
1015 TGroupIconEntry(result).CopyFrom(AContent);
1016 AContent.Free;
1017 end;
1018 'cur': begin
1019 result := TGroupCursorEntry.Create(self, entryName, resourceInfo);
1020 AContent.Position:= 0;
1021 TGroupCursorEntry(result).CopyFrom(AContent);
1022 AContent.Free;
1023 end;
1024 'bmp': begin
1025 result := TBitmapResourceEntry.Create(self, entryName, resourceInfo, AContent);
1026 AContent.Position:= 0;
1027 TBitmapResourceEntry(result).CopyFrom(AContent);
1028 AContent.Free;
1029 end;
1030 'dat': result := TUnformattedResourceEntry.Create(self, NameOrId(RT_RCDATA), entryName, resourceInfo, AContent);
1031 'html','htm': result := TUnformattedResourceEntry.Create(self, NameOrId(RT_HTML), entryName, resourceInfo, AContent);
1032 'manifest': result := TUnformattedResourceEntry.Create(self, NameOrId(RT_MANIFEST), entryName, resourceInfo, AContent);
1033 'ani': result := TUnformattedResourceEntry.Create(self, NameOrId(RT_ANICURSOR), entryName, resourceInfo, AContent);
1034 else
1035 case SuggestImageFormat('.'+AExtension) of
1036 ifUnknown: raise exception.Create('Unhandled file extension');
1037 else
1038 result := TUnformattedResourceEntry.Create(self, NameOrId(RT_RCDATA), entryName, resourceInfo, AContent);
1039 end;
1040 end;
1041end;
1042
1043function TWinResourceContainer.CreateEntry(AName: utf8string; AExtension: utf8string;
1044 AContent: TStream): TMultiFileEntry;
1045begin
1046 result := CreateEntry(AName, AExtension, AContent, 0);
1047end;
1048
1049procedure TWinResourceContainer.Clear;
1050begin
1051 ClearHiddenEntries;
1052 inherited Clear;
1053end;
1054
1055destructor TWinResourceContainer.Destroy;
1056begin
1057 ClearHiddenEntries;
1058 FreeAndNil(FHiddenEntries);
1059 inherited Destroy;
1060end;
1061
1062procedure TWinResourceContainer.Delete(AIndex: integer);
1063begin
1064 if (AIndex >= 0) and (AIndex < Count) then
1065 TCustomResourceEntry(Entry[AIndex]).DecrementReferences;
1066 inherited Delete(AIndex);
1067end;
1068
1069procedure TWinResourceContainer.SaveToStream(ADestination: TStream);
1070var
1071 i: Integer;
1072begin
1073 for i := 0 to Count-1 do
1074 TCustomResourceEntry(Entry[i]).Serialize(ADestination);
1075 for i := 0 to FHiddenEntries.Count-1 do
1076 TCustomResourceEntry(FHiddenEntries.Items[i]).Serialize(ADestination);
1077end;
1078
1079function TWinResourceContainer.InternalFind(const AEntry: TNameOrId;
1080 const AType: TNameOrId; ALanguageId: integer): TCustomResourceEntry;
1081var i: integer;
1082begin
1083 if Assigned(FHiddenEntries) and (ALanguageId = 0) and (AType.Id >= 0) then
1084 begin
1085 for i := 0 to FHiddenEntries.Count-1 do
1086 if (TCustomResourceEntry(FHiddenEntries.Items[i]).FEntryNameOrId = AEntry) and
1087 (TCustomResourceEntry(FHiddenEntries.Items[i]).FTypeNameOrId = AType) then
1088 begin
1089 result := TCustomResourceEntry(FHiddenEntries.Items[i]);
1090 exit;
1091 end;
1092 end;
1093 for i := 0 to Count-1 do
1094 if (TCustomResourceEntry(Entry[i]).FEntryNameOrId = AEntry) and
1095 (TCustomResourceEntry(Entry[i]).FTypeNameOrId = AType) and
1096 (TCustomResourceEntry(Entry[i]).LanguageId = ALanguageId) then
1097 begin
1098 result := TCustomResourceEntry(Entry[i]);
1099 exit;
1100 end;
1101 result := nil;
1102end;
1103
1104procedure TWinResourceContainer.AddHidden(AEntry: TCustomResourceEntry);
1105begin
1106 FHiddenEntries.Add(AEntry);
1107end;
1108
1109function TWinResourceContainer.GetMaxId(AType: TNameOrId): integer;
1110var i: integer;
1111begin
1112 result := 0;
1113 if Assigned(FHiddenEntries) and (AType.Id >= 0) then
1114 begin
1115 for i := 0 to FHiddenEntries.Count-1 do
1116 if (TCustomResourceEntry(FHiddenEntries.Items[i]).FTypeNameOrId = AType) then
1117 begin
1118 if TCustomResourceEntry(FHiddenEntries.Items[i]).Id > result then result := TCustomResourceEntry(FHiddenEntries.Items[i]).Id;
1119 end;
1120 end;
1121 for i := 0 to Count-1 do
1122 if (TCustomResourceEntry(Entry[i]).FTypeNameOrId = AType) then
1123 begin
1124 if TCustomResourceEntry(Entry[i]).Id > result then result := TCustomResourceEntry(Entry[i]).Id;
1125 end;
1126end;
1127
1128procedure TWinResourceContainer.IncrementReferenceOf(ANameId, ATypeId: integer);
1129var
1130 item: TCustomResourceEntry;
1131begin
1132 item := InternalFind(NameOrId(ANameId), NameOrId(ATypeId));
1133 if Assigned(item) then inc(item.FReferenceCount);
1134end;
1135
1136procedure TWinResourceContainer.DecrementReferenceOf(ANameId, ATypeId: integer);
1137var
1138 item: TCustomResourceEntry;
1139begin
1140 item := InternalFind(NameOrId(ANameId), NameOrId(ATypeId));
1141 if Assigned(item) then
1142 begin
1143 if item.FReferenceCount > 1 then
1144 dec(item.FReferenceCount)
1145 else
1146 RemoveHidden(item);
1147 end;
1148end;
1149
1150end.
1151
Note: See TracBrowser for help on using the repository browser.