| 1 | unit BGRAWinResource;
|
|---|
| 2 |
|
|---|
| 3 | {$mode objfpc}{$H+}
|
|---|
| 4 |
|
|---|
| 5 | interface
|
|---|
| 6 |
|
|---|
| 7 | uses
|
|---|
| 8 | Classes, SysUtils, BGRAMultiFileType, BGRABitmapTypes, BGRAReadBMP;
|
|---|
| 9 |
|
|---|
| 10 | const
|
|---|
| 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 |
|
|---|
| 37 | type
|
|---|
| 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 |
|
|---|
| 209 | implementation
|
|---|
| 210 |
|
|---|
| 211 | uses Math, BGRAUTF8;
|
|---|
| 212 |
|
|---|
| 213 | operator =(const ANameOrId1, ANameOrId2: TNameOrId): boolean;
|
|---|
| 214 | begin
|
|---|
| 215 | if (ANameOrId1.Id < 0) then
|
|---|
| 216 | result := (ANameOrId2.Id < 0) and (ANameOrId2.Name = ANameOrId1.Name)
|
|---|
| 217 | else
|
|---|
| 218 | result := ANameOrId2.Id = ANameOrId1.Id;
|
|---|
| 219 | end;
|
|---|
| 220 |
|
|---|
| 221 | function NameOrId(AName: string): TNameOrId; overload;
|
|---|
| 222 | begin
|
|---|
| 223 | result.Id := -1;
|
|---|
| 224 | result.Name := AName;
|
|---|
| 225 | end;
|
|---|
| 226 |
|
|---|
| 227 | function NameOrId(AId: integer): TNameOrId; overload;
|
|---|
| 228 | begin
|
|---|
| 229 | result.Id := AId;
|
|---|
| 230 | result.Name := IntToStr(AId);
|
|---|
| 231 | end;
|
|---|
| 232 |
|
|---|
| 233 | { TGroupCursorEntry }
|
|---|
| 234 |
|
|---|
| 235 | function TGroupCursorEntry.GetExtension: utf8string;
|
|---|
| 236 | begin
|
|---|
| 237 | Result:= 'cur';
|
|---|
| 238 | end;
|
|---|
| 239 |
|
|---|
| 240 | function TGroupCursorEntry.ExpectedResourceType: word;
|
|---|
| 241 | begin
|
|---|
| 242 | result := ICON_OR_CURSOR_FILE_CURSOR_TYPE;
|
|---|
| 243 | end;
|
|---|
| 244 |
|
|---|
| 245 | constructor TGroupCursorEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 246 | AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
|
|---|
| 247 | ADataStream: TStream);
|
|---|
| 248 | begin
|
|---|
| 249 | inherited Create(AContainer,NameOrId(RT_GROUP_CURSOR),AEntryNameOrId,AResourceInfo,ADataStream);
|
|---|
| 250 | end;
|
|---|
| 251 |
|
|---|
| 252 | constructor TGroupCursorEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 253 | AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
|
|---|
| 254 | begin
|
|---|
| 255 | inherited Create(AContainer,NameOrId(RT_GROUP_CURSOR),AEntryNameOrId,AResourceInfo);
|
|---|
| 256 | end;
|
|---|
| 257 |
|
|---|
| 258 | { TGroupIconEntry }
|
|---|
| 259 |
|
|---|
| 260 | function TGroupIconEntry.GetExtension: utf8string;
|
|---|
| 261 | begin
|
|---|
| 262 | Result:= 'ico';
|
|---|
| 263 | end;
|
|---|
| 264 |
|
|---|
| 265 | function TGroupIconEntry.ExpectedResourceType: word;
|
|---|
| 266 | begin
|
|---|
| 267 | result := ICON_OR_CURSOR_FILE_ICON_TYPE;
|
|---|
| 268 | end;
|
|---|
| 269 |
|
|---|
| 270 | constructor TGroupIconEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 271 | AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
|
|---|
| 272 | ADataStream: TStream);
|
|---|
| 273 | begin
|
|---|
| 274 | inherited Create(AContainer,NameOrId(RT_GROUP_ICON),AEntryNameOrId,AResourceInfo,ADataStream);
|
|---|
| 275 | end;
|
|---|
| 276 |
|
|---|
| 277 | constructor TGroupIconEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 278 | AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo);
|
|---|
| 279 | begin
|
|---|
| 280 | inherited Create(AContainer,NameOrId(RT_GROUP_ICON),AEntryNameOrId,AResourceInfo);
|
|---|
| 281 | end;
|
|---|
| 282 |
|
|---|
| 283 | { TGroupIconHeader }
|
|---|
| 284 |
|
|---|
| 285 | procedure TGroupIconHeader.SwapIfNecessary;
|
|---|
| 286 | begin
|
|---|
| 287 | Reserved := LEtoN(Reserved);
|
|---|
| 288 | ResourceType := LEtoN(ResourceType);
|
|---|
| 289 | ImageCount := LEtoN(ImageCount);
|
|---|
| 290 | end;
|
|---|
| 291 |
|
|---|
| 292 | { TGroupIconOrCursorEntry }
|
|---|
| 293 |
|
|---|
| 294 | function TGroupIconOrCursorEntry.GetNbIcons: integer;
|
|---|
| 295 | begin
|
|---|
| 296 | result := FGroupIconHeader.ImageCount;
|
|---|
| 297 | end;
|
|---|
| 298 |
|
|---|
| 299 | function TGroupIconOrCursorEntry.GetFileSize: int64;
|
|---|
| 300 | var
|
|---|
| 301 | i: Integer;
|
|---|
| 302 | begin
|
|---|
| 303 | Result:= sizeof(FGroupIconHeader) + sizeof(TIconFileDirEntry)*NbIcons;
|
|---|
| 304 | for i := 0 to NbIcons-1 do
|
|---|
| 305 | Result += LEtoN(FDirectory[i].ImageSize);
|
|---|
| 306 | end;
|
|---|
| 307 |
|
|---|
| 308 | function TGroupIconOrCursorEntry.GetDataSize: integer;
|
|---|
| 309 | begin
|
|---|
| 310 | result := sizeof(FGroupIconHeader) + sizeof(TGroupIconDirEntry)*NbIcons;
|
|---|
| 311 | end;
|
|---|
| 312 |
|
|---|
| 313 | procedure TGroupIconOrCursorEntry.SerializeData(ADestination: TStream);
|
|---|
| 314 | begin
|
|---|
| 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);
|
|---|
| 322 | end;
|
|---|
| 323 |
|
|---|
| 324 | procedure TGroupIconOrCursorEntry.IncrementReferences;
|
|---|
| 325 | var
|
|---|
| 326 | i: Integer;
|
|---|
| 327 | begin
|
|---|
| 328 | for i := 0 to NbIcons-1 do
|
|---|
| 329 | TWinResourceContainer(Container).IncrementReferenceOf(LEtoN(FDirectory[i].ImageId), TypeId - RT_GROUP);
|
|---|
| 330 | end;
|
|---|
| 331 |
|
|---|
| 332 | procedure TGroupIconOrCursorEntry.DecrementReferences;
|
|---|
| 333 | var
|
|---|
| 334 | i: Integer;
|
|---|
| 335 | begin
|
|---|
| 336 | for i := 0 to NbIcons-1 do
|
|---|
| 337 | TWinResourceContainer(Container).DecrementReferenceOf(LEtoN(FDirectory[i].ImageId), TypeId - RT_GROUP);
|
|---|
| 338 | end;
|
|---|
| 339 |
|
|---|
| 340 | constructor TGroupIconOrCursorEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 341 | ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
|
|---|
| 342 | ADataStream: TStream);
|
|---|
| 343 | begin
|
|---|
| 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;
|
|---|
| 356 | end;
|
|---|
| 357 |
|
|---|
| 358 | constructor TGroupIconOrCursorEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 359 | ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId;
|
|---|
| 360 | const AResourceInfo: TResourceInfo);
|
|---|
| 361 | begin
|
|---|
| 362 | inherited Create(AContainer,ATypeNameOrId,AEntryNameOrId,AResourceInfo);
|
|---|
| 363 |
|
|---|
| 364 | FGroupIconHeader.Reserved := 0;
|
|---|
| 365 | FGroupIconHeader.ResourceType := ExpectedResourceType;
|
|---|
| 366 | FGroupIconHeader.ImageCount := 0;
|
|---|
| 367 | end;
|
|---|
| 368 |
|
|---|
| 369 | procedure TGroupIconOrCursorEntry.Clear;
|
|---|
| 370 | begin
|
|---|
| 371 | DecrementReferences;
|
|---|
| 372 | FDirectory := nil;
|
|---|
| 373 | FGroupIconHeader.ImageCount := 0;
|
|---|
| 374 | end;
|
|---|
| 375 |
|
|---|
| 376 | function TGroupIconOrCursorEntry.CopyTo(ADestination: TStream): int64;
|
|---|
| 377 | var
|
|---|
| 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 |
|
|---|
| 399 | begin
|
|---|
| 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;
|
|---|
| 443 | end;
|
|---|
| 444 |
|
|---|
| 445 | procedure TGroupIconOrCursorEntry.CopyFrom(ASource: TStream);
|
|---|
| 446 | var
|
|---|
| 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;
|
|---|
| 455 | begin
|
|---|
| 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;
|
|---|
| 499 | end;
|
|---|
| 500 |
|
|---|
| 501 | { TBitmapResourceEntry }
|
|---|
| 502 |
|
|---|
| 503 | function TBitmapResourceEntry.GetFileSize: int64;
|
|---|
| 504 | begin
|
|---|
| 505 | result := sizeof(TBitMapFileHeader)+FDataStream.Size;
|
|---|
| 506 | end;
|
|---|
| 507 |
|
|---|
| 508 | function TBitmapResourceEntry.GetExtension: utf8string;
|
|---|
| 509 | begin
|
|---|
| 510 | Result:= 'bmp';
|
|---|
| 511 | end;
|
|---|
| 512 |
|
|---|
| 513 | constructor TBitmapResourceEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 514 | AEntryNameOrId: TNameOrId; const AResourceInfo: TResourceInfo;
|
|---|
| 515 | ADataStream: TStream);
|
|---|
| 516 | begin
|
|---|
| 517 | inherited Create(AContainer, NameOrId(RT_BITMAP), AEntryNameOrId, AResourceInfo, ADataStream);
|
|---|
| 518 | end;
|
|---|
| 519 |
|
|---|
| 520 | function TBitmapResourceEntry.CopyTo(ADestination: TStream): int64;
|
|---|
| 521 | var fileHeader: TBitMapFileHeader;
|
|---|
| 522 | begin
|
|---|
| 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);
|
|---|
| 530 | end;
|
|---|
| 531 |
|
|---|
| 532 | procedure TBitmapResourceEntry.CopyFrom(ASource: TStream);
|
|---|
| 533 | var
|
|---|
| 534 | fileHeader: TBitMapFileHeader;
|
|---|
| 535 | dataSize: integer;
|
|---|
| 536 | begin
|
|---|
| 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);
|
|---|
| 547 | end;
|
|---|
| 548 |
|
|---|
| 549 | { TUnformattedResourceEntry }
|
|---|
| 550 |
|
|---|
| 551 | function TUnformattedResourceEntry.GetFileSize: int64;
|
|---|
| 552 | begin
|
|---|
| 553 | Result:= FDataStream.Size;
|
|---|
| 554 | end;
|
|---|
| 555 |
|
|---|
| 556 | function TUnformattedResourceEntry.GetDataSize: integer;
|
|---|
| 557 | begin
|
|---|
| 558 | result := FDataStream.Size;
|
|---|
| 559 | end;
|
|---|
| 560 |
|
|---|
| 561 | procedure TUnformattedResourceEntry.SerializeData(ADestination: TStream);
|
|---|
| 562 | begin
|
|---|
| 563 | if FDataStream.Size > 0 then
|
|---|
| 564 | begin
|
|---|
| 565 | FDataStream.Position := 0;
|
|---|
| 566 | ADestination.CopyFrom(FDataStream, FDataStream.Size);
|
|---|
| 567 | end;
|
|---|
| 568 | end;
|
|---|
| 569 |
|
|---|
| 570 | function TUnformattedResourceEntry.GetExtension: utf8string;
|
|---|
| 571 | var format: TBGRAImageFormat;
|
|---|
| 572 | begin
|
|---|
| 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;
|
|---|
| 592 | end;
|
|---|
| 593 |
|
|---|
| 594 | constructor TUnformattedResourceEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 595 | ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId;
|
|---|
| 596 | const AResourceInfo: TResourceInfo; ADataStream: TStream);
|
|---|
| 597 | begin
|
|---|
| 598 | inherited Create(AContainer,ATypeNameOrId,AEntryNameOrId,AResourceInfo);
|
|---|
| 599 | FDataStream := ADataStream;
|
|---|
| 600 | end;
|
|---|
| 601 |
|
|---|
| 602 | destructor TUnformattedResourceEntry.Destroy;
|
|---|
| 603 | begin
|
|---|
| 604 | FreeAndNil(FDataStream);
|
|---|
| 605 | inherited Destroy;
|
|---|
| 606 | end;
|
|---|
| 607 |
|
|---|
| 608 | function TUnformattedResourceEntry.CopyTo(ADestination: TStream): int64;
|
|---|
| 609 | begin
|
|---|
| 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;
|
|---|
| 617 | end;
|
|---|
| 618 |
|
|---|
| 619 | { TResourceInfo }
|
|---|
| 620 |
|
|---|
| 621 | procedure TResourceInfo.SwapIfNecessary;
|
|---|
| 622 | begin
|
|---|
| 623 | DataVersion := LEtoN(DataVersion);
|
|---|
| 624 | MemoryFlags := LEtoN(MemoryFlags);
|
|---|
| 625 | LanguageId := LEtoN(LanguageId);
|
|---|
| 626 | Version := LEtoN(Version);
|
|---|
| 627 | Characteristics := LEtoN(Characteristics);
|
|---|
| 628 | end;
|
|---|
| 629 |
|
|---|
| 630 | { TCustomResourceEntry }
|
|---|
| 631 |
|
|---|
| 632 | function TCustomResourceEntry.GetId: integer;
|
|---|
| 633 | begin
|
|---|
| 634 | result := FEntryNameOrId.Id;
|
|---|
| 635 | end;
|
|---|
| 636 |
|
|---|
| 637 | function TCustomResourceEntry.GetTypeId: integer;
|
|---|
| 638 | begin
|
|---|
| 639 | result := FTypeNameOrId.Id;
|
|---|
| 640 | end;
|
|---|
| 641 |
|
|---|
| 642 | function GetDWord(var ASource: PByte; var ARemainingBytes: Integer): DWord;
|
|---|
| 643 | begin
|
|---|
| 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;
|
|---|
| 655 | end;
|
|---|
| 656 |
|
|---|
| 657 | function GetWord(var ASource: PByte; var ARemainingBytes: Integer): Word;
|
|---|
| 658 | begin
|
|---|
| 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;
|
|---|
| 670 | end;
|
|---|
| 671 |
|
|---|
| 672 | function GetNameOrId(var ASource: PByte; var ARemainingBytes: Integer): TNameOrId;
|
|---|
| 673 | var curChar: Word;
|
|---|
| 674 | pstart: PByte;
|
|---|
| 675 | begin
|
|---|
| 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;
|
|---|
| 689 | end;
|
|---|
| 690 |
|
|---|
| 691 | function TCustomResourceEntry.GetLanguageId: integer;
|
|---|
| 692 | begin
|
|---|
| 693 | result := FResourceInfo.LanguageId;
|
|---|
| 694 | end;
|
|---|
| 695 |
|
|---|
| 696 | class function TCustomResourceEntry.GetNextEntry(AContainer: TMultiFileContainer; AStream: TStream): TCustomResourceEntry;
|
|---|
| 697 | var
|
|---|
| 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;
|
|---|
| 706 | begin
|
|---|
| 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;
|
|---|
| 749 | end;
|
|---|
| 750 |
|
|---|
| 751 | procedure WriteNameOrId(ADestination: TStream; ANameOrId: TNameOrId);
|
|---|
| 752 | var buffer: PUnicodeChar;
|
|---|
| 753 | maxLen,actualLen: integer;
|
|---|
| 754 | begin
|
|---|
| 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;
|
|---|
| 771 | end;
|
|---|
| 772 |
|
|---|
| 773 | procedure TCustomResourceEntry.Serialize(ADestination: TStream);
|
|---|
| 774 | var zero: DWord;
|
|---|
| 775 | padding: integer;
|
|---|
| 776 | begin
|
|---|
| 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;
|
|---|
| 785 | end;
|
|---|
| 786 |
|
|---|
| 787 | procedure TCustomResourceEntry.SetLanguageId(AValue: integer);
|
|---|
| 788 | begin
|
|---|
| 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');
|
|---|
| 803 | end;
|
|---|
| 804 |
|
|---|
| 805 | procedure TCustomResourceEntry.SerializeHeader(ADestination: TStream);
|
|---|
| 806 | var
|
|---|
| 807 | entryHeader: record
|
|---|
| 808 | EntrySize: integer;
|
|---|
| 809 | HeaderSize: integer;
|
|---|
| 810 | end;
|
|---|
| 811 | headerStream: TMemoryStream;
|
|---|
| 812 | begin
|
|---|
| 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;
|
|---|
| 833 | end;
|
|---|
| 834 |
|
|---|
| 835 | constructor TCustomResourceEntry.Create(AContainer: TMultiFileContainer;
|
|---|
| 836 | ATypeNameOrId: TNameOrId; AEntryNameOrId: TNameOrId;
|
|---|
| 837 | const AResourceInfo: TResourceInfo);
|
|---|
| 838 | begin
|
|---|
| 839 | inherited Create(AContainer);
|
|---|
| 840 | FTypeNameOrId := ATypeNameOrId;
|
|---|
| 841 | FEntryNameOrId := AEntryNameOrId;
|
|---|
| 842 | FResourceInfo := AResourceInfo;
|
|---|
| 843 | end;
|
|---|
| 844 |
|
|---|
| 845 | procedure TCustomResourceEntry.SetId(AValue: integer);
|
|---|
| 846 | begin
|
|---|
| 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');
|
|---|
| 857 | end;
|
|---|
| 858 |
|
|---|
| 859 | function TCustomResourceEntry.GetName: utf8string;
|
|---|
| 860 | begin
|
|---|
| 861 | Result:= FEntryNameOrId.Name;
|
|---|
| 862 | end;
|
|---|
| 863 |
|
|---|
| 864 | procedure TCustomResourceEntry.SetName(AValue: utf8string);
|
|---|
| 865 | begin
|
|---|
| 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;
|
|---|
| 871 | end;
|
|---|
| 872 |
|
|---|
| 873 | function TCustomResourceEntry.GetTypeName: utf8string;
|
|---|
| 874 | begin
|
|---|
| 875 | result := FTypeNameOrId.Name;
|
|---|
| 876 | end;
|
|---|
| 877 |
|
|---|
| 878 | procedure TCustomResourceEntry.IncrementReferences;
|
|---|
| 879 | begin
|
|---|
| 880 | //nothing
|
|---|
| 881 | end;
|
|---|
| 882 |
|
|---|
| 883 | procedure TCustomResourceEntry.DecrementReferences;
|
|---|
| 884 | begin
|
|---|
| 885 | //nothing
|
|---|
| 886 | end;
|
|---|
| 887 |
|
|---|
| 888 | { TWinResourceContainer }
|
|---|
| 889 |
|
|---|
| 890 | procedure TWinResourceContainer.LoadFromStream(AStream: TStream);
|
|---|
| 891 | var curEntry: TCustomResourceEntry;
|
|---|
| 892 | i: Integer;
|
|---|
| 893 | begin
|
|---|
| 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;
|
|---|
| 907 | end;
|
|---|
| 908 |
|
|---|
| 909 | function TWinResourceContainer.IndexOf(AName: utf8string; AExtenstion: utf8string; ACaseSensitive: boolean): integer;
|
|---|
| 910 | begin
|
|---|
| 911 | result := IndexOf(AName, AExtenstion, 0, ACaseSensitive);
|
|---|
| 912 | end;
|
|---|
| 913 |
|
|---|
| 914 | function TWinResourceContainer.IndexOf(AName: utf8string; AExtenstion: utf8string;
|
|---|
| 915 | ALanguageId: integer; ACaseSensitive: boolean): integer;
|
|---|
| 916 | var
|
|---|
| 917 | i: Integer;
|
|---|
| 918 | entryId, errPos: integer;
|
|---|
| 919 | begin
|
|---|
| 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;
|
|---|
| 959 | end;
|
|---|
| 960 |
|
|---|
| 961 | procedure TWinResourceContainer.Init;
|
|---|
| 962 | begin
|
|---|
| 963 | inherited Init;
|
|---|
| 964 | FHiddenEntries := TMultiFileEntryList.Create;
|
|---|
| 965 | end;
|
|---|
| 966 |
|
|---|
| 967 | procedure TWinResourceContainer.ClearHiddenEntries;
|
|---|
| 968 | var i: integer;
|
|---|
| 969 | begin
|
|---|
| 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;
|
|---|
| 976 | end;
|
|---|
| 977 |
|
|---|
| 978 | procedure TWinResourceContainer.RemoveHidden(AEntry: TCustomResourceEntry);
|
|---|
| 979 | var
|
|---|
| 980 | index: LongInt;
|
|---|
| 981 | begin
|
|---|
| 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;
|
|---|
| 991 | end;
|
|---|
| 992 |
|
|---|
| 993 | function TWinResourceContainer.CreateEntry(AName: utf8string; AExtension: utf8string;
|
|---|
| 994 | AContent: TStream; ALanguageId: integer): TMultiFileEntry;
|
|---|
| 995 | var
|
|---|
| 996 | resourceInfo: TResourceInfo;
|
|---|
| 997 | entryName: TNameOrId;
|
|---|
| 998 | errPos: integer;
|
|---|
| 999 | begin
|
|---|
| 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;
|
|---|
| 1041 | end;
|
|---|
| 1042 |
|
|---|
| 1043 | function TWinResourceContainer.CreateEntry(AName: utf8string; AExtension: utf8string;
|
|---|
| 1044 | AContent: TStream): TMultiFileEntry;
|
|---|
| 1045 | begin
|
|---|
| 1046 | result := CreateEntry(AName, AExtension, AContent, 0);
|
|---|
| 1047 | end;
|
|---|
| 1048 |
|
|---|
| 1049 | procedure TWinResourceContainer.Clear;
|
|---|
| 1050 | begin
|
|---|
| 1051 | ClearHiddenEntries;
|
|---|
| 1052 | inherited Clear;
|
|---|
| 1053 | end;
|
|---|
| 1054 |
|
|---|
| 1055 | destructor TWinResourceContainer.Destroy;
|
|---|
| 1056 | begin
|
|---|
| 1057 | ClearHiddenEntries;
|
|---|
| 1058 | FreeAndNil(FHiddenEntries);
|
|---|
| 1059 | inherited Destroy;
|
|---|
| 1060 | end;
|
|---|
| 1061 |
|
|---|
| 1062 | procedure TWinResourceContainer.Delete(AIndex: integer);
|
|---|
| 1063 | begin
|
|---|
| 1064 | if (AIndex >= 0) and (AIndex < Count) then
|
|---|
| 1065 | TCustomResourceEntry(Entry[AIndex]).DecrementReferences;
|
|---|
| 1066 | inherited Delete(AIndex);
|
|---|
| 1067 | end;
|
|---|
| 1068 |
|
|---|
| 1069 | procedure TWinResourceContainer.SaveToStream(ADestination: TStream);
|
|---|
| 1070 | var
|
|---|
| 1071 | i: Integer;
|
|---|
| 1072 | begin
|
|---|
| 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);
|
|---|
| 1077 | end;
|
|---|
| 1078 |
|
|---|
| 1079 | function TWinResourceContainer.InternalFind(const AEntry: TNameOrId;
|
|---|
| 1080 | const AType: TNameOrId; ALanguageId: integer): TCustomResourceEntry;
|
|---|
| 1081 | var i: integer;
|
|---|
| 1082 | begin
|
|---|
| 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;
|
|---|
| 1102 | end;
|
|---|
| 1103 |
|
|---|
| 1104 | procedure TWinResourceContainer.AddHidden(AEntry: TCustomResourceEntry);
|
|---|
| 1105 | begin
|
|---|
| 1106 | FHiddenEntries.Add(AEntry);
|
|---|
| 1107 | end;
|
|---|
| 1108 |
|
|---|
| 1109 | function TWinResourceContainer.GetMaxId(AType: TNameOrId): integer;
|
|---|
| 1110 | var i: integer;
|
|---|
| 1111 | begin
|
|---|
| 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;
|
|---|
| 1126 | end;
|
|---|
| 1127 |
|
|---|
| 1128 | procedure TWinResourceContainer.IncrementReferenceOf(ANameId, ATypeId: integer);
|
|---|
| 1129 | var
|
|---|
| 1130 | item: TCustomResourceEntry;
|
|---|
| 1131 | begin
|
|---|
| 1132 | item := InternalFind(NameOrId(ANameId), NameOrId(ATypeId));
|
|---|
| 1133 | if Assigned(item) then inc(item.FReferenceCount);
|
|---|
| 1134 | end;
|
|---|
| 1135 |
|
|---|
| 1136 | procedure TWinResourceContainer.DecrementReferenceOf(ANameId, ATypeId: integer);
|
|---|
| 1137 | var
|
|---|
| 1138 | item: TCustomResourceEntry;
|
|---|
| 1139 | begin
|
|---|
| 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;
|
|---|
| 1148 | end;
|
|---|
| 1149 |
|
|---|
| 1150 | end.
|
|---|
| 1151 |
|
|---|