├── .gitignore ├── CARParser.xcodeproj └── project.pbxproj ├── CARParser ├── Bom.h ├── Car.h └── main.m ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots/**/*.png 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /CARParser.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 02F41A6D1E7F0D11001F152D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 02F41A6C1E7F0D11001F152D /* main.m */; }; 11 | 02F41A761E7F0D42001F152D /* bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02F41A751E7F0D42001F152D /* bom.framework */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 02F41A671E7F0D11001F152D /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 02F41A691E7F0D11001F152D /* CARParser */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CARParser; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 02F41A6C1E7F0D11001F152D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | 02F41A731E7F0D33001F152D /* Bom.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Bom.h; sourceTree = ""; }; 30 | 02F41A751E7F0D42001F152D /* bom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = bom.framework; path = System/Library/PrivateFrameworks/bom.framework; sourceTree = SDKROOT; }; 31 | FAA4E987216B39F300E22E75 /* Car.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Car.h; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | 02F41A661E7F0D11001F152D /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | 02F41A761E7F0D42001F152D /* bom.framework in Frameworks */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 02F41A601E7F0D11001F152D = { 47 | isa = PBXGroup; 48 | children = ( 49 | 02F41A6B1E7F0D11001F152D /* CARParser */, 50 | 02F41A6A1E7F0D11001F152D /* Products */, 51 | 02F41A741E7F0D42001F152D /* Frameworks */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 02F41A6A1E7F0D11001F152D /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 02F41A691E7F0D11001F152D /* CARParser */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 02F41A6B1E7F0D11001F152D /* CARParser */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 02F41A731E7F0D33001F152D /* Bom.h */, 67 | FAA4E987216B39F300E22E75 /* Car.h */, 68 | 02F41A6C1E7F0D11001F152D /* main.m */, 69 | ); 70 | path = CARParser; 71 | sourceTree = ""; 72 | }; 73 | 02F41A741E7F0D42001F152D /* Frameworks */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 02F41A751E7F0D42001F152D /* bom.framework */, 77 | ); 78 | name = Frameworks; 79 | sourceTree = ""; 80 | }; 81 | /* End PBXGroup section */ 82 | 83 | /* Begin PBXNativeTarget section */ 84 | 02F41A681E7F0D11001F152D /* CARParser */ = { 85 | isa = PBXNativeTarget; 86 | buildConfigurationList = 02F41A701E7F0D11001F152D /* Build configuration list for PBXNativeTarget "CARParser" */; 87 | buildPhases = ( 88 | 02F41A651E7F0D11001F152D /* Sources */, 89 | 02F41A661E7F0D11001F152D /* Frameworks */, 90 | 02F41A671E7F0D11001F152D /* CopyFiles */, 91 | ); 92 | buildRules = ( 93 | ); 94 | dependencies = ( 95 | ); 96 | name = CARParser; 97 | productName = CARParser; 98 | productReference = 02F41A691E7F0D11001F152D /* CARParser */; 99 | productType = "com.apple.product-type.tool"; 100 | }; 101 | /* End PBXNativeTarget section */ 102 | 103 | /* Begin PBXProject section */ 104 | 02F41A611E7F0D11001F152D /* Project object */ = { 105 | isa = PBXProject; 106 | attributes = { 107 | LastUpgradeCheck = 1000; 108 | ORGANIZATIONNAME = "Alexandre Colucci"; 109 | TargetAttributes = { 110 | 02F41A681E7F0D11001F152D = { 111 | CreatedOnToolsVersion = 8.3; 112 | }; 113 | }; 114 | }; 115 | buildConfigurationList = 02F41A641E7F0D11001F152D /* Build configuration list for PBXProject "CARParser" */; 116 | compatibilityVersion = "Xcode 3.2"; 117 | developmentRegion = English; 118 | hasScannedForEncodings = 0; 119 | knownRegions = ( 120 | en, 121 | ); 122 | mainGroup = 02F41A601E7F0D11001F152D; 123 | productRefGroup = 02F41A6A1E7F0D11001F152D /* Products */; 124 | projectDirPath = ""; 125 | projectRoot = ""; 126 | targets = ( 127 | 02F41A681E7F0D11001F152D /* CARParser */, 128 | ); 129 | }; 130 | /* End PBXProject section */ 131 | 132 | /* Begin PBXSourcesBuildPhase section */ 133 | 02F41A651E7F0D11001F152D /* Sources */ = { 134 | isa = PBXSourcesBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | 02F41A6D1E7F0D11001F152D /* main.m in Sources */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | /* End PBXSourcesBuildPhase section */ 142 | 143 | /* Begin XCBuildConfiguration section */ 144 | 02F41A6E1E7F0D11001F152D /* Debug */ = { 145 | isa = XCBuildConfiguration; 146 | buildSettings = { 147 | ALWAYS_SEARCH_USER_PATHS = NO; 148 | CLANG_ANALYZER_NONNULL = YES; 149 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 150 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 151 | CLANG_CXX_LIBRARY = "libc++"; 152 | CLANG_ENABLE_MODULES = YES; 153 | CLANG_ENABLE_OBJC_ARC = YES; 154 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 155 | CLANG_WARN_BOOL_CONVERSION = YES; 156 | CLANG_WARN_COMMA = YES; 157 | CLANG_WARN_CONSTANT_CONVERSION = YES; 158 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 159 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 160 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 161 | CLANG_WARN_EMPTY_BODY = YES; 162 | CLANG_WARN_ENUM_CONVERSION = YES; 163 | CLANG_WARN_INFINITE_RECURSION = YES; 164 | CLANG_WARN_INT_CONVERSION = YES; 165 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 166 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 167 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 168 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 169 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 170 | CLANG_WARN_STRICT_PROTOTYPES = YES; 171 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 172 | CLANG_WARN_UNREACHABLE_CODE = YES; 173 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 174 | CODE_SIGN_IDENTITY = "-"; 175 | COPY_PHASE_STRIP = NO; 176 | DEBUG_INFORMATION_FORMAT = dwarf; 177 | ENABLE_STRICT_OBJC_MSGSEND = YES; 178 | ENABLE_TESTABILITY = YES; 179 | FRAMEWORK_SEARCH_PATHS = ( 180 | "$(inherited)", 181 | "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", 182 | ); 183 | GCC_C_LANGUAGE_STANDARD = gnu99; 184 | GCC_DYNAMIC_NO_PIC = NO; 185 | GCC_NO_COMMON_BLOCKS = YES; 186 | GCC_OPTIMIZATION_LEVEL = 0; 187 | GCC_PREPROCESSOR_DEFINITIONS = ( 188 | "DEBUG=1", 189 | "$(inherited)", 190 | ); 191 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 192 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 193 | GCC_WARN_UNDECLARED_SELECTOR = YES; 194 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 195 | GCC_WARN_UNUSED_FUNCTION = YES; 196 | GCC_WARN_UNUSED_VARIABLE = YES; 197 | MACOSX_DEPLOYMENT_TARGET = 10.14; 198 | MTL_ENABLE_DEBUG_INFO = YES; 199 | ONLY_ACTIVE_ARCH = YES; 200 | SDKROOT = macosx; 201 | }; 202 | name = Debug; 203 | }; 204 | 02F41A6F1E7F0D11001F152D /* Release */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | CLANG_ANALYZER_NONNULL = YES; 209 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 210 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 211 | CLANG_CXX_LIBRARY = "libc++"; 212 | CLANG_ENABLE_MODULES = YES; 213 | CLANG_ENABLE_OBJC_ARC = YES; 214 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 215 | CLANG_WARN_BOOL_CONVERSION = YES; 216 | CLANG_WARN_COMMA = YES; 217 | CLANG_WARN_CONSTANT_CONVERSION = YES; 218 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 219 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 220 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 221 | CLANG_WARN_EMPTY_BODY = YES; 222 | CLANG_WARN_ENUM_CONVERSION = YES; 223 | CLANG_WARN_INFINITE_RECURSION = YES; 224 | CLANG_WARN_INT_CONVERSION = YES; 225 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 226 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 227 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 229 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 230 | CLANG_WARN_STRICT_PROTOTYPES = YES; 231 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 232 | CLANG_WARN_UNREACHABLE_CODE = YES; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | CODE_SIGN_IDENTITY = "-"; 235 | COPY_PHASE_STRIP = NO; 236 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 237 | ENABLE_NS_ASSERTIONS = NO; 238 | ENABLE_STRICT_OBJC_MSGSEND = YES; 239 | FRAMEWORK_SEARCH_PATHS = ( 240 | "$(inherited)", 241 | "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", 242 | ); 243 | GCC_C_LANGUAGE_STANDARD = gnu99; 244 | GCC_NO_COMMON_BLOCKS = YES; 245 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 246 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 247 | GCC_WARN_UNDECLARED_SELECTOR = YES; 248 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 249 | GCC_WARN_UNUSED_FUNCTION = YES; 250 | GCC_WARN_UNUSED_VARIABLE = YES; 251 | MACOSX_DEPLOYMENT_TARGET = 10.14; 252 | MTL_ENABLE_DEBUG_INFO = NO; 253 | SDKROOT = macosx; 254 | }; 255 | name = Release; 256 | }; 257 | 02F41A711E7F0D11001F152D /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | DEVELOPMENT_TEAM = ""; 261 | PRODUCT_NAME = "$(TARGET_NAME)"; 262 | PROVISIONING_PROFILE_SPECIFIER = ""; 263 | }; 264 | name = Debug; 265 | }; 266 | 02F41A721E7F0D11001F152D /* Release */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | DEVELOPMENT_TEAM = ""; 270 | PRODUCT_NAME = "$(TARGET_NAME)"; 271 | PROVISIONING_PROFILE_SPECIFIER = ""; 272 | }; 273 | name = Release; 274 | }; 275 | /* End XCBuildConfiguration section */ 276 | 277 | /* Begin XCConfigurationList section */ 278 | 02F41A641E7F0D11001F152D /* Build configuration list for PBXProject "CARParser" */ = { 279 | isa = XCConfigurationList; 280 | buildConfigurations = ( 281 | 02F41A6E1E7F0D11001F152D /* Debug */, 282 | 02F41A6F1E7F0D11001F152D /* Release */, 283 | ); 284 | defaultConfigurationIsVisible = 0; 285 | defaultConfigurationName = Release; 286 | }; 287 | 02F41A701E7F0D11001F152D /* Build configuration list for PBXNativeTarget "CARParser" */ = { 288 | isa = XCConfigurationList; 289 | buildConfigurations = ( 290 | 02F41A711E7F0D11001F152D /* Debug */, 291 | 02F41A721E7F0D11001F152D /* Release */, 292 | ); 293 | defaultConfigurationIsVisible = 0; 294 | defaultConfigurationName = Release; 295 | }; 296 | /* End XCConfigurationList section */ 297 | }; 298 | rootObject = 02F41A611E7F0D11001F152D /* Project object */; 299 | } 300 | -------------------------------------------------------------------------------- /CARParser/Bom.h: -------------------------------------------------------------------------------- 1 | // 2 | // Bom.h 3 | // CARParser 4 | // 5 | // Created by Alexandre Colucci. 6 | // Copyright © 2018 Timac. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | typedef uint32_t BOMBlockID; 12 | typedef struct BOMStorage *BOMStorage; 13 | 14 | typedef struct BOMTree *BOMTree; 15 | typedef struct BOMTreeIterator *BOMTreeIterator; 16 | 17 | // Opening a BOM 18 | BOMStorage BOMStorageOpen(const char *inPath, Boolean inWriting); 19 | 20 | // Accessing a BOM block 21 | BOMBlockID BOMStorageGetNamedBlock(BOMStorage inStorage, const char *inName); 22 | size_t BOMStorageSizeOfBlock(BOMStorage inStorage, BOMBlockID inBlockID); 23 | int BOMStorageCopyFromBlock(BOMStorage inStorage, BOMBlockID inBlockID, void *outData); 24 | 25 | // Accessing a BOM tree 26 | BOMTree BOMTreeOpenWithName(BOMStorage inStorage, const char *inName, Boolean inWriting); 27 | BOMTreeIterator BOMTreeIteratorNew(BOMTree inTree, void *, void *, void *); 28 | Boolean BOMTreeIteratorIsAtEnd(BOMTreeIterator iterator); 29 | void BOMTreeIteratorNext(BOMTreeIterator iterator); 30 | 31 | // Accessing the keys and values of a BOM tree 32 | void * BOMTreeIteratorKey(BOMTreeIterator iterator); 33 | size_t BOMTreeIteratorKeySize(BOMTreeIterator iterator); 34 | void * BOMTreeIteratorValue(BOMTreeIterator iterator); 35 | size_t BOMTreeIteratorValueSize(BOMTreeIterator iterator); 36 | -------------------------------------------------------------------------------- /CARParser/Car.h: -------------------------------------------------------------------------------- 1 | // 2 | // Car.h 3 | // CARParser 4 | // 5 | // Created by Alexandre Colucci. 6 | // Copyright © 2018 Timac. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | // MARK: - carheader 12 | 13 | struct carheader 14 | { 15 | uint32_t tag; // 'CTAR' 16 | uint32_t coreuiVersion; 17 | uint32_t storageVersion; 18 | uint32_t storageTimestamp; 19 | uint32_t renditionCount; 20 | char mainVersionString[128]; 21 | char versionString[256]; 22 | uuid_t uuid; 23 | uint32_t associatedChecksum; 24 | uint32_t schemaVersion; 25 | uint32_t colorSpaceID; 26 | uint32_t keySemantics; 27 | } __attribute__((packed)); 28 | 29 | 30 | // MARK: - carextendedMetadata 31 | 32 | struct carextendedMetadata { 33 | uint32_t tag; // 'META' 34 | char thinningArguments[256]; 35 | char deploymentPlatformVersion[256]; 36 | char deploymentPlatform[256]; 37 | char authoringTool[256]; 38 | } __attribute__((packed)); 39 | 40 | 41 | 42 | // MARK: - renditionkeytoken 43 | 44 | // As seen in -[CUIRenditionKey nameOfAttributeName:] 45 | enum RenditionAttributeType 46 | { 47 | kRenditionAttributeType_ThemeLook = 0, 48 | kRenditionAttributeType_Element = 1, 49 | kRenditionAttributeType_Part = 2, 50 | kRenditionAttributeType_Size = 3, 51 | kRenditionAttributeType_Direction = 4, 52 | kRenditionAttributeType_placeholder = 5, 53 | kRenditionAttributeType_Value = 6, 54 | kRenditionAttributeType_ThemeAppearance = 7, 55 | kRenditionAttributeType_Dimension1 = 8, 56 | kRenditionAttributeType_Dimension2 = 9, 57 | kRenditionAttributeType_State = 10, 58 | kRenditionAttributeType_Layer = 11, 59 | kRenditionAttributeType_Scale = 12, 60 | kRenditionAttributeType_Unknown13 = 13, 61 | kRenditionAttributeType_PresentationState = 14, 62 | kRenditionAttributeType_Idiom = 15, 63 | kRenditionAttributeType_Subtype = 16, 64 | kRenditionAttributeType_Identifier = 17, 65 | kRenditionAttributeType_PreviousValue = 18, 66 | kRenditionAttributeType_PreviousState = 19, 67 | kRenditionAttributeType_HorizontalSizeClass = 20, 68 | kRenditionAttributeType_VerticalSizeClass = 21, 69 | kRenditionAttributeType_MemoryLevelClass = 22, 70 | kRenditionAttributeType_GraphicsFeatureSetClass = 23, 71 | kRenditionAttributeType_DisplayGamut = 24, 72 | kRenditionAttributeType_DeploymentTarget = 25 73 | }; 74 | typedef enum RenditionAttributeType RenditionAttributeType; 75 | 76 | 77 | struct renditionAttribute { 78 | uint16_t name; 79 | uint16_t value; 80 | } __attribute__((packed)); 81 | 82 | 83 | struct renditionkeytoken { 84 | struct { 85 | uint16_t x; 86 | uint16_t y; 87 | } cursorHotSpot; 88 | 89 | uint16_t numberOfAttributes; 90 | struct renditionAttribute attributes[]; 91 | } __attribute__((packed)); 92 | 93 | 94 | // MARK: - renditionkeyfmt 95 | 96 | 97 | struct renditionkeyfmt { 98 | uint32_t tag; // 'kfmt' 99 | uint32_t version; 100 | uint32_t maximumRenditionKeyTokenCount; 101 | uint32_t renditionKeyTokens[]; 102 | } __attribute__((packed)); 103 | 104 | 105 | // MARK: - csiheader 106 | 107 | 108 | struct renditionFlags { 109 | uint32_t isHeaderFlaggedFPO:1; 110 | uint32_t isExcludedFromContrastFilter:1; 111 | uint32_t isVectorBased:1; 112 | uint32_t isOpaque:1; 113 | uint32_t bitmapEncoding:4; 114 | uint32_t optOutOfThinning:1; 115 | uint32_t isFlippable:1; 116 | uint32_t isTintable:1; 117 | uint32_t preservedVectorRepresentation:1; 118 | uint32_t reserved:20; 119 | } __attribute__((packed)); 120 | 121 | struct csimetadata { 122 | uint32_t modtime; 123 | uint16_t layout; 124 | uint16_t zero; 125 | char name[128]; 126 | } __attribute__((packed)); 127 | 128 | 129 | struct csibitmaplist { 130 | uint32_t tvlLength; // Length of all the TLV following the csiheader 131 | uint32_t unknown; 132 | uint32_t zero; 133 | uint32_t renditionLength; 134 | } __attribute__((packed)); 135 | 136 | 137 | struct csiheader { 138 | uint32_t tag; // 'CTSI' 139 | uint32_t version; 140 | struct renditionFlags renditionFlags; 141 | uint32_t width; 142 | uint32_t height; 143 | uint32_t scaleFactor; 144 | uint32_t pixelFormat; 145 | struct { 146 | uint32_t colorSpaceID:4; 147 | uint32_t reserved:28; 148 | } colorSpace; 149 | struct csimetadata csimetadata; 150 | struct csibitmaplist csibitmaplist; 151 | } __attribute__((packed)); 152 | 153 | 154 | // MARK: - Layout 155 | 156 | 157 | enum RenditionLayoutType 158 | { 159 | kRenditionLayoutType_TextEffect = 0x007, 160 | kRenditionLayoutType_Vector = 0x009, 161 | 162 | kRenditionLayoutType_Data = 0x3E8, 163 | kRenditionLayoutType_ExternalLink = 0x3E9, 164 | kRenditionLayoutType_LayerStack = 0x3EA, 165 | kRenditionLayoutType_InternalReference = 0x3EB, 166 | kRenditionLayoutType_PackedImage = 0x3EC, 167 | kRenditionLayoutType_NameList = 0x3ED, 168 | kRenditionLayoutType_UnknownAddObject = 0x3EE, 169 | kRenditionLayoutType_Texture = 0x3EF, 170 | kRenditionLayoutType_TextureImage = 0x3F0, 171 | kRenditionLayoutType_Color = 0x3F1, 172 | kRenditionLayoutType_MultisizeImage = 0x3F2, 173 | kRenditionLayoutType_LayerReference = 0x3F4, 174 | kRenditionLayoutType_ContentRendition = 0x3F5, 175 | kRenditionLayoutType_RecognitionObject = 0x3F6, 176 | }; 177 | typedef enum RenditionLayoutType RenditionLayoutType; 178 | 179 | enum CoreThemeImageSubtype 180 | { 181 | kCoreThemeOnePartFixedSize = 10, 182 | kCoreThemeOnePartTile = 11, 183 | kCoreThemeOnePartScale = 12, 184 | kCoreThemeThreePartHTile = 20, 185 | kCoreThemeThreePartHScale = 21, 186 | kCoreThemeThreePartHUniform = 22, 187 | kCoreThemeThreePartVTile = 23, 188 | kCoreThemeThreePartVScale = 24, 189 | kCoreThemeThreePartVUniform = 25, 190 | kCoreThemeNinePartTile = 30, 191 | kCoreThemeNinePartScale = 31, 192 | kCoreThemeNinePartHorizontalUniformVerticalScale = 32, 193 | kCoreThemeNinePartHorizontalScaleVerticalUniform = 33, 194 | kCoreThemeNinePartEdgesOnly = 34, 195 | kCoreThemeManyPartLayoutUnknown = 40, 196 | kCoreThemeAnimationFilmstrip = 50 197 | }; 198 | typedef enum CoreThemeImageSubtype CoreThemeImageSubtype; 199 | 200 | 201 | // MARK: - TLV 202 | 203 | 204 | // As seen in -[CSIGenerator writeResourcesToData:] 205 | enum RenditionTLVType 206 | { 207 | kRenditionTLVType_Slices = 0x3E9, 208 | kRenditionTLVType_Metrics = 0x3EB, 209 | kRenditionTLVType_BlendModeAndOpacity = 0x3EC, 210 | kRenditionTLVType_UTI = 0x3ED, 211 | kRenditionTLVType_EXIFOrientation = 0x3EE, 212 | kRenditionTLVType_ExternalTags = 0x3F0, 213 | kRenditionTLVType_Frame = 0x3F1, 214 | }; 215 | typedef enum RenditionTLVType RenditionTLVType; 216 | 217 | 218 | // MARK: - CUIRawDataRendition 219 | 220 | struct CUIRawDataRendition { 221 | uint32_t tag; // RAWD 222 | uint32_t version; 223 | uint32_t rawDataLength; 224 | uint8_t rawData[]; 225 | } __attribute__((packed)); 226 | 227 | 228 | // MARK: - CUIRawPixelRendition 229 | 230 | struct CUIRawPixelRendition { 231 | uint32_t tag; // RAWD 232 | uint32_t version; 233 | uint32_t rawDataLength; 234 | uint8_t rawData[]; 235 | } __attribute__((packed)); 236 | 237 | 238 | // MARK: - csicolor 239 | 240 | struct csicolor { 241 | uint32_t tag; // COLR 242 | uint32_t version; 243 | struct { 244 | uint32_t colorSpaceID:8; 245 | uint32_t unknown0:3; 246 | uint32_t reserved:21; 247 | } colorSpace; 248 | uint32_t numberOfComponents; 249 | double components[]; 250 | } __attribute__((packed)); 251 | 252 | 253 | 254 | // MARK: - CUIThemePixelRendition 255 | 256 | 257 | // As seen in _CUIConvertCompressionTypeToString 258 | enum RenditionCompressionType 259 | { 260 | kRenditionCompressionType_uncompressed = 0, 261 | kRenditionCompressionType_rle, 262 | kRenditionCompressionType_zip, 263 | kRenditionCompressionType_lzvn, 264 | kRenditionCompressionType_lzfse, 265 | kRenditionCompressionType_jpeg_lzfse, 266 | kRenditionCompressionType_blurred, 267 | kRenditionCompressionType_astc, 268 | kRenditionCompressionType_palette_img, 269 | kRenditionCompressionType_deepmap_lzfse, 270 | }; 271 | typedef enum RenditionCompressionType RenditionCompressionType; 272 | 273 | struct CUIThemePixelRendition { 274 | uint32_t tag; // 'CELM' 275 | uint32_t version; 276 | uint32_t compressionType; 277 | uint32_t rawDataLength; 278 | uint8_t rawData[]; 279 | } __attribute__((packed)); 280 | 281 | -------------------------------------------------------------------------------- /CARParser/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CARParser 4 | // 5 | // Created by Alexandre Colucci. 6 | // Copyright © 2018 Timac. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Bom.h" 11 | #import "Car.h" 12 | 13 | // Print an OSType 14 | #define FourCC2Str(fourcc) (const char[]){*(((char*)&fourcc)+3), *(((char*)&fourcc)+2), *(((char*)&fourcc)+1), *(((char*)&fourcc)+0),0} 15 | 16 | 17 | void PrintUsage() 18 | { 19 | printf("NAME\n"); 20 | printf("\t\tCARParser to parse bom files\n\n"); 21 | 22 | printf("SYNOPSIS\n"); 23 | printf("\t\tCARParser path\n\n"); 24 | 25 | printf("DESCRIPTION\n"); 26 | printf("\t\tCARParser is a command line tool to inspect the content of .car files.\n\n"); 27 | 28 | printf("\n"); 29 | } 30 | 31 | 32 | // MARK: - Utilities 33 | 34 | NSString *GetUnixTimestampDescription(uint32_t inUnixTimestamp) 35 | { 36 | NSString *formattedDate = nil; 37 | 38 | if(inUnixTimestamp > 0) 39 | { 40 | NSDate *theDate = [NSDate dateWithTimeIntervalSince1970:inUnixTimestamp]; 41 | if(theDate != nil) 42 | { 43 | NSTimeZone *timezone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 44 | NSISO8601DateFormatOptions options = NSISO8601DateFormatWithInternetDateTime | NSISO8601DateFormatWithDashSeparatorInDate | NSISO8601DateFormatWithColonSeparatorInTime | NSISO8601DateFormatWithTimeZone; 45 | formattedDate = [NSISO8601DateFormatter stringFromDate:theDate timeZone:timezone formatOptions:options]; 46 | } 47 | } 48 | 49 | if(formattedDate == nil) 50 | { 51 | return [NSString stringWithFormat:@"%u", inUnixTimestamp]; 52 | } 53 | else 54 | { 55 | return [NSString stringWithFormat:@"%u (%@)", inUnixTimestamp, formattedDate]; 56 | } 57 | } 58 | 59 | NSString *GetOSTypeDescription(OSType inOSType) 60 | { 61 | NSString *formattedString = nil; 62 | 63 | if(inOSType > 0) 64 | { 65 | formattedString = [NSString stringWithFormat:@"'%s' (0x%04X)", FourCC2Str(inOSType), inOSType]; 66 | } 67 | else 68 | { 69 | formattedString = [NSString stringWithFormat:@"0x%04X", inOSType]; 70 | } 71 | 72 | return formattedString; 73 | } 74 | 75 | NSString *GetScaleFactorDescription(uint32_t inScaleFactor) 76 | { 77 | NSString *formattedScaleFactor = nil; 78 | 79 | if(inScaleFactor > 0 && ((inScaleFactor % 100) == 0)) 80 | { 81 | formattedScaleFactor = [NSString stringWithFormat:@"%u (@%ux)", inScaleFactor, inScaleFactor / 100]; 82 | } 83 | else 84 | { 85 | formattedScaleFactor = [NSString stringWithFormat:@"%u", inScaleFactor]; 86 | } 87 | 88 | return formattedScaleFactor; 89 | } 90 | 91 | NSString *GetRenditionFlagsDescription(struct renditionFlags *inRenditionFlags) 92 | { 93 | if(inRenditionFlags == NULL) 94 | { 95 | return @""; 96 | } 97 | 98 | NSMutableString *renditionFlagsString = [[NSMutableString alloc] initWithString:@""]; 99 | 100 | BOOL shouldAddComma = NO; 101 | 102 | if(inRenditionFlags->isHeaderFlaggedFPO) 103 | { 104 | if(shouldAddComma) 105 | { 106 | [renditionFlagsString appendString:@","]; 107 | } 108 | shouldAddComma = YES; 109 | 110 | [renditionFlagsString appendString:@"isHeaderFlaggedFPO"]; 111 | } 112 | 113 | if(inRenditionFlags->isExcludedFromContrastFilter) 114 | { 115 | if(shouldAddComma) 116 | { 117 | [renditionFlagsString appendString:@","]; 118 | } 119 | shouldAddComma = YES; 120 | 121 | [renditionFlagsString appendString:@"isExcludedFromContrastFilter"]; 122 | } 123 | 124 | if(inRenditionFlags->isVectorBased) 125 | { 126 | if(shouldAddComma) 127 | { 128 | [renditionFlagsString appendString:@","]; 129 | } 130 | shouldAddComma = YES; 131 | 132 | [renditionFlagsString appendString:@"isVectorBased"]; 133 | } 134 | 135 | if(inRenditionFlags->isOpaque) 136 | { 137 | if(shouldAddComma) 138 | { 139 | [renditionFlagsString appendString:@","]; 140 | } 141 | shouldAddComma = YES; 142 | 143 | [renditionFlagsString appendString:@"isOpaque"]; 144 | } 145 | 146 | if(shouldAddComma) 147 | { 148 | [renditionFlagsString appendString:@","]; 149 | } 150 | shouldAddComma = YES; 151 | 152 | [renditionFlagsString appendString:[NSString stringWithFormat:@"bitmapEncoding %X", inRenditionFlags->bitmapEncoding]]; 153 | 154 | if(inRenditionFlags->optOutOfThinning) 155 | { 156 | if(shouldAddComma) 157 | { 158 | [renditionFlagsString appendString:@","]; 159 | } 160 | shouldAddComma = YES; 161 | 162 | [renditionFlagsString appendString:@"optOutOfThinning"]; 163 | } 164 | 165 | if(inRenditionFlags->isFlippable) 166 | { 167 | if(shouldAddComma) 168 | { 169 | [renditionFlagsString appendString:@","]; 170 | } 171 | shouldAddComma = YES; 172 | 173 | [renditionFlagsString appendString:@"isFlippable"]; 174 | } 175 | 176 | if(inRenditionFlags->isTintable) 177 | { 178 | if(shouldAddComma) 179 | { 180 | [renditionFlagsString appendString:@","]; 181 | } 182 | shouldAddComma = YES; 183 | 184 | [renditionFlagsString appendString:@"isTintable"]; 185 | } 186 | 187 | if(inRenditionFlags->preservedVectorRepresentation) 188 | { 189 | if(shouldAddComma) 190 | { 191 | [renditionFlagsString appendString:@","]; 192 | } 193 | shouldAddComma = YES; 194 | 195 | [renditionFlagsString appendString:@"preservedVectorRepresentation"]; 196 | } 197 | 198 | // Prevent a never read warning 199 | (void)shouldAddComma; 200 | 201 | return renditionFlagsString; 202 | } 203 | 204 | NSString *GetNameOfAttributeType(RenditionAttributeType inAttributeType) 205 | { 206 | switch (inAttributeType) 207 | { 208 | case kRenditionAttributeType_ThemeLook: 209 | return @"Theme Look"; 210 | break; 211 | 212 | case kRenditionAttributeType_Element: 213 | return @"Element"; 214 | break; 215 | 216 | case kRenditionAttributeType_Part: 217 | return @"Part"; 218 | break; 219 | 220 | case kRenditionAttributeType_Size: 221 | return @"Size"; 222 | break; 223 | 224 | case kRenditionAttributeType_Direction: 225 | return @"Direction"; 226 | break; 227 | 228 | case kRenditionAttributeType_placeholder: 229 | return @"placeholder"; 230 | break; 231 | 232 | case kRenditionAttributeType_Value: 233 | return @"Value"; 234 | break; 235 | 236 | case kRenditionAttributeType_ThemeAppearance: 237 | return @"Theme Appearance"; 238 | break; 239 | 240 | case kRenditionAttributeType_Dimension1: 241 | return @"Dimension 1"; 242 | break; 243 | 244 | case kRenditionAttributeType_Dimension2: 245 | return @"Dimension 2"; 246 | break; 247 | 248 | case kRenditionAttributeType_State: 249 | return @"State"; 250 | break; 251 | 252 | case kRenditionAttributeType_Layer: 253 | return @"Layer"; 254 | break; 255 | 256 | case kRenditionAttributeType_Scale: 257 | return @"Scale"; 258 | break; 259 | 260 | case kRenditionAttributeType_PresentationState: 261 | return @"Presentation State"; 262 | break; 263 | 264 | case kRenditionAttributeType_Idiom: 265 | return @"Idiom"; 266 | break; 267 | 268 | case kRenditionAttributeType_Subtype: 269 | return @"Subtype"; 270 | break; 271 | 272 | case kRenditionAttributeType_Identifier: 273 | return @"Identifier"; 274 | break; 275 | 276 | case kRenditionAttributeType_PreviousValue: 277 | return @"Previous Value"; 278 | break; 279 | 280 | case kRenditionAttributeType_PreviousState: 281 | return @"Previous State"; 282 | break; 283 | 284 | case kRenditionAttributeType_HorizontalSizeClass: 285 | return @"Horizontal Size Class"; 286 | break; 287 | 288 | case kRenditionAttributeType_VerticalSizeClass: 289 | return @"Vertical Size Class"; 290 | break; 291 | 292 | case kRenditionAttributeType_MemoryLevelClass: 293 | return @"Memory Level Class"; 294 | break; 295 | 296 | case kRenditionAttributeType_GraphicsFeatureSetClass: 297 | return @"Graphics Feature Set Class"; 298 | break; 299 | 300 | case kRenditionAttributeType_DisplayGamut: 301 | return @"Display Gamut"; 302 | break; 303 | 304 | case kRenditionAttributeType_DeploymentTarget: 305 | return @"Deployment Target"; 306 | break; 307 | 308 | default: 309 | return [NSString stringWithFormat:@"Unknown %d", inAttributeType]; 310 | break; 311 | } 312 | } 313 | 314 | // From -[CUINamedColor _colorSpaceWithID:] 315 | NSString *GetColorSpaceNameWithID(int64_t inColorSpaceID) 316 | { 317 | switch (inColorSpaceID) 318 | { 319 | case 0: 320 | default: 321 | { 322 | return @"SRGB"; 323 | } 324 | break; 325 | 326 | case 1: 327 | { 328 | return @"GrayGamma2_2"; 329 | } 330 | break; 331 | 332 | case 2: 333 | { 334 | return @"DisplayP3"; 335 | } 336 | break; 337 | 338 | case 3: 339 | { 340 | return @"ExtendedRangeSRGB"; 341 | } 342 | break; 343 | 344 | case 4: 345 | { 346 | return @"ExtendedLinearSRGB"; 347 | } 348 | break; 349 | 350 | case 5: 351 | { 352 | return @"ExtendedGray"; 353 | } 354 | break; 355 | } 356 | } 357 | 358 | NSString *GetTLVTNameWithType(RenditionTLVType inTLVType) 359 | { 360 | switch (inTLVType) 361 | { 362 | case kRenditionTLVType_Slices: 363 | { 364 | return @"Slices"; 365 | } 366 | break; 367 | 368 | case kRenditionTLVType_Metrics: 369 | { 370 | return @"Metrics"; 371 | } 372 | break; 373 | 374 | case kRenditionTLVType_BlendModeAndOpacity: 375 | { 376 | return @"BlendModeAndOpacity"; 377 | } 378 | break; 379 | 380 | case kRenditionTLVType_UTI: 381 | { 382 | return @"UTI"; 383 | } 384 | break; 385 | 386 | case kRenditionTLVType_EXIFOrientation: 387 | { 388 | return @"EXIFOrientation"; 389 | } 390 | break; 391 | 392 | case kRenditionTLVType_ExternalTags: 393 | { 394 | return @"ExternalTags"; 395 | } 396 | break; 397 | 398 | case kRenditionTLVType_Frame: 399 | { 400 | return @"Frame"; 401 | } 402 | break; 403 | 404 | default: 405 | { 406 | return [NSString stringWithFormat:@"Unknown 0x%04X", inTLVType]; 407 | } 408 | break; 409 | } 410 | } 411 | 412 | NSString *GetNameWithCoreThemeImageSubType(CoreThemeImageSubtype inType) 413 | { 414 | switch (inType) 415 | { 416 | case kCoreThemeOnePartFixedSize: 417 | { 418 | return @"One Part Fixed Size"; 419 | } 420 | break; 421 | 422 | case kCoreThemeOnePartTile: 423 | { 424 | return @"One Part Tiled"; 425 | } 426 | break; 427 | 428 | case kCoreThemeOnePartScale: 429 | { 430 | return @"One Part Scaled"; 431 | } 432 | break; 433 | 434 | case kCoreThemeThreePartHTile: 435 | { 436 | return @"Three Part Horizontal Tiled"; 437 | } 438 | break; 439 | 440 | case kCoreThemeThreePartHScale: 441 | { 442 | return @"Three Part Horizontal Scaled"; 443 | } 444 | break; 445 | 446 | case kCoreThemeThreePartHUniform: 447 | { 448 | return @"Three Part Horizontal Uniform"; 449 | } 450 | break; 451 | 452 | case kCoreThemeThreePartVTile: 453 | { 454 | return @"Three Part Vertical Tiled"; 455 | } 456 | break; 457 | 458 | case kCoreThemeThreePartVScale: 459 | { 460 | return @"Three Part Vertical Scaled"; 461 | } 462 | break; 463 | 464 | case kCoreThemeThreePartVUniform: 465 | { 466 | return @"Three Part Vertical Uniform"; 467 | } 468 | break; 469 | 470 | case kCoreThemeNinePartTile: 471 | { 472 | return @"Nine Part Tiled"; 473 | } 474 | break; 475 | 476 | case kCoreThemeNinePartScale: 477 | { 478 | return @"Nine Part Scaled"; 479 | } 480 | break; 481 | 482 | case kCoreThemeNinePartHorizontalUniformVerticalScale: 483 | { 484 | return @"Nine Part Horizontal Uniform Vertical Scaled"; 485 | } 486 | break; 487 | 488 | case kCoreThemeNinePartHorizontalScaleVerticalUniform: 489 | { 490 | return @"Nine Part Horizontal Scaled Vertical Uniform"; 491 | } 492 | break; 493 | 494 | case kCoreThemeNinePartEdgesOnly: 495 | { 496 | return @"Nine Part Edges Only"; 497 | } 498 | break; 499 | 500 | case kCoreThemeManyPartLayoutUnknown: 501 | { 502 | return @"Many Part Layout Unknown"; 503 | } 504 | break; 505 | 506 | case kCoreThemeAnimationFilmstrip: 507 | { 508 | return @"Animation Filmstrip"; 509 | } 510 | break; 511 | 512 | default: 513 | { 514 | return [NSString stringWithFormat:@"Unknown 0x%04X", inType]; 515 | } 516 | break; 517 | } 518 | } 519 | 520 | NSString *GetRenditionLayoutNameWithType(RenditionLayoutType inType) 521 | { 522 | if(inType >= kCoreThemeOnePartFixedSize && inType < kRenditionLayoutType_Data) 523 | { 524 | return [NSString stringWithFormat:@"Image (%@)", GetNameWithCoreThemeImageSubType((CoreThemeImageSubtype)inType)]; 525 | } 526 | 527 | switch (inType) 528 | { 529 | case kRenditionLayoutType_TextEffect: 530 | { 531 | return @"TextEffect"; 532 | } 533 | break; 534 | 535 | case kRenditionLayoutType_Data: 536 | { 537 | return @"Data"; 538 | } 539 | break; 540 | 541 | case kRenditionLayoutType_ExternalLink: 542 | { 543 | return @"ExternalLink"; 544 | } 545 | break; 546 | 547 | case kRenditionLayoutType_LayerStack: 548 | { 549 | return @"LayerStack"; 550 | } 551 | break; 552 | 553 | case kRenditionLayoutType_InternalReference: 554 | { 555 | return @"InternalReference"; 556 | } 557 | break; 558 | 559 | case kRenditionLayoutType_PackedImage: 560 | { 561 | return @"PackedImage"; 562 | } 563 | break; 564 | 565 | case kRenditionLayoutType_NameList: 566 | { 567 | return @"NameList"; 568 | } 569 | break; 570 | 571 | case kRenditionLayoutType_UnknownAddObject: 572 | { 573 | return @"UnknownAddObject"; 574 | } 575 | break; 576 | 577 | case kRenditionLayoutType_Texture: 578 | { 579 | return @"Texture"; 580 | } 581 | break; 582 | 583 | case kRenditionLayoutType_TextureImage: 584 | { 585 | return @"TextureImage"; 586 | } 587 | break; 588 | 589 | case kRenditionLayoutType_Color: 590 | { 591 | return @"Color"; 592 | } 593 | break; 594 | 595 | case kRenditionLayoutType_MultisizeImage: 596 | { 597 | return @"MultisizeImage"; 598 | } 599 | break; 600 | 601 | case kRenditionLayoutType_LayerReference: 602 | { 603 | return @"LayerReference"; 604 | } 605 | break; 606 | 607 | case kRenditionLayoutType_ContentRendition: 608 | { 609 | return @"ContentRendition"; 610 | } 611 | break; 612 | 613 | case kRenditionLayoutType_RecognitionObject: 614 | { 615 | return @"Recognition"; 616 | } 617 | break; 618 | 619 | default: 620 | { 621 | return [NSString stringWithFormat:@"Unknown 0x%04X", inType]; 622 | } 623 | break; 624 | } 625 | } 626 | 627 | NSString *GetRenditionCompressionDescription(RenditionCompressionType inCompressionType) 628 | { 629 | switch (inCompressionType) 630 | { 631 | case kRenditionCompressionType_uncompressed: 632 | return @"uncompressed"; 633 | break; 634 | 635 | case kRenditionCompressionType_rle: 636 | return @"rle"; 637 | break; 638 | 639 | case kRenditionCompressionType_zip: 640 | return @"zip"; 641 | break; 642 | 643 | case kRenditionCompressionType_lzvn: 644 | return @"lzvn"; 645 | break; 646 | 647 | case kRenditionCompressionType_lzfse: 648 | return @"lzfse"; 649 | break; 650 | 651 | case kRenditionCompressionType_jpeg_lzfse: 652 | return @"jpeg-lzfse"; 653 | break; 654 | 655 | case kRenditionCompressionType_blurred: 656 | return @"blurred"; 657 | break; 658 | 659 | case kRenditionCompressionType_astc: 660 | return @"astc"; 661 | break; 662 | 663 | case kRenditionCompressionType_palette_img: 664 | return @"palette-img"; 665 | break; 666 | 667 | case kRenditionCompressionType_deepmap_lzfse: 668 | return @"deepmap-lzfse"; 669 | break; 670 | 671 | default: 672 | return @"Unknown"; 673 | break; 674 | } 675 | } 676 | 677 | 678 | // MARK: - BOM utilities 679 | 680 | NSData *GetDataFromBomBlock(BOMStorage inBOMStorage, const char *inBlockName) 681 | { 682 | NSData *outData = nil; 683 | 684 | BOMBlockID blockID = BOMStorageGetNamedBlock(inBOMStorage, inBlockName); 685 | size_t blockSize = BOMStorageSizeOfBlock(inBOMStorage, blockID); 686 | if(blockSize > 0) 687 | { 688 | void *mallocedBlock = malloc(blockSize); 689 | int res = BOMStorageCopyFromBlock(inBOMStorage, blockID, mallocedBlock); 690 | if(res == noErr) 691 | { 692 | outData = [[NSData alloc] initWithBytes:mallocedBlock length:blockSize]; 693 | } 694 | 695 | free(mallocedBlock); 696 | } 697 | 698 | return outData; 699 | } 700 | 701 | typedef void (^ParseBOMTreeCallback)(NSData *inKey, NSData *inValue); 702 | void ParseBOMTree(BOMStorage inBOMStorage, const char *inTreeName, ParseBOMTreeCallback keyValueCallback) 703 | { 704 | NSData *keyData = nil; 705 | NSData *keyValue = nil; 706 | 707 | // Open the BOM tree 708 | BOMTree bomTree = BOMTreeOpenWithName(inBOMStorage, inTreeName, false); 709 | if(bomTree == NULL) 710 | return; 711 | 712 | // Create a BOMTreeIterator and loop until the end 713 | BOMTreeIterator bomIterator = BOMTreeIteratorNew(bomTree, NULL, NULL, NULL); 714 | while(!BOMTreeIteratorIsAtEnd(bomIterator)) 715 | { 716 | // Get the key 717 | void * key = BOMTreeIteratorKey(bomIterator); 718 | size_t keySize = BOMTreeIteratorKeySize(bomIterator); 719 | keyData = [NSData dataWithBytes:key length:keySize]; 720 | 721 | // Get the value associated to the key 722 | size_t valueSize = BOMTreeIteratorValueSize(bomIterator); 723 | if(valueSize > 0) 724 | { 725 | void * value = BOMTreeIteratorValue(bomIterator); 726 | if(value != NULL) 727 | { 728 | keyValue = [NSData dataWithBytes:value length:valueSize]; 729 | } 730 | } 731 | 732 | if(keyData != nil) 733 | { 734 | keyValueCallback(keyData, keyValue); 735 | } 736 | 737 | // Next item in the tree 738 | BOMTreeIteratorNext(bomIterator); 739 | } 740 | } 741 | 742 | 743 | // MARK: - Parse the .car file 744 | 745 | int main(int argc, const char * argv[]) 746 | { 747 | @autoreleasepool 748 | { 749 | if(argc != 2) 750 | { 751 | PrintUsage(); 752 | return -1; 753 | } 754 | 755 | NSString *path = [NSString stringWithUTF8String:argv[1]]; 756 | 757 | BOMStorage bomStorage = BOMStorageOpen([path fileSystemRepresentation], false); 758 | 759 | // Dump CARHEADER 760 | { 761 | NSData *blockData = GetDataFromBomBlock(bomStorage, "CARHEADER"); 762 | if(blockData != nil) 763 | { 764 | struct carheader *carHeader = (struct carheader *)[blockData bytes]; 765 | if(carHeader->tag == 'RATC') 766 | { 767 | // The header should be swapped 768 | NSLog(@"The CARHEADER header should be swapped"); 769 | } 770 | 771 | fprintf(stderr, "\nCARHEADER:\n" 772 | "\t coreuiVersion: %u\n" 773 | "\t storageVersion: %u\n" 774 | "\t storageTimestamp: %s\n" 775 | "\t renditionCount: %u\n" 776 | "\t mainVersionString: %s" 777 | "\t versionString: %s\n" 778 | "\t uuid: %s\n" 779 | "\t associatedChecksum: 0x%04X\n" 780 | "\t schemaVersion: %u\n" 781 | "\t colorSpaceID: %u\n" 782 | "\t keySemantics: %u\n", 783 | carHeader->coreuiVersion, 784 | carHeader->storageVersion, 785 | [GetUnixTimestampDescription(carHeader->storageTimestamp) UTF8String], 786 | carHeader->renditionCount, 787 | carHeader->mainVersionString, 788 | carHeader->versionString, 789 | [[[[NSUUID alloc] initWithUUIDBytes:carHeader->uuid] UUIDString] UTF8String], 790 | carHeader->associatedChecksum, 791 | carHeader->schemaVersion, 792 | carHeader->colorSpaceID, 793 | carHeader->keySemantics); 794 | } 795 | else 796 | { 797 | NSLog(@"Invalid CARHEADER"); 798 | } 799 | } 800 | 801 | // Dump EXTENDED_METADATA 802 | { 803 | NSData *blockData = GetDataFromBomBlock(bomStorage, "EXTENDED_METADATA"); 804 | if(blockData != nil) 805 | { 806 | struct carextendedMetadata *carExtendedMetadata = (struct carextendedMetadata *)[blockData bytes]; 807 | 808 | fprintf(stderr, "\nEXTENDED_METADATA:\n" 809 | "\t thinningArguments: %s\n" 810 | "\t deploymentPlatformVersion: %s\n" 811 | "\t deploymentPlatform: %s\n" 812 | "\t authoringTool: %s\n", 813 | carExtendedMetadata->thinningArguments, 814 | carExtendedMetadata->deploymentPlatformVersion, 815 | carExtendedMetadata->deploymentPlatform, 816 | carExtendedMetadata->authoringTool); 817 | } 818 | else 819 | { 820 | NSLog(@"Invalid EXTENDED_METADATA"); 821 | } 822 | } 823 | 824 | // Dump CARGLOBALS 825 | { 826 | NSData *blockData = GetDataFromBomBlock(bomStorage, "CARGLOBALS"); 827 | if(blockData != nil) 828 | { 829 | NSLog(@"CARGLOBALS: %@", blockData); 830 | } 831 | } 832 | 833 | 834 | NSMutableArray *keyFormatStrings = [[NSMutableArray alloc] init]; 835 | 836 | // Dump KEYFORMAT 837 | { 838 | NSData *blockData = GetDataFromBomBlock(bomStorage, "KEYFORMAT"); 839 | if(blockData != nil) 840 | { 841 | struct renditionkeyfmt *keyFormat = (struct renditionkeyfmt *)[blockData bytes]; 842 | 843 | fprintf(stderr, "\nKEYFORMAT:\n" 844 | "\t maximumRenditionKeyTokenCount: %u\n", 845 | keyFormat->maximumRenditionKeyTokenCount); 846 | 847 | for(uint32_t renditionKeyTokenIndex = 0 ; renditionKeyTokenIndex < keyFormat->maximumRenditionKeyTokenCount ; renditionKeyTokenIndex++) 848 | { 849 | NSString *attributeName = GetNameOfAttributeType(keyFormat->renditionKeyTokens[renditionKeyTokenIndex]); 850 | fprintf(stderr, "\t renditionKeyTokens: %s\n", [attributeName UTF8String]); 851 | [keyFormatStrings addObject:attributeName]; 852 | } 853 | } 854 | else 855 | { 856 | NSLog(@"Invalid KEYFORMAT"); 857 | } 858 | } 859 | 860 | // Dump KEYFORMATWORKAROUND 861 | { 862 | NSData *blockData = GetDataFromBomBlock(bomStorage, "KEYFORMATWORKAROUND"); 863 | if(blockData != nil) 864 | { 865 | struct renditionkeyfmt *keyFormatWorkaround = (struct renditionkeyfmt *)[blockData bytes]; 866 | 867 | fprintf(stderr, "\nKEYFORMATWORKAROUND:\n" 868 | "\t maximumRenditionKeyTokenCount: %u\n", 869 | keyFormatWorkaround->maximumRenditionKeyTokenCount); 870 | 871 | for(uint32_t renditionKeyTokenIndex = 0 ; renditionKeyTokenIndex < keyFormatWorkaround->maximumRenditionKeyTokenCount ; renditionKeyTokenIndex++) 872 | { 873 | fprintf(stderr, "\t renditionKeyTokens: %s\n", [GetNameOfAttributeType(keyFormatWorkaround->renditionKeyTokens[renditionKeyTokenIndex]) UTF8String]); 874 | } 875 | } 876 | } 877 | 878 | // Dump EXTERNAL_KEYS 879 | { 880 | NSData *blockData = GetDataFromBomBlock(bomStorage, "EXTERNAL_KEYS"); 881 | if(blockData != nil) 882 | { 883 | NSLog(@"EXTERNAL_KEYS: %@", blockData); 884 | } 885 | } 886 | 887 | fprintf(stderr, "\nTree APPEARANCEKEYS\n"); 888 | ParseBOMTree(bomStorage, "APPEARANCEKEYS", ^(NSData *inKey, NSData *inValue) 889 | { 890 | NSString *appearanceName = [[NSString alloc] initWithBytes:[inKey bytes] length:[inKey length] encoding:NSUTF8StringEncoding]; 891 | uint16_t appearanceIdentifier = 0; 892 | if(inValue != nil) 893 | { 894 | appearanceIdentifier = *(uint16_t *)([inValue bytes]); 895 | } 896 | 897 | fprintf(stderr, "\t '%s': %u\n", [appearanceName UTF8String], appearanceIdentifier); 898 | }); 899 | 900 | fprintf(stderr, "\nTree FACETKEYS\n"); 901 | ParseBOMTree(bomStorage, "FACETKEYS", ^(NSData *inKey, NSData *inValue) 902 | { 903 | NSString *facetName = [[NSString alloc] initWithBytes:[inKey bytes] length:[inKey length] encoding:NSUTF8StringEncoding]; 904 | fprintf(stderr, "\t '%s':", [facetName UTF8String]); 905 | 906 | const void *bytes = [inValue bytes]; 907 | if(bytes != NULL) 908 | { 909 | struct renditionkeytoken *renditionkeytoken = (struct renditionkeytoken *)bytes; 910 | uint16_t numberOfAttributes = renditionkeytoken->numberOfAttributes; 911 | for(uint16_t keyIndex = 0 ; keyIndex < numberOfAttributes ; keyIndex++) 912 | { 913 | struct renditionAttribute renditionAttribute = renditionkeytoken->attributes[keyIndex]; 914 | fprintf(stderr, "\n\t\t %s: %04X", [GetNameOfAttributeType(renditionAttribute.name) UTF8String], renditionAttribute.value); 915 | } 916 | } 917 | 918 | fprintf(stderr, "\n"); 919 | }); 920 | 921 | fprintf(stderr, "\nTree RENDITIONS\n"); 922 | ParseBOMTree(bomStorage, "RENDITIONS", ^(NSData *inKey, NSData *inValue) 923 | { 924 | fprintf(stderr, "\n\t Key '%s'\n", [[inKey description] UTF8String]); 925 | 926 | // Parse the key 927 | const void *keyBytes = [inKey bytes]; 928 | for(uint16_t keyIndex = 0 ; keyIndex < ([inKey length] / 2) ; keyIndex++) 929 | { 930 | uint16_t key = *(uint16_t *)(keyBytes + 2 * keyIndex); 931 | fprintf(stderr, "\t\t %s: %04X\n", [keyFormatStrings[keyIndex] UTF8String], key); 932 | } 933 | 934 | // Parse the value 935 | if(inValue != nil) 936 | { 937 | const void *valueBytes = [inValue bytes]; 938 | 939 | struct csiheader *csiHeader = (struct csiheader *)valueBytes; 940 | 941 | fprintf(stderr, "\n\t\t csiHeader:\n" 942 | "\t\t\t version: %u\n" 943 | "\t\t\t renditionFlags: %s\n" 944 | "\t\t\t width: %u\n" 945 | "\t\t\t height: %u\n" 946 | "\t\t\t scaleFactor: %s\n" 947 | "\t\t\t pixelFormat: %s\n" 948 | "\t\t\t colorSpaceID: %u\n" 949 | "\t\t\t modtime: %s\n" 950 | "\t\t\t layout: %s\n" 951 | "\t\t\t name: %s\n" 952 | "\t\t\t tvlLength: %u\n" 953 | "\t\t\t renditionLength: %u\n" 954 | , 955 | csiHeader->version, 956 | [GetRenditionFlagsDescription(&(csiHeader->renditionFlags)) UTF8String], 957 | csiHeader->width, 958 | csiHeader->height, 959 | [GetScaleFactorDescription(csiHeader->scaleFactor) UTF8String], 960 | [GetOSTypeDescription(csiHeader->pixelFormat) UTF8String], 961 | csiHeader->colorSpace.colorSpaceID, 962 | [GetUnixTimestampDescription(csiHeader->csimetadata.modtime) UTF8String], 963 | [GetRenditionLayoutNameWithType(csiHeader->csimetadata.layout) UTF8String], 964 | csiHeader->csimetadata.name, 965 | csiHeader->csibitmaplist.tvlLength, 966 | csiHeader->csibitmaplist.renditionLength); 967 | 968 | // Print the TLV 969 | uint32_t tvlLength = csiHeader->csibitmaplist.tvlLength; 970 | if(tvlLength > 0) 971 | { 972 | fprintf(stderr, "\t\t\t tlv:\n"); 973 | 974 | const void *tlvBytes = valueBytes + sizeof(*csiHeader); 975 | const void *tlvPos = tlvBytes; 976 | 977 | while(tlvBytes + tvlLength > tlvPos) 978 | { 979 | uint32_t tlvTag = *(uint32_t *)tlvPos; 980 | uint32_t tlvLength = *(uint32_t *)(tlvPos + 4); 981 | 982 | fprintf(stderr, "\t\t\t\t %s: " , [GetTLVTNameWithType(tlvTag) UTF8String]); 983 | for(uint32_t valuePos = 0 ; valuePos < tlvLength ; valuePos++) 984 | { 985 | fprintf(stderr, "%02X" , *(uint8_t*)(tlvPos + 8 + valuePos)); 986 | } 987 | 988 | fprintf(stderr, "\n"); 989 | 990 | tlvPos += 8 + tlvLength; 991 | } 992 | } 993 | 994 | const void *renditionBytes = valueBytes + sizeof(*csiHeader) + csiHeader->csibitmaplist.tvlLength; 995 | 996 | if(csiHeader->pixelFormat == 'DATA') 997 | { 998 | struct CUIRawDataRendition *rawDataRendition = (struct CUIRawDataRendition *)renditionBytes; 999 | if(rawDataRendition->tag == 'RAWD') 1000 | { 1001 | uint32_t rawDataLength = rawDataRendition->rawDataLength; 1002 | uint8_t *rawData = rawDataRendition->rawData; 1003 | if(rawDataLength > 4) 1004 | { 1005 | 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)); 1006 | } 1007 | else 1008 | { 1009 | fprintf(stderr, "\t\t\t Found RawDataRendition with size %u\n", rawDataLength); 1010 | } 1011 | } 1012 | } 1013 | else if(csiHeader->pixelFormat == 'JPEG' || csiHeader->pixelFormat == 'HEIF') 1014 | { 1015 | struct CUIRawPixelRendition *rawPixelRendition = (struct CUIRawPixelRendition *)renditionBytes; 1016 | if(rawPixelRendition->tag == 'RAWD') 1017 | { 1018 | uint32_t rawDataLength = rawPixelRendition->rawDataLength; 1019 | uint8_t *rawDataBytes = rawPixelRendition->rawData; 1020 | 1021 | NSData *rawData = [[NSData alloc] initWithBytes:rawDataBytes length:rawDataLength]; 1022 | CGImageSourceRef sourceRef = CGImageSourceCreateWithData((CFDataRef)rawData, NULL); 1023 | CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL); 1024 | fprintf(stderr, "\t\t\t Found RawPixelRendition of size (%ld x %ld) with rawDataLength %u\n", CGImageGetWidth(imageRef), CGImageGetHeight(imageRef), rawDataLength); 1025 | CFRelease(imageRef); 1026 | CFRelease(sourceRef); 1027 | } 1028 | } 1029 | else if(csiHeader->pixelFormat == 0 && csiHeader->csimetadata.layout == kRenditionLayoutType_Color) 1030 | { 1031 | struct csicolor *colorRendition = (struct csicolor *)renditionBytes; 1032 | 1033 | if(colorRendition->numberOfComponents == 4) 1034 | { 1035 | // Use the hardcoded DeviceRGB color space instead of the real colorSpace from the colorSpaceID 1036 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 1037 | CGColorRef __unused theColor = CGColorCreate(colorSpaceRef, colorRendition->components); 1038 | CFRelease(theColor); 1039 | CFRelease(colorSpaceRef); 1040 | 1041 | NSString *colorString = [NSString stringWithFormat:@"%f,%f,%f,%f", colorRendition->components[0], colorRendition->components[1], colorRendition->components[2], colorRendition->components[3]]; 1042 | fprintf(stderr, "\n\t\t Found Color %s with colorspace ID %d\n", [colorString UTF8String], colorRendition->colorSpace.colorSpaceID & 0xFF); 1043 | } 1044 | else 1045 | { 1046 | fprintf(stderr, "\n\t\t Found Color with colorspace ID %d but with %u components\n", colorRendition->colorSpace.colorSpaceID & 0xFF, colorRendition->numberOfComponents); 1047 | } 1048 | } 1049 | else if(csiHeader->pixelFormat == 'ARGB' || csiHeader->pixelFormat == 'GA8 ' || 1050 | csiHeader->pixelFormat == 'RGB5' || csiHeader->pixelFormat == 'RGBW' || 1051 | csiHeader->pixelFormat == 'GA16') 1052 | { 1053 | struct CUIThemePixelRendition *themePixelRendition = (struct CUIThemePixelRendition *)renditionBytes; 1054 | 1055 | uint32_t compressionType = themePixelRendition->compressionType; 1056 | uint32_t rawDataLength = themePixelRendition->rawDataLength; 1057 | uint8_t * __unused rawDataBytes = themePixelRendition->rawData; 1058 | 1059 | fprintf(stderr, "\n\t\t Found ThemePixelRendition with size %u and compression %s\n", rawDataLength, [GetRenditionCompressionDescription(compressionType) UTF8String]); 1060 | } 1061 | else 1062 | { 1063 | fprintf(stderr, "\n\t\t Found unknown rendition with pixel format %s\n", FourCC2Str(csiHeader->pixelFormat)); 1064 | } 1065 | } 1066 | }); 1067 | 1068 | // Dump the other trees 1069 | NSArray *treeNames = @[ @"COLORS", @"FONTS", @"FONTSIZES", @"GLYPHS", @"BEZELS", @"BITMAPKEYS", @"ELEMENT_INFO", @"PART_INFO"]; 1070 | for(NSString *treeName in treeNames) 1071 | { 1072 | fprintf(stderr, "\nTree '%s'\n", [treeName UTF8String]); 1073 | 1074 | BOMTree bomTree = BOMTreeOpenWithName(bomStorage, [treeName UTF8String], false); 1075 | if(bomTree == NULL) 1076 | continue; 1077 | 1078 | // Parse the tree 1079 | BOMTreeIterator bomIterator = BOMTreeIteratorNew(bomTree, NULL, NULL, NULL); 1080 | while(!BOMTreeIteratorIsAtEnd(bomIterator)) 1081 | { 1082 | // Get the key 1083 | void * key = BOMTreeIteratorKey(bomIterator); 1084 | size_t keySize = BOMTreeIteratorKeySize(bomIterator); 1085 | NSData * keyData = [NSData dataWithBytes:key length:keySize]; 1086 | 1087 | BOOL isValidString = YES; 1088 | NSString *keyString = [[NSString alloc] initWithData:keyData encoding:NSASCIIStringEncoding]; 1089 | if([keyString length] == [keyData length]) 1090 | { 1091 | for(NSUInteger characterIndex = 0 ; characterIndex < [keyString length] ; characterIndex++) 1092 | { 1093 | unichar theChar = [keyString characterAtIndex:characterIndex]; 1094 | isValidString &= isprint(theChar); 1095 | if(!isValidString) 1096 | { 1097 | break; 1098 | } 1099 | } 1100 | } 1101 | else 1102 | { 1103 | isValidString = NO; 1104 | } 1105 | 1106 | // Get the value associated to the key 1107 | void * value = BOMTreeIteratorValue(bomIterator); 1108 | size_t valueSize = BOMTreeIteratorValueSize(bomIterator); 1109 | NSData *valueData = [NSData dataWithBytes:value length:valueSize]; 1110 | 1111 | if(isValidString) 1112 | { 1113 | fprintf(stderr, "\t Key '%s' -> %s\n", [keyString UTF8String], [[valueData description] UTF8String]); 1114 | } 1115 | else 1116 | { 1117 | fprintf(stderr, "\t Key '%s' -> %s\n", [[keyData description] UTF8String], [[valueData description] UTF8String]); 1118 | } 1119 | 1120 | // Next item in the tree 1121 | BOMTreeIteratorNext(bomIterator); 1122 | } 1123 | } 1124 | } 1125 | 1126 | return 0; 1127 | } 1128 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Alexandre Colucci, blog.timac.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CARParser 2 | CARParser is a tool to manually parse .car files (compiled Asset Catalogs) 3 | 4 | # Blog post 5 | [https://blog.timac.org/2018/1018-reverse-engineering-the-car-file-format/](https://blog.timac.org/2018/1018-reverse-engineering-the-car-file-format/) 6 | 7 | --------------------------------------------------------------------------------