• Reverse engineering the. Car file format (Compiled Asset Catalogs)
  • Originally written by Timac
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: LoneyIsError
  • Proofread by: jaredliw

I refer to part of the content of this article for PNG images in iOS projects, and hope to support more

The reverse.carFile (compiled Asset Catalogs)

The Asset Catalog is an important part of any iOS, tvOS, watchOS, and macOS application. It lets you organize and manage the different materials your application uses, such as images, Sprites, textures, ARKit resources, colors, and data.

Apple continues to expand Asset Catalogs functionality each year:

  • Xcode 9 adds support for Color Asset and improves vector Resources (PDF) support. See WWDC 2017 Session Cocoa new features.
  • Xcode 10 adds support for efficient graphics, Apple deep pixel image compression, and macOS Mojave dark mode. See WWDC 2018 Session Optimization App materials.

Less well known is that the Asset Catalog is compiled into a.car file when building an application using Xcode. However, Apple has no documentation for.car files, and surprisingly, I couldn’t find much information online.

In this article, I try to fill in the gaps in information about.car format files by describing the global structure of the.car file and its different elements. In this article, I built a CARParser tool to manually parse.car files. The full source code for the tool can be downloaded at the end of this article.

Please note that the documentation and CARParser tool in this article are for educational purposes only. You should not work directly with.car files as you do here. Several tools (including my own, which I plan to open source at some point) can dump the contents of.car files. But these tools only use some of Apple’s proprietary apis and do not parse files directly. Similarly, with reverse engineering, there is no guarantee that the data will be completely accurate. At the time of publication, this article should reflect the state in macOS Mojave and iOS 12. However, it may become obsolete with future updates to macOS or iOS versions.

What is Asset Catalogs?

The Asset Catalog was introduced in Xcode 5 to make it easier to manage images, especially when working with PNG images (@1x, @2x, @3x, etc.) in multiple resolutions. In Xcode, the Asset Catalog shows up as the.xcassets folder, and Apple describes its purpose in detail. The.xcassets Format on disk is also well described by Apple in the Asset Catalog Format Reference.

For the purposes of this article, I created a sample Asset Catalog that contains various types of artifacts. You can download the sample Asset Catalog here. The corresponding.car file can be downloaded here.

The Asset Catalog set for iOS 12 contains the following files:

  • PNG files with 3 resolutions @1x, @2x, and @3x
  • A PDF file
  • A text file
  • A JPG image
  • One color file (red, 50% opacity)

What is thecarFile?

When a developer builds an iOS, watchOS, tvOS, or macOS application, the Asset Catalog containing various artifacts (images, ICONS, textures, and so on) is not simply copied into the application package, but rather compiled into a CAR file.

When the application is running on iOS, getting the image from the CAR file is as simple as:

UIImage *myImage = [UIImage imageNamed:@"MyImage"];
Copy the code

Perform this line of code, from private CoreUI. The framework (/ System/Library/PrivateFrameworks/CoreUI framework) provides with named myimage.png material for the most suitable UIImage. MyImage is the name of the material, also known as the dimension name. A CAR file can contain multiple images for a given story name: @1x resolution, @2x resolution, @3x resolution, dark mode,… The representations of these resources are called replicas. Each copy has a unique identifier, called a copy key. In effect, the replica key is a list of properties that describe the replica: original latitude, resolution,…

What does the CAR extension mean? Take, for example, the names of various methods in the IBFoundation framework in Xcode, which might represent Compiled Asset records.

On macOS, there are several closed-source tools to handle the Asset Catalog:

  • Xcode allows you to editAsset CatalogAnd compile them
  • actoolAllows you to compile, print, update, and validateAsset Catalog
  • assetutilYou’re allowed to handle it.carFile. It can be fromcarDelete unwanted material from the file, but it can also be parsedcarFile and generate JSON file output.

Running the assetutil -i assets. car command prints some interesting information about the car file:

[{"AssetStorageVersion" : "IBCocoaTouchImageCatalogTool - 10.0"."Authoring Tool" : "@ #) PROGRAM: CoreThemeDefinition PROJECT: CoreThemeDefinition - 346.29 \ n"."CoreUIVersion" : 498."DumpToolVersion" : 498.4599999999998976."Key Format" : [
      "kCRThemeAppearanceName"."kCRThemeScaleName"."kCRThemeIdiomName"."kCRThemeSubtypeName"."kCRThemeDeploymentTargetName"."kCRThemeGraphicsClassName"."kCRThemeMemoryClassName"."kCRThemeDisplayGamutName"."kCRThemeDirectionName"."kCRThemeSizeClassHorizontalName"."kCRThemeSizeClassVerticalName"."kCRThemeIdentifierName"."kCRThemeElementName"."kCRThemePartName"."kCRThemeStateName"."kCRThemeValueName"."kCRThemeDimension1Name"."kCRThemeDimension2Name"]."MainVersion" : "@ #) PROGRAM: CoreUI PROJECT: CoreUI - 498.40.1 \ n"."Platform" : "ios"."PlatformVersion" : "12.0"."SchemaVersion" : 2."StorageVersion" : 15
  },
  {
    "AssetType" : "Data"."Compression" : "uncompressed"."Data Length" : 7284."Idiom" : "universal"."Name" : "MyPDF"."Scale" : 1."SizeOnDisk" : 7538."UTI" : "com.adobe.pdf"
  },
  {
    "AssetType" : "Data"."Compression" : "uncompressed"."Data Length" : 14."Idiom" : "universal"."Name" : "MyText"."Scale" : 1."SizeOnDisk" : 238."UTI" : "UTI-Unknown"
  },
  {
    "AssetType" : "Image"."BitsPerComponent" : 8."ColorModel" : "RGB"."Colorspace" : "srgb"."Compression" : "palette-img"."Encoding" : "ARGB"."Idiom" : "universal"."Image Type" : "kCoreThemeOnePartScale"."Name" : "MyPNG"."Opaque" : false."PixelHeight" : 28."PixelWidth" : 28."RenditionName" : "Timac.png"."Scale" : 1."SizeOnDisk" : 1007."Template Mode" : "automatic"
  },
  {
    "AssetType" : "Color"."Color components" : [
      1.0.0.0.5]."Colorspace" : "srgb"."Idiom" : "universal"."Name" : "MyColor"."Scale" : 1
  },
  {
    "AssetType" : "Image"."BitsPerComponent" : 8."ColorModel" : "RGB"."Encoding" : "JPEG"."Idiom" : "universal"."Image Type" : "kCoreThemeOnePartScale"."Name" : "MyJPG"."Opaque" : true."PixelHeight" : 200."PixelWidth" : 200."RenditionName" : "TimacJPG.jpg"."Scale" : 1."SizeOnDisk" : 8042."Template Mode" : "automatic"
  },
  {
    "AssetType" : "Image"."BitsPerComponent" : 8."ColorModel" : "RGB"."Colorspace" : "srgb"."Compression" : "palette-img"."Encoding" : "ARGB"."Idiom" : "universal"."Image Type" : "kCoreThemeOnePartScale"."Name" : "MyPNG"."Opaque" : false."PixelHeight" : 56."PixelWidth" : 56."RenditionName" : "[email protected]"."Scale" : 2."SizeOnDisk" : 1102."Template Mode" : "automatic"
  },
  {
    "AssetType" : "Image"."BitsPerComponent" : 8."ColorModel" : "RGB"."Colorspace" : "srgb"."Compression" : "palette-img"."Encoding" : "ARGB"."Idiom" : "universal"."Image Type" : "kCoreThemeOnePartScale"."Name" : "MyPNG"."Opaque" : false."PixelHeight" : 84."PixelWidth" : 84."RenditionName" : "[email protected]"."Scale" : 3."SizeOnDisk" : 1961."Template Mode" : "automatic"}]Copy the code

Special BOM document

Opening the CAR file in HexFiend displays some useful information:

The magic value BOMStore tells us that the CAR file is a special BOM file. BOM (Bill of Materials) is a file format inherited from NeXTSTEP and still used in macOS installers to identify files to install, remove, or upgrade. You can find some basic information in the MAN 5 BOM:

The Mac OS X installer uses the file system 'bill of Materials' to determine which files to install, remove, or upgrade. The Bill of Materials (BOM) contains all the documents in the catalog and some information about each document. File information includes the UNIX permission, owner, group, size, and time when the file was last modified. It also includes checksums for each file and information about hard links.Copy the code

MacOS provides several closed-source tools to process BOM files, such as LSBOM and MKBOM. You can use the lsbom check in/private/var/db/receipts/the installation program. For example, run the lsbom/private/var/db/receipts/com. Apple. PKG. Numbers5. Bom will print apple Numbers to install all of the files (path, permissions, UID/GID, size, and CRC32 checksum) :

. 40775 0/0 ./Applications 40775 0/80 ./Applications/Numbers.app 40755 0/0 ./Applications/Numbers.app/Contents 40755 0/0  ./Applications/Numbers.app/Contents/Info.plist 100644 0/0 7093 2611993997 ./Applications/Numbers.app/Contents/MacOS 40755 0/0 ./Applications/Numbers.app/Contents/MacOS/Numbers 100755 0/0 9838697539155192 ./Applications/Numbers.app/Contents/PkgInfo 100644 0/0 8 3080130777 [...]Copy the code

Unfortunately, the BOM file format itself is not documented, and tools that manipulate bom files cannot process CAR files. Lsbom, reimplemented by Joseph Coffland and Julian Devlin, contains some useful information about the BOM file format in its code. As we can see, the BOM stores blocks and trees.

However, in contrast to a regular BOM file, a CAR file contains multiple BOM blocks:

  • CARHEADER

  • EXTENDED_METADATA

  • KEYFORMAT

  • CARGLOBALS

  • KEYFORMATWORKAROUND

  • EXTERNAL_KEYS

And a BOM tree to store multiple databases:

  • FACETKEYS

  • RENDITIONS

  • APPEARANCEKEYS

  • COLORS

  • FONTS

  • FONTSIZES

  • GLYPHS

  • BEZELS

  • BITMAPKEYS

  • ELEMENT_INFO

  • PART_INFO

Some blocks and trees are optional. In this article, I describe only a few important blocks: CARHEADER, EXTENDED_METADATA, KEYFORMAT, and important trees: FACETKEYS, RENDITIONS, and APPEARANCEKEYS. Other blocks and trees are generally empty or nonexistent.

Parsing the BOM

On macOS, the private Coreui. framework is responsible for extracting these stories and therefore includes the code to parse the BOM. Facts have proven that it USES and is located in the/System/Library/PrivateFrameworks/Bom. Private Bom in the framework. The framework of the same code. I decided to use this proprietary framework to parse the BOM.

By looking at the calls to the CoreUI framework, it was easy to reverse the API required to parse a CAR file, and I ended up with these Class C apis:

typedef uint32_t BOMBlockID;
typedef struct BOMStorage *BOMStorage;

typedef struct BOMTree *BOMTree;
typedef struct BOMTreeIterator *BOMTreeIterator;

// Opening a BOM
BOMStorage BOMStorageOpen(const char *inPath, Boolean inWriting);

// Accessing a BOM block
BOMBlockID BOMStorageGetNamedBlock(BOMStorage inStorage, const char *inName);
size_t BOMStorageSizeOfBlock(BOMStorage inStorage, BOMBlockID inBlockID);
int BOMStorageCopyFromBlock(BOMStorage inStorage, BOMBlockID inBlockID, void *outData);

// Accessing a BOM tree
BOMTree BOMTreeOpenWithName(BOMStorage inStorage, const char *inName, Boolean inWriting);
BOMTreeIterator BOMTreeIteratorNew(BOMTree inTree, void *, void *, void *);
Boolean BOMTreeIteratorIsAtEnd(BOMTreeIterator iterator);
void BOMTreeIteratorNext(BOMTreeIterator iterator);

// Accessing the keys and values of a BOM tree
void * BOMTreeIteratorKey(BOMTreeIterator iterator);
size_t BOMTreeIteratorKeySize(BOMTreeIterator iterator);
void * BOMTreeIteratorValue(BOMTreeIterator iterator);
size_t BOMTreeIteratorValueSize(BOMTreeIterator iterator);
Copy the code

Using the BOM. framework’s private API, accessing the CARHEADER block’s data is as simple as executing a command:

NSData *blockData = GetDataFromBomBlock(bomStorage, "CARHEADER");
Copy the code

GetDataFromBomBlock() is implemented as:

NSData *GetDataFromBomBlock(BOMStorage inBOMStorage, const char *inBlockName) {
	NSData *outData = nil;
	
	BOMBlockID blockID = BOMStorageGetNamedBlock(inBOMStorage, inBlockName);
	size_t blockSize = BOMStorageSizeOfBlock(inBOMStorage, blockID);
	if(blockSize > 0) {
		void *mallocedBlock = malloc(blockSize);
		int res = BOMStorageCopyFromBlock(inBOMStorage, blockID, mallocedBlock);
		if(res == noErr) {
			outData = [[NSData alloc] initWithBytes:mallocedBlock length:blockSize];
		}
		
		free(mallocedBlock);
	}
	
	return outData;
}
Copy the code

Similarly, there is a simple way to get all the keys/values of a BOM tree. For example, to get all the keys/values of the FACETKEYS tree, you could execute the following code:

ParseBOMTree(bomStorage, "FACETKEYS"And ^ (NSData *inKey, NSData *inValue) {
	// This Objective-C block is called for each key found.
	// The value corresponding to the key is passed as parameter.
});
Copy the code

The ParseBOMTree() method is implemented as follows:

typedef void (^ParseBOMTreeCallback)(NSData *inKey, NSData *inValue);
void ParseBOMTree(BOMStorage inBOMStorage, const char *inTreeName, ParseBOMTreeCallback keyValueCallback) {
	NSData *keyData = nil;
	NSData *keyValue = nil;
	
	// Open the BOM tree
	BOMTree bomTree = BOMTreeOpenWithName(inBOMStorage, inTreeName, false);
	if(bomTree == NULL)
		return;

	// Create a BOMTreeIterator and loop until the end
	BOMTreeIterator	bomIterator = BOMTreeIteratorNew(bomTree, NULL.NULL.NULL);
	while(! BOMTreeIteratorIsAtEnd(bomIterator)) {// Get the key
		void * key = BOMTreeIteratorKey(bomIterator);
		size_t keySize = BOMTreeIteratorKeySize(bomIterator);
		keyData = [NSData dataWithBytes:key length:keySize];
		
		// Get the value associated to the key
		size_t valueSize = BOMTreeIteratorValueSize(bomIterator);
		if(valueSize > 0) {
			void * value = BOMTreeIteratorValue(bomIterator);
			if(value ! =NULL) {
				keyValue = [NSDatadataWithBytes:value length:valueSize]; }}if(keyData ! =nil) {
			keyValueCallback(keyData, keyValue);
		}
		
		// Next item in the treeBOMTreeIteratorNext(bomIterator); }}Copy the code

Now that we can access the contents of the BOM, let’s look at the different blocks and trees.

CARHEADER block

The CARHEADER block contains information about the number of stories in the file as well as version information. It has a fixed size of 436 bytes. You can access the data using the GetDataFromBomBlock() method described earlier:

NSData *blockData = GetDataFromBomBlock(bomStorage, "CARHEADER");
if(blockData ! = nil) {struct carheader *carHeader =(struct carheader *)[blockData bytes]; [...]. }Copy the code

To help understand these structures, I use the application Synalyze It! With a custom create syntax. Pro to parse various data blocks. In Synalyze It! The structure of CARHEADER in Pro is as follows:

The structure of the reverse output is very simple, we can see that the data starts with the tag CTAR:

struct carheader {
    uint32_t tag;								// 'CTAR'
    uint32_t coreuiVersion;
    uint32_t storageVersion;
    uint32_t storageTimestamp;
    uint32_t renditionCount;
    char mainVersionString[128];
    char versionString[256];
    uuid_t uuid;
    uint32_t associatedChecksum;
    uint32_t schemaVersion;
    uint32_t colorSpaceID;
    uint32_t keySemantics;
} __attribute__((packed));
Copy the code

Here’s what you see when you parse the demo footage:

CARHEADER: coreuiVersion: 498 storageVersion: 15 storageTimestamp: 1539543253 (2018-10-14T18:54:13Z) renditionCount: 7 mainVersionString: @ (#) PROGRAM: CoreUI PROJECT: CoreUI - 498.40.1 versionString: uuid IBCocoaTouchImageCatalogTool - 10.0: 9EA56D07-3242-4F88-8BC1-C16C25EA65F2 associatedChecksum: 0x79965D18 schemaVersion: 2 colorSpaceID: 1 keySemantics: 2Copy the code

EXTENDED_METADATA block

The EXTENDED_METADATA block has a fixed size of 1028 bytes and contains some additional information:

The structure is simple and starts with the tag META:

struct carextendedMetadata {
    uint32_t tag;								// 'META'
    char thinningArguments[256];
    char deploymentPlatformVersion[256];
    char deploymentPlatform[256];
    char authoringTool[256];
} __attribute__((packed));
Copy the code

Here’s what you can see when you analyze such blocks:

EXTENDED_METADATA: thinningArguments: deploymentPlatformVersion: 12.0 deploymentPlatform: ios authoringTool: @ (#) PROGRAM: CoreThemeDefinition PROJECT: CoreThemeDefinition - 346.29Copy the code

APPEARANCEKEYS tree

Before we look at the more complex trees, let’s start with the APPEARANCEKEYS tree. This tree is used to support the new dark mode in macOS Mojave. Since there is no dark mode in iOS, you will not see the APPEARANCEKEYS tree from the CAR files of iOS apps — at least not in iOS 12 and earlier.

In iOS 13 it will

In this tree, keys are appearance names (strings) and values are appearance unique identifiers (uint16_t). Parsing key/value pairs is therefore a breeze:

ParseBOMTree(bomStorage, "APPEARANCEKEYS"And ^ (NSData *inKey, NSData *inValue) {
	NSString *appearanceName = [[NSString alloc] initWithBytes:[inKey bytes] length:[inKey length] encoding:NSUTF8StringEncoding];
	uint16_t appearanceIdentifier = 0;
	if(inValue ! =nil) {
		appearanceIdentifier = *(uint16_t *)([inValue bytes]);
	}
	
	fprintf(stderr, "\t '%s': %u\n", [appearanceName UTF8String], appearanceIdentifier);
});
Copy the code

For example, running this code on a macOS CAR file produces:

Tree APPEARANCEKEYS
	 'NSAppearanceNameAccessibilityDarkAqua': 6
	 'NSAppearanceNameAccessibilitySystem': 3
	 'NSAppearanceNameDarkAqua': 1
	 'NSAppearanceNameSystem': 0
Copy the code

FACETKEYS tree

The FACETKEYS tree contains the dimension name — which is a synonym for the material name and is used for the values of keys and their attributes. For example, for the key MyColor in the demo material, we can see its value:

<00000000 03000100 55000200 D9001100 9FAF>
Copy the code

The value is a renditionKeyToken structure that contains a list of attributes:

struct renditionkeytoken {
  struct {
    uint16_t x;
    uint16_t y;
  } cursorHotSpot;
  
  uint16_t numberOfAttributes;
  struct renditionAttribute attributes[];
} __attribute__((packed));
Copy the code

The cursorHotSpot field seems to be a relic of the old cursor functionality. After that, we can see the number of attributes, followed by the list of attributes. The attributes themselves are key/value pairs with simple structures of names and values:

struct renditionAttribute {
	uint16_t name;
	uint16_t value;
} __attribute__((packed));
Copy the code

There is a set of possible attribute names:

enum RenditionAttributeType {
	kRenditionAttributeType_ThemeLook       = 0,
	kRenditionAttributeType_Element	        = 1,
	kRenditionAttributeType_Part		= 2,
	kRenditionAttributeType_Size		= 3,
	kRenditionAttributeType_Direction	= 4,
	kRenditionAttributeType_placeholder	= 5,
	kRenditionAttributeType_Value	        = 6,
	kRenditionAttributeType_ThemeAppearance = 7,
	kRenditionAttributeType_Dimension1  = 8,
	kRenditionAttributeType_Dimension2  = 9,
	kRenditionAttributeType_State       = 10,
	kRenditionAttributeType_Layer       = 11,
	kRenditionAttributeType_Scale       = 12,
	kRenditionAttributeType_Unknown13   = 13,
	kRenditionAttributeType_PresentationState = 14,
	kRenditionAttributeType_Idiom	          = 15,
	kRenditionAttributeType_Subtype		  = 16,
	kRenditionAttributeType_Identifier	  = 17,
	kRenditionAttributeType_PreviousValue	  = 18,
	kRenditionAttributeType_PreviousState	  = 19,
	kRenditionAttributeType_HorizontalSizeClass     = 20,
	kRenditionAttributeType_VerticalSizeClass	= 21,
	kRenditionAttributeType_MemoryLevelClass	= 22,
	kRenditionAttributeType_GraphicsFeatureSetClass = 23,
	kRenditionAttributeType_DisplayGamut		= 24,
	kRenditionAttributeType_DeploymentTarget	= 25
};
Copy the code

Once you understand the structure, you can parse the FACETKEYS tree using the following code:

ParseBOMTree(bomStorage, "FACETKEYS"And ^ (NSData *inKey, NSData *inValue)
{
	NSString *facetName = [[NSString alloc] initWithBytes:[inKey bytes] length:[inKey length] encoding:NSUTF8StringEncoding];
	fprintf(stderr, "\t '%s':", [facetName UTF8String]);
	
	const void *bytes = [inValue bytes];
	if(bytes ! =NULL) {
		struct renditionkeytoken *renditionkeytoken = (struct renditionkeytoken *)bytes;
		uint16_t numberOfAttributes = renditionkeytoken->numberOfAttributes;
		for(uint16_t keyIndex = 0 ; keyIndex < numberOfAttributes ; keyIndex++) {
			struct renditionAttribute renditionAttribute = renditionkeytoken->attributes[keyIndex];
			fprintf(stderr, "\n\t\t %s: %04X", [GetNameOfAttributeType(renditionAttribute.name) UTF8String], renditionAttribute.value);
		}
	}
	
	fprintf(stderr, "\n");
});
Copy the code

Running this code on the sample project prints:

Tree FACETKEYS
	 'Image1':
		 Element: 0055
		 Part: 00B5
		 Identifier: 8019
	 'Image2':
		 Element: 0055
		 Part: 00B5
		 Identifier: 0C7A
	 'Image3':
		 Element: 0055
		 Part: 00B5
		 Identifier: 98DB
	 'Image4':
		 Element: 0055
		 Part: 00B5
		 Identifier: 253C
	 'Image5':
		 Element: 0055
		 Part: 00B5
		 Identifier: B19D
	 'Image6':
		 Element: 0055
		 Part: 00B5
		 Identifier: 3DFE
	 'Image7':
		 Element: 0055
		 Part: 00B5
		 Identifier: CA5F
	 'Image8':
		 Element: 0055
		 Part: 00B5
		 Identifier: 56C0
Copy the code

KEYFORMAT block and RENDITION treeA copy of the key

As we’ll see shortly, the RENDITION tree stores pairs (copy keys, copy values). Copy keys are as follows:

<00000100 00000000 00000000 00000000 00000000 000006fe 5500b500 00000000 00000000>
Copy the code

Replica keys are lists of values that correspond to attributes in the KEYFORMAT block. To understand replica keys, we first need to look at the KEYFORMAT block.

Here is an example of the KEYFORMAT block in the demo project:

The structure of the KEYFORMAT block begins with the tag KFMT:

struct renditionkeyfmt {
    uint32_t tag;								// 'kfmt'
    uint32_t version;
    uint32_t maximumRenditionKeyTokenCount;
    uint32_t renditionKeyTokens[];
} __attribute__((packed));
Copy the code

This block can be parsed with the following code:

NSData *blockData = GetDataFromBomBlock(bomStorage, "KEYFORMAT");
if(blockData ! =nil) {
	struct renditionkeyfmt *keyFormat = (struct renditionkeyfmt *)[blockData bytes];
	
	fprintf(stderr, "\nKEYFORMAT:\n"
		"\t maximumRenditionKeyTokenCount: %u\n",
		keyFormat->maximumRenditionKeyTokenCount);
	
	for(uint32_t renditionKeyTokenIndex = 0 ; renditionKeyTokenIndex < keyFormat->maximumRenditionKeyTokenCount ; renditionKeyTokenIndex++) {
		NSString *attributeName = GetNameOfAttributeType(keyFormat->renditionKeyTokens[renditionKeyTokenIndex]);
		fprintf(stderr, "\t renditionKeyTokens: %s\n", [attributeName UTF8String]); [keyFormatStrings addObject:attributeName]; }}Copy the code

Running this code on the sample project, we get:

KEYFORMAT:
	 maximumRenditionKeyTokenCount: 18
	 renditionKeyTokens: Theme Appearance
	 renditionKeyTokens: Scale
	 renditionKeyTokens: Idiom
	 renditionKeyTokens: Subtype
	 renditionKeyTokens: Deployment Target
	 renditionKeyTokens: Graphics Feature Set Class
	 renditionKeyTokens: Memory Level Class
	 renditionKeyTokens: Display Gamut
	 renditionKeyTokens: Direction
	 renditionKeyTokens: Horizontal Size Class
	 renditionKeyTokens: Vertical Size Class
	 renditionKeyTokens: Identifier
	 renditionKeyTokens: Element
	 renditionKeyTokens: Part
	 renditionKeyTokens: State
	 renditionKeyTokens: Value
	 renditionKeyTokens: Dimension 1
	 renditionKeyTokens: Dimension 2
Copy the code

Now that we have the list of properties from the KEYFORMAT block, we can decode an example copy of the key from the RENDITION tree:

 Key '<00000100 00000000 00000000 00000000 00000000 000006fe 5500b500 00000000 00000000>'
	 Theme Appearance: 0000
	 Scale: 0001
	 Idiom: 0000
	 Subtype: 0000
	 Deployment Target: 0000
	 Graphics Feature Set Class: 0000
	 Memory Level Class: 0000
	 Display Gamut: 0000
	 Direction: 0000
	 Horizontal Size Class: 0000
	 Vertical Size Class: 0000
	 Identifier: FE06
	 Element: 0055
	 Part: 00B5
	 State: 0000
	 Value: 0000
	 Dimension 1: 0000
	 Dimension 2: 0000
Copy the code

As we can see, this copy key corresponds to a dimension with the identifier FE06 and a scale of @1x. Using the FACETKEYS tree, we can see the MyPDF footage corresponding to this copy key.

RENDITION tree

The RENDITION tree is a complex structure that contains material data. The key is the copy of the key that we have analyzed, and the value is the raw data of some header prefixes.

Because the structure is complex, let’s start by looking at the text files in the sample project. Here is a copy of the text.txt file containing the blog.timac.org content:

The duplicate key consists of three parts:

  • csiheaderHeaders, suitable for all types of renditions. The fixed length of the header is 184 bytes.
  • TLV (type-length-value) list, whose length is incsiheaderSpecified in the header. It contains information about the inability to accommodate incsiheaderExtension information in the header, such as UTI in the story.
  • Replica data begins with a header specific to the replica type, followed by the data for the story. Depending on the copy type, the data can be compressed or uncompressed.

Here’s how to use Synalyze It! Pro made screen shots for visualization of these three parts:

  • csiheaderThe head is blue.
  • The TLV list is green.
  • The copy data is orange.

csiheader

As mentioned earlier, the value of the copy begins with a fixed-length header (184 bytes) that contains various information about the material:

struct csiheader {
    uint32_t tag;								// 'CTSI'
    uint32_t version;
    struct renditionFlags renditionFlags;
    uint32_t width;
    uint32_t height;
    uint32_t scaleFactor;
    uint32_t pixelFormat;
	struct {
		uint32_t colorSpaceID:4;
		uint32_t reserved:28;
    } colorSpace;
    struct csimetadata csimetadata;
    struct csibitmaplist csibitmaplist;
} __attribute__((packed));
Copy the code
  • theThe labelIs set toCTSIIt seems to beCore Theme Structured ImageIs an acronym for.
  • versionThe value is always 1.
  • renditionFlagsIs a 32-bit integer whose bits represent some properties of the copy:
struct renditionFlags { 
  uint32_t isHeaderFlaggedFPO:1; 
  uint32_t isExcludedFromContrastFilter:1; 
  uint32_t isVectorBased:1; 
  uint32_t isOpaque:1; 
  uint32_t bitmapEncoding:4; 
  uint32_t optOutOfThinning:1; 
  uint32_t isFlippable:1; 
  uint32_t isTintable:1; 
  uint32_t preservedVectorRepresentation:1; 
  uint32_t reserved:20; 
} attribute((packed));
Copy the code

Width and height describe the size of the image in pixels. If the material has no width or height, the value is 0.

ScaleFactor is a scale value multiplied by 100. For example, an @2x image has a scaleFactor value of 200.

PixelFormat can contain multiple values, depending on the type of copy material, such as: ARGB, GA8, RGB5, RGBW, GA16, JPEG, HEIF, DATA…

ColorSpaceID identifies which color space should be used. Starting with MacOS Mojave and iOS 12, 6 different color Spaces are supported:

NSString *GetColorSpaceNameWithID(int64_t inColorSpaceID) { 
  switch (inColorSpaceID) { 
    case 0: 
    default: { 
      return @"SRGB";
    } 
      break;
    case 1: {
      return @"GrayGamma2_2";
    }
      break;
    case 2: {
        return @"DisplayP3";
    }
      break;
    case 3: {
      return @"ExtendedRangeSRGB";
    }
      break;
    case 4: {
      return @"ExtendedLinearSRGB";
    }
      break;
    case 5: {
      return @"ExtendedGray";
    }
      break; }}Copy the code
  • csimetadataThe structure contains some important information about the story: the name, the layout, and when it was modified.
struct csimetadata { 
  uint32_t modtime; 
  uint16_t layout; 
  uint16_t zero; 
  char name[128]; 
} __attribute__((packed)); 
Copy the code

The Layout field is particularly interesting because it identifies the type of data being stored: image, data, texture, color… For images, subtypes are stored in the layout:

enum RenditionLayoutType { 
    kRenditionLayoutType_TextEffect = 0x007,
    kRenditionLayoutType_Vector = 0x009,
    kRenditionLayoutType_Data = 0x3E8,
    kRenditionLayoutType_ExternalLink = 0x3E9,
    kRenditionLayoutType_LayerStack = 0x3EA,
    kRenditionLayoutType_InternalReference = 0x3EB,
    kRenditionLayoutType_PackedImage = 0x3EC,
    kRenditionLayoutType_NameList = 0x3ED,
    kRenditionLayoutType_UnknownAddObject = 0x3EE,
    kRenditionLayoutType_Texture = 0x3EF,
    kRenditionLayoutType_TextureImage = 0x3F0,
    kRenditionLayoutType_Color = 0x3F1,
    kRenditionLayoutType_MultisizeImage = 0x3F2,
    kRenditionLayoutType_LayerReference = 0x3F4,
    kRenditionLayoutType_ContentRendition = 0x3F5,
    kRenditionLayoutType_RecognitionObject = 0x3F6};enum CoreThemeImageSubtype {
    kCoreThemeOnePartFixedSize = 10, 
    kCoreThemeOnePartTile = 11, 
    kCoreThemeOnePartScale = 12, 
    kCoreThemeThreePartHTile = 20, 
    kCoreThemeThreePartHScale = 21, 
    kCoreThemeThreePartHUniform = 22, 
    kCoreThemeThreePartVTile = 23, 
    kCoreThemeThreePartVScale = 24, 
    kCoreThemeThreePartVUniform = 25, 
    kCoreThemeNinePartTile = 30, 
    kCoreThemeNinePartScale = 31, 
    kCoreThemeNinePartHorizontalUniformVerticalScale = 32, 
    kCoreThemeNinePartHorizontalScaleVerticalUniform = 33, 
    kCoreThemeNinePartEdgesOnly = 34, 
    kCoreThemeManyPartLayoutUnknown = 40, 
    kCoreThemeAnimationFilmstrip = 50 
}; 
Copy the code
  • In the end,csibitmaplistContains the size of the replica data (renditionLength). This is followed by a list of TLVS (type length values) whose length is written intvlLengthIn the field:
struct csibitmaplist { 
  uint32_t tvlLength; // Length of all the TLV following the csiheader 
  uint32_t unknown; 
  uint32_t zero; 
  uint32_t renditionLength; 
} __attribute__((packed));
Copy the code

Using the above structure, we can use Synalyze It! Create custom syntax in Pro to quickly understand the structure:

TVL

Immediately after the cSIBitMaplist at the end of the CSIheader, there is a list of TLVS (type length values). For text files, the TLV data is:

<EC030000 08000000 00000000 0000803F EE030000 04000000 01000000>
Copy the code

Here is a list of possible tags:

enum RenditionTLVType {
	kRenditionTLVType_Slices 				= 0x3E9,
	kRenditionTLVType_Metrics 				= 0x3EB,
	kRenditionTLVType_BlendModeAndOpacity	= 0x3EC,
	kRenditionTLVType_UTI	 				= 0x3ED,
	kRenditionTLVType_EXIFOrientation		= 0x3EE,
	kRenditionTLVType_ExternalTags			= 0x3F0,
	kRenditionTLVType_Frame					= 0x3F1};Copy the code

The following code can be used to dump TLV:

// Print the TLV
uint32_t tvlLength = csiHeader->csibitmaplist.tvlLength;
if(tvlLength > 0) {
	fprintf(stderr."\t\t\t tlv:\n");
	
	const void *tlvBytes = valueBytes + sizeof(*csiHeader);
	const void *tlvPos = tlvBytes;
	
	while(tlvBytes + tvlLength > tlvPos) {
		uint32_t tlvTag = *(uint32_t *)tlvPos;
		uint32_t tlvLength = *(uint32_t *)(tlvPos + 4);
		
		fprintf(stderr."\t\t\t\t %s: " , [GetTLVTNameWithType(tlvTag) UTF8String]);
		for(uint32_t valuePos = 0 ; valuePos < tlvLength ; valuePos++) {
			fprintf(stderr."%02X", * (uint8_t*)(tlvPos + 8 + valuePos));
		}
		
		fprintf(stderr."\n");
		
		tlvPos += 8+ tlvLength; }}Copy the code

Running this code on a text file yields the following information:

tlv:
	BlendModeAndOpacity: 000000000000803F
	EXIFOrientation: 01000000
Copy the code

For PDF materials, we clearly see com.adobe.pdf UTI:

tlv:
	BlendModeAndOpacity: 000000000000803F
	UTI: 0E00000000000000636F6D2E61646F62652E70646600
	EXIFOrientation: 01000000
Copy the code

Different types of copies

After these complex structures, you can see the replica data. It contains a header for a specific replica type, followed by compressed or uncompressed actual data. The length is set in the renditionLength field of the CSIBitMaplist structure.

For text files, the copy data contains a simple title followed by the string blog.timac.org:

<44574152 00000000 0E000000 626C6F67 2E74696D 61632E6F 7267>
Copy the code

However, duplicate data is not always simple. In fact, as of macOS Mojave, there are 21 types of replicas:

  • CUIRawDataRendition
  • CUIRawPixelRendition
  • CUIThemeColorRendition
  • CUIThemePixelRendition
  • CUIPDFRendition
  • CUIThemeModelMeshRendition
  • CUIMutableThemeRendition
  • CUIThemeEffectRendition
  • CUIThemeMultisizeImageSetRendition
  • CUIThemeGradientRendition
  • CUIExternalLinkRendition
  • CUIThinningPlaceholderRendition
  • CUIThemeTextureRendition
  • CUIThemeTextureImageRendition
  • CUIInternalLinkRendition
  • CUINameContentRendition
  • CUIThemeSchemaRendition
  • CUIThemeSchemaEffectRendition
  • CUIThemeModelAssetRendition

And 2 subtypes of CUIRawDataRendition:

  • CUILayerStackRendition
  • CUIRecognitionObjectRendition

The pixelFormat and Layout fields of the CSIHeader are used to know which replica type to use. In this article, I will describe only the four most common replica types:

  • CUIRawDataRendition: pixelFormat is set toDATA, the layout is set to kRenditionLayoutType_Data
  • CUIRawPixelRendition: pixelFormat forJPEGHEIF. The layout is set to a subtype of the image.
  • CUIThemeColorRendition: pixelFormat is 0, layout is kRenditionLayoutType_Color
  • CUIThemePixelRendition: pixelFormat is set toARGB,GA8,RGB5,RGBWGA16, and the layout is set to the image subtype.

CUIRawDataRendition

Let’s start with the cuirawat Arendition copy type used by the text file. As we can see, the structure is simple:

struct CUIRawDataRendition {
    uint32_t tag;					// RAWD
    uint32_t version;
    uint32_t rawDataLength;
	uint8_t rawData[];
} __attribute__((packed));
Copy the code

Here is the code that parses CUIRawDataRendition to recover the original data:

if(csiHeader->pixelFormat == 'DATA') {
	struct CUIRawDataRendition *rawDataRendition = (struct CUIRawDataRendition *)renditionBytes;
	if(rawDataRendition->tag == 'RAWD') {
		uint32_t rawDataLength = rawDataRendition->rawDataLength;
		uint8_t *rawData = rawDataRendition->rawData;
		if(rawDataLength > 4) {
			fprintf(stderr."\t\t\t Found RawDataRendition with size %u: 0x%02X%02X%02X%02X... \n", rawDataLength, *(uint8_t*)rawData, *(uint8_t*)(rawData + 1), * (uint8_t*)(rawData + 2), * (uint8_t*)(rawData + 3));
		}
		else {
			fprintf(stderr."\t\t\t Found RawDataRendition with size %u\n", rawDataLength); }}}Copy the code

CUIRawPixelRendition

The CUIRawPixelRendition structure for storing JPeGs and heIfs is the same as the CUIRawDataRendition structure:

struct CUIRawPixelRendition {
    uint32_t tag;					// RAWD
    uint32_t version;
    uint32_t rawDataLength;
	uint8_t rawData[];
} __attribute__((packed));
Copy the code

The code for restoring the image is also simple:

else if(csiHeader->pixelFormat == 'JPEG' || csiHeader->pixelFormat == 'HEIF') {
	struct CUIRawPixelRendition *rawPixelRendition = (struct CUIRawPixelRendition *)renditionBytes;
	if(rawPixelRendition->tag == 'RAWD') {
		uint32_t rawDataLength = rawPixelRendition->rawDataLength;
		uint8_t *rawDataBytes = rawPixelRendition->rawData;
		
		NSData *rawData = [[NSData alloc] initWithBytes:rawDataBytes length:rawDataLength];
		CGImageSourceRef sourceRef = CGImageSourceCreateWithData((CFDataRef)rawData, NULL);
		CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0.NULL);
		fprintf(stderr, "\t\t\t Found RawPixelRendition of size (%ld x %ld) with rawDataLength %u\n".CGImageGetWidth(imageRef), CGImageGetHeight(imageRef), rawDataLength);
		CFRelease(imageRef);
		CFRelease(sourceRef); }}Copy the code

By running this code in Xcode, we can use the QuickLook plugin to view the restored image:

CUIThemeColorRendition

CUIThemeColorRendition copy is used to store named color values and contains:

  • The amount of color composition

    Color Components is a kind of meta information that exists mainly in image files.

  • The value of color composition

  • Color space

struct csicolor {
	uint32_t tag;					// COLR
	uint32_t version;
	struct {
		uint32_t colorSpaceID:8;
		uint32_t unknown0:3;
		uint32_t reserved:21;
    } colorSpace;
	uint32_t numberOfComponents;
	double components[];
} __attribute__((packed));
Copy the code

CGColorRef can be accessed with the following code:

else if(csiHeader->pixelFormat == 0 && csiHeader->csimetadata.layout == kRenditionLayoutType_Color) {
	struct csicolor *colorRendition = (struct csicolor *)renditionBytes;
	
	if(colorRendition->numberOfComponents == 4) {
		// Use the hardcoded DeviceRGB color space instead of the real colorSpace from the colorSpaceID
		CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(a);CGColorRef __unused theColor = CGColorCreate(colorSpaceRef, colorRendition->components);
		CFRelease(theColor);
		CFRelease(colorSpaceRef);
		
		NSString *colorString = [NSString stringWithFormat:@"%f,%f,%f,%f", colorRendition->components[0], colorRendition->components[1], colorRendition->components[2], colorRendition->components[3]];
		fprintf(stderr, "\n\t\t Found Color %s with colorspace ID %d\n", [colorString UTF8String], colorRendition->colorSpace.colorSpaceID & 0xFF);
	}
  else {
		fprintf(stderr, "\n\t\t Found Color with colorspace ID %d but with %u components\n", colorRendition->colorSpace.colorSpaceID & 0xFF, colorRendition->numberOfComponents); }}Copy the code

The restored CGColorRef can be viewed using the QuickLook plugin by running this code in Xcode:

CUIThemePixelRendition

Not all structures are so simple; the CUIThemePixelRendition for PNG images, for example, is a bit more complicated. Like other types of replicas, CUIThemePixelRendition has a specific header:

struct CUIThemePixelRendition {
    uint32_t tag;					// 'CELM'
    uint32_t version;
    uint32_t compressionType;
    uint32_t rawDataLength;
	uint8_t rawData[];
} __attribute__((packed));
Copy the code
  • The value of tag is set to CELM

  • Version is always set to 0

  • CompressionType is set to one of the following options:

    enum RenditionCompressionType { 
        kRenditionCompressionType_uncompressed = 0,
        kRenditionCompressionType_rle, 
        kRenditionCompressionType_zip, 
        kRenditionCompressionType_lzvn, 
        kRenditionCompressionType_lzfse, 
        kRenditionCompressionType_jpeg_lzfse, 
        kRenditionCompressionType_blurred, 
        kRenditionCompressionType_astc, 
        kRenditionCompressionType_palette_img, 
        kRenditionCompressionType_deepmap_lzfse, 
    }; 
    Copy the code

When compression is used, the original data is compressed and then needs to be decoded using the corresponding algorithm. The exact decompression algorithm used is beyond the scope of this article.

  • RawDataLength Contains the size of rawData.

  • Finally, rawData contains real data — uncompressed or compressed. If the data is compressed, you need to decompress it using the algorithm specified in the compressionType field.

Below is a copy in PNG format at Synalyze It! Presentation in Pro. Note: Red is the compressed original data:

conclusion

The.car file can store many different types of assets, which makes this file format quite complex. In this article, I describe the most important structures and how to dump them. Similar methods can be applied to analyze and understand other structures.

The full source code for the CARParser application can be downloaded here. You can easily modify this tool to produce the same output as Assetutil-i Assets.car.

Here’s what you’ll see when you run CARParser on the demo project:

CARHEADER:
	 coreuiVersion: 498
	 storageVersion: 15
	 storageTimestamp: 1539543253 (2018-10-14T18:54:13Z)
	 renditionCount: 7
	 mainVersionString: @(#)PROGRAM:CoreUI  PROJECT:CoreUI-498.40.1
	 versionString: IBCocoaTouchImageCatalogTool-10.0
	 uuid: 9EA56D07-3242-4F88-8BC1-C16C25EA65F2
	 associatedChecksum: 0x79965D18
	 schemaVersion: 2
	 colorSpaceID: 1
	 keySemantics: 2

EXTENDED_METADATA:
	 thinningArguments: 
	 deploymentPlatformVersion: 12.0
	 deploymentPlatform: ios
	 authoringTool: @(#)PROGRAM:CoreThemeDefinition  PROJECT:CoreThemeDefinition-346.29

KEYFORMAT:
	 maximumRenditionKeyTokenCount: 18
	 renditionKeyTokens: Theme Appearance
	 renditionKeyTokens: Scale
	 renditionKeyTokens: Idiom
	 renditionKeyTokens: Subtype
	 renditionKeyTokens: Deployment Target
	 renditionKeyTokens: Graphics Feature Set Class
	 renditionKeyTokens: Memory Level Class
	 renditionKeyTokens: Display Gamut
	 renditionKeyTokens: Direction
	 renditionKeyTokens: Horizontal Size Class
	 renditionKeyTokens: Vertical Size Class
	 renditionKeyTokens: Identifier
	 renditionKeyTokens: Element
	 renditionKeyTokens: Part
	 renditionKeyTokens: State
	 renditionKeyTokens: Value
	 renditionKeyTokens: Dimension 1
	 renditionKeyTokens: Dimension 2

Tree APPEARANCEKEYS

Tree FACETKEYS
	 'MyColor':
		 Element: 0055
		 Part: 00D9
		 Identifier: AF9F
	 'MyJPG':
		 Element: 0055
		 Part: 00B5
		 Identifier: BCAD
	 'MyPDF':
		 Element: 0055
		 Part: 00B5
		 Identifier: FE06
	 'MyPNG':
		 Element: 0055
		 Part: 00B5
		 Identifier: 7F71
	 'MyText':
		 Element: 0055
		 Part: 00B5
		 Identifier: 9236

Tree RENDITIONS

	 Key '<00000100 00000000 00000000 00000000 00000000 000006fe 5500b500 00000000 00000000>'
		 Theme Appearance: 0000
		 Scale: 0001
		 Idiom: 0000
		 Subtype: 0000
		 Deployment Target: 0000
		 Graphics Feature Set Class: 0000
		 Memory Level Class: 0000
		 Display Gamut: 0000
		 Direction: 0000
		 Horizontal Size Class: 0000
		 Vertical Size Class: 0000
		 Identifier: FE06
		 Element: 0055
		 Part: 00B5
		 State: 0000
		 Value: 0000
		 Dimension 1: 0000
		 Dimension 2: 0000

		 csiHeader:
			 version: 1
			 renditionFlags: bitmapEncoding 0
			 width: 0
			 height: 0
			 scaleFactor: 100 (@1x)
			 pixelFormat: 'DATA' (0x44415441)
			 colorSpaceID: 1
			 modtime: 0
			 layout: Data
			 name: CoreStructuredImage
			 tvlLength: 58
			 renditionLength: 7296
			 tlv:
				 BlendModeAndOpacity: 000000000000803F
				 UTI: 0E00000000000000636F6D2E61646F62652E70646600
				 EXIFOrientation: 01000000
			 Found RawDataRendition with size 7284: 0x25504446...

	 Key '<00000100 00000000 00000000 00000000 00000000 00003692 5500b500 00000000 00000000>'
		 Theme Appearance: 0000
		 Scale: 0001
		 Idiom: 0000
		 Subtype: 0000
		 Deployment Target: 0000
		 Graphics Feature Set Class: 0000
		 Memory Level Class: 0000
		 Display Gamut: 0000
		 Direction: 0000
		 Horizontal Size Class: 0000
		 Vertical Size Class: 0000
		 Identifier: 9236
		 Element: 0055
		 Part: 00B5
		 State: 0000
		 Value: 0000
		 Dimension 1: 0000
		 Dimension 2: 0000

		 csiHeader:
			 version: 1
			 renditionFlags: bitmapEncoding 0
			 width: 0
			 height: 0
			 scaleFactor: 100 (@1x)
			 pixelFormat: 'DATA' (0x44415441)
			 colorSpaceID: 14
			 modtime: 0
			 layout: Data
			 name: text.txt
			 tvlLength: 28
			 renditionLength: 26
			 tlv:
				 BlendModeAndOpacity: 000000000000803F
				 EXIFOrientation: 01000000
			 Found RawDataRendition with size 14: 0x626C6F67...

	 Key '<00000100 00000000 00000000 00000000 00000000 0000717f 5500b500 00000000 00000000>'
		 Theme Appearance: 0000
		 Scale: 0001
		 Idiom: 0000
		 Subtype: 0000
		 Deployment Target: 0000
		 Graphics Feature Set Class: 0000
		 Memory Level Class: 0000
		 Display Gamut: 0000
		 Direction: 0000
		 Horizontal Size Class: 0000
		 Vertical Size Class: 0000
		 Identifier: 7F71
		 Element: 0055
		 Part: 00B5
		 State: 0000
		 Value: 0000
		 Dimension 1: 0000
		 Dimension 2: 0000

		 csiHeader:
			 version: 1
			 renditionFlags: bitmapEncoding 1
			 width: 28
			 height: 28
			 scaleFactor: 100 (@1x)
			 pixelFormat: 'ARGB' (0x41524742)
			 colorSpaceID: 1
			 modtime: 0
			 layout: Image (One Part Scaled)
			 name: Timac.png
			 tvlLength: 104
			 renditionLength: 719
			 tlv:
				 Slices: 0100000000000000000000001C0000001C000000
				 Metrics: 01000000000000000000000000000000000000001C0000001C000000
				 BlendModeAndOpacity: 000000000000803F
				 EXIFOrientation: 01000000
				 Unknown 0x03EF: 80000000

		 Found ThemePixelRendition with size 703 and compression palette-img

	 Key '<00000100 00000000 00000000 00000000 00000000 00009faf 5500d900 00000000 00000000>'
		 Theme Appearance: 0000
		 Scale: 0001
		 Idiom: 0000
		 Subtype: 0000
		 Deployment Target: 0000
		 Graphics Feature Set Class: 0000
		 Memory Level Class: 0000
		 Display Gamut: 0000
		 Direction: 0000
		 Horizontal Size Class: 0000
		 Vertical Size Class: 0000
		 Identifier: AF9F
		 Element: 0055
		 Part: 00D9
		 State: 0000
		 Value: 0000
		 Dimension 1: 0000
		 Dimension 2: 0000

		 csiHeader:
			 version: 1
			 renditionFlags: bitmapEncoding 0
			 width: 0
			 height: 0
			 scaleFactor: 0
			 pixelFormat: 0x0000
			 colorSpaceID: 1
			 modtime: 0
			 layout: Color
			 name: MyColor
			 tvlLength: 28
			 renditionLength: 48
			 tlv:
				 BlendModeAndOpacity: 0000000000000000
				 EXIFOrientation: 01000000

		 Found Color 1.000000,0.000000,0.000000,0.500000 with colorspace ID 1

	 Key '<00000100 00000000 00000000 00000000 00000000 0000adbc 5500b500 00000000 00000000>'
		 Theme Appearance: 0000
		 Scale: 0001
		 Idiom: 0000
		 Subtype: 0000
		 Deployment Target: 0000
		 Graphics Feature Set Class: 0000
		 Memory Level Class: 0000
		 Display Gamut: 0000
		 Direction: 0000
		 Horizontal Size Class: 0000
		 Vertical Size Class: 0000
		 Identifier: BCAD
		 Element: 0055
		 Part: 00B5
		 State: 0000
		 Value: 0000
		 Dimension 1: 0000
		 Dimension 2: 0000

		 csiHeader:
			 version: 1
			 renditionFlags: bitmapEncoding 1
			 width: 0
			 height: 0
			 scaleFactor: 100 (@1x)
			 pixelFormat: 'JPEG' (0x4A504547)
			 colorSpaceID: 14
			 modtime: 0
			 layout: Image (One Part Scaled)
			 name: TimacJPG.jpg
			 tvlLength: 92
			 renditionLength: 7766
			 tlv:
				 Slices: 010000000000000000000000C8000000C8000000
				 Metrics: 0100000000000000000000000000000000000000C8000000C8000000
				 BlendModeAndOpacity: 000000000000803F
				 EXIFOrientation: 01000000
			 Found RawPixelRendition of size (200 x 200) with rawDataLength 7754

	 Key '<00000200 00000000 00000000 00000000 00000000 0000717f 5500b500 00000000 00000000>'
		 Theme Appearance: 0000
		 Scale: 0002
		 Idiom: 0000
		 Subtype: 0000
		 Deployment Target: 0000
		 Graphics Feature Set Class: 0000
		 Memory Level Class: 0000
		 Display Gamut: 0000
		 Direction: 0000
		 Horizontal Size Class: 0000
		 Vertical Size Class: 0000
		 Identifier: 7F71
		 Element: 0055
		 Part: 00B5
		 State: 0000
		 Value: 0000
		 Dimension 1: 0000
		 Dimension 2: 0000

		 csiHeader:
			 version: 1
			 renditionFlags: bitmapEncoding 1
			 width: 56
			 height: 56
			 scaleFactor: 200 (@2x)
			 pixelFormat: 'ARGB' (0x41524742)
			 colorSpaceID: 1
			 modtime: 0
			 layout: Image (One Part Scaled)
			 name: [email protected]
			 tvlLength: 104
			 renditionLength: 814
			 tlv:
				 Slices: 0100000000000000000000003800000038000000
				 Metrics: 01000000000000000000000000000000000000003800000038000000
				 BlendModeAndOpacity: 000000000000803F
				 EXIFOrientation: 01000000
				 Unknown 0x03EF: E0000000

		 Found ThemePixelRendition with size 798 and compression palette-img

	 Key '<00000300 00000000 00000000 00000000 00000000 0000717f 5500b500 00000000 00000000>'
		 Theme Appearance: 0000
		 Scale: 0003
		 Idiom: 0000
		 Subtype: 0000
		 Deployment Target: 0000
		 Graphics Feature Set Class: 0000
		 Memory Level Class: 0000
		 Display Gamut: 0000
		 Direction: 0000
		 Horizontal Size Class: 0000
		 Vertical Size Class: 0000
		 Identifier: 7F71
		 Element: 0055
		 Part: 00B5
		 State: 0000
		 Value: 0000
		 Dimension 1: 0000
		 Dimension 2: 0000

		 csiHeader:
			 version: 1
			 renditionFlags: bitmapEncoding 1
			 width: 84
			 height: 84
			 scaleFactor: 300 (@3x)
			 pixelFormat: 'ARGB' (0x41524742)
			 colorSpaceID: 1
			 modtime: 0
			 layout: Image (One Part Scaled)
			 name: [email protected]
			 tvlLength: 104
			 renditionLength: 1673
			 tlv:
				 Slices: 0100000000000000000000005400000054000000
				 Metrics: 01000000000000000000000000000000000000005400000054000000
				 BlendModeAndOpacity: 000000000000803F
				 EXIFOrientation: 01000000
				 Unknown 0x03EF: 60010000

		 Found ThemePixelRendition with size 1657 and compression palette-img

Tree 'COLORS'

Tree 'FONTS'

Tree 'FONTSIZES'

Tree 'GLYPHS'

Tree 'BEZELS'

Tree 'BITMAPKEYS'
	 Key '' -> <01000000 00000000 4c000000 12000000 ffffffff 0e000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 ffffffff ffffffff ffffffff 01000000 ffffffff 01000000 01000000>
	 Key '' -> <01000000 00000000 4c000000 12000000 ffffffff 02000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 ffffffff ffffffff ffffffff 01000000 ffffffff 01000000 01000000>
	 Key '' -> <01000000 00000000 4c000000 12000000 ffffffff 02000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 ffffffff ffffffff ffffffff 01000000 ffffffff 01000000 01000000>
	 Key '' -> <01000000 00000000 4c000000 12000000 ffffffff 02000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 ffffffff ffffffff ffffffff 01000000 ffffffff 01000000 01000000>
	 Key '' -> <01000000 00000000 4c000000 12000000 ffffffff 02000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 ffffffff ffffffff ffffffff 01000000 ffffffff 01000000 01000000>

Tree 'ELEMENT_INFO'

Tree 'PART_INFO'
Copy the code

You can find my QuickLook plugin for visualized.car files in this article: QuickLook plugin for visualized.car files (compiled Asset Catalogs)

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.