diff --git a/src/AppIcon+Car.swift b/AssetCarReader/AssetCarReader.swift similarity index 54% rename from src/AppIcon+Car.swift rename to AssetCarReader/AssetCarReader.swift index b136395..1ed660a 100644 --- a/src/AppIcon+Car.swift +++ b/AssetCarReader/AssetCarReader.swift @@ -1,33 +1,27 @@ import Foundation import AppKit // NSImage -import CoreUI // CUICatalog -import os // OSLog +private import CoreUI // CUICatalog +private import os // OSLog -private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "AppIcon+Car") +private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "AssetCarReader") -// this has been written from scratch but general usage on -// including the private framework has been taken from: -// https://github.com/showxu/cartools -// also see: -// https://blog.timac.org/2018/1018-reverse-engineering-the-car-file-format/ - -extension AppIcon { - /// Use `CUICatalog` to extract an image from `Assets.car` - func imageFromAssetsCar(_ imageName: String) -> NSImage? { - guard let data = meta.readPayloadFile("Assets.car") else { - return nil - } - let catalog: CUICatalog +public class CarReader { + private let catalog: CUICatalog + + public init?(_ data: Data) { do { catalog = try data.withUnsafeBytes { try CUICatalog(bytes: $0.baseAddress!, length: UInt64(data.count)) } } catch { - os_log(.error, log: log, "[icon-car] ERROR: could not open catalog: %{public}@", error.localizedDescription) + os_log(.error, log: log, "[asset-car] ERROR: could not open catalog: %{public}@", error.localizedDescription) return nil } - - if let validName = carVerifyNameExists(imageName, in: catalog) { - if let bestImage = carFindHighestResolutionIcon(catalog.images(withName: validName)) { - os_log(.debug, log: log, "[icon-car] using Assets.car with key %{public}@", validName) + } + + /// Use `CUICatalog` to extract an image from `Assets.car` + public func imageFromAssetsCar(_ imageName: String) -> NSImage? { + if let validName = verifyNameExists(imageName, in: catalog) { + if let bestImage = findHighestResolutionIcon(catalog.images(withName: validName)) { + os_log(.debug, log: log, "[asset-car] using Assets.car with key %{public}@", validName) return NSImage(cgImage: bestImage.image, size: bestImage.size) } } @@ -35,19 +29,19 @@ extension AppIcon { } - // MARK: - Helper: Assets.car + // MARK: - Private methods /// Helper method to check available icon names. Will return a valid name or `nil` if no image with that key is found. - func carVerifyNameExists(_ imageName: String, in catalog: CUICatalog) -> String? { + private func verifyNameExists(_ imageName: String, in catalog: CUICatalog) -> String? { if let availableNames = catalog.allImageNames(), !availableNames.contains(imageName) { // Theoretically this should never happen. Assuming the image name is found in an image file. - os_log(.info, log: log, "[icon-car] WARN: key '%{public}@' does not match any available key", imageName) + os_log(.info, log: log, "[asset-car] WARN: key '%{public}@' does not match any available key", imageName) - if let alternativeName = carSearchAlternativeName(imageName, inAvailable: availableNames) { - os_log(.info, log: log, "[icon-car] falling back to '%{public}@'", alternativeName) + if let alternativeName = searchAlternativeName(imageName, inAvailable: availableNames) { + os_log(.info, log: log, "[asset-car] falling back to '%{public}@'", alternativeName) return alternativeName } - os_log(.debug, log: log, "[icon-car] available keys: %{public}@", catalog.allImageNames() ?? []) + os_log(.debug, log: log, "[asset-car] available keys: %{public}@", catalog.allImageNames() ?? []) return nil } return imageName; @@ -55,7 +49,7 @@ extension AppIcon { /// If exact name does not exist in catalog, search for a name that shares the same prefix. /// E.g., "AppIcon60x60" may match "AppIcon" or "AppIcon60x60_small" - func carSearchAlternativeName(_ originalName: String, inAvailable availableNames: [String]) -> String? { + private func searchAlternativeName(_ originalName: String, inAvailable availableNames: [String]) -> String? { var bestOption: String? = nil var bestDiff: Int = 999 @@ -72,7 +66,7 @@ extension AppIcon { } /// Given a list of `CUINamedImage`, return the one with the highest resolution. Vector graphics are ignored. - func carFindHighestResolutionIcon(_ availableImages: [CUINamedImage]) -> CUINamedImage? { + private func findHighestResolutionIcon(_ availableImages: [CUINamedImage]) -> CUINamedImage? { var largestWidth: CGFloat = 0 var largestImage: CUINamedImage? = nil // cast to NSArray is necessary as otherwise this will crash diff --git a/AssetCarReader/AssetCarReader.xcconfig b/AssetCarReader/AssetCarReader.xcconfig new file mode 100644 index 0000000..2864014 --- /dev/null +++ b/AssetCarReader/AssetCarReader.xcconfig @@ -0,0 +1,9 @@ +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +FRAMEWORK_SEARCH_PATHS = $(PROJECT_DIR)/AssetCarReader/PrivateFrameworks +SYSTEM_FRAMEWORK_SEARCH_PATHS = $(SYSTEM_LIBRARY_DIR)/PrivateFrameworks +SWIFT_INCLUDE_PATHS = $(SRCROOT)/AssetCarReader/PrivateFrameworks/CoreUI.framework + +DYLIB_COMPATIBILITY_VERSION = 1 +DYLIB_CURRENT_VERSION = 42 diff --git a/PrivateFrameworks/CoreUI.framework/Headers b/AssetCarReader/PrivateFrameworks/CoreUI.framework/Headers similarity index 100% rename from PrivateFrameworks/CoreUI.framework/Headers rename to AssetCarReader/PrivateFrameworks/CoreUI.framework/Headers diff --git a/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUICatalog.h b/AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUICatalog.h similarity index 100% rename from PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUICatalog.h rename to AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUICatalog.h diff --git a/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedImage.h b/AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedImage.h similarity index 100% rename from PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedImage.h rename to AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedImage.h diff --git a/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedLookup.h b/AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedLookup.h similarity index 100% rename from PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedLookup.h rename to AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/A/Headers/CUINamedLookup.h diff --git a/PrivateFrameworks/CoreUI.framework/Versions/Current b/AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/Current similarity index 100% rename from PrivateFrameworks/CoreUI.framework/Versions/Current rename to AssetCarReader/PrivateFrameworks/CoreUI.framework/Versions/Current diff --git a/PrivateFrameworks/CoreUI.framework/module.modulemap b/AssetCarReader/PrivateFrameworks/CoreUI.framework/module.modulemap similarity index 100% rename from PrivateFrameworks/CoreUI.framework/module.modulemap rename to AssetCarReader/PrivateFrameworks/CoreUI.framework/module.modulemap diff --git a/QLAppBundle.xcconfig b/QLAppBundle.xcconfig index 0200060..c7aa55e 100644 --- a/QLAppBundle.xcconfig +++ b/QLAppBundle.xcconfig @@ -1,10 +1,6 @@ // Configuration settings file format documentation can be found at: // https://help.apple.com/xcode/#/dev745c5c974 -FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)/PrivateFrameworks -SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) $(SYSTEM_LIBRARY_DIR)/PrivateFrameworks -SWIFT_INCLUDE_PATHS = $(inherited) $(SRCROOT)/PrivateFrameworks/CoreUI.framework - CODE_SIGN_STYLE = Manual CODE_SIGN_IDENTITY = Apple Development ENABLE_HARDENED_RUNTIME = YES diff --git a/QLAppBundle.xcodeproj/project.pbxproj b/QLAppBundle.xcodeproj/project.pbxproj index cd60c41..67a7cba 100644 --- a/QLAppBundle.xcodeproj/project.pbxproj +++ b/QLAppBundle.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 54442C722E378BDD008A870E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54442C6D2E378BDD008A870E /* MainMenu.xib */; }; 54442C792E378BE0008A870E /* PreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54442C742E378BE0008A870E /* PreviewViewController.swift */; }; 54442C7B2E378BE0008A870E /* PreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54442C762E378BE0008A870E /* PreviewViewController.xib */; }; + 544AF3692EB6AAC0006837F2 /* AssetCarReader.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 544AF3682EB6AAC0006837F2 /* AssetCarReader.xcconfig */; }; 545459C42EA469E4002892E5 /* defaultIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 54D3A6F22EA4603B001EF4F6 /* defaultIcon.png */; }; 545459C52EA469EA002892E5 /* template.html in Resources */ = {isa = PBXBuildFile; fileRef = 54D3A6F32EA4603B001EF4F6 /* template.html */; }; 54581FD12EB29A0B0043A0B3 /* QuickLookThumbnailing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54581FD02EB29A0B0043A0B3 /* QuickLookThumbnailing.framework */; }; @@ -23,14 +24,10 @@ 54581FDA2EB29A0B0043A0B3 /* QLThumbnail.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 54581FCF2EB29A0B0043A0B3 /* QLThumbnail.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 545820232EB29B4C0043A0B3 /* defaultIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 54D3A6F22EA4603B001EF4F6 /* defaultIcon.png */; }; 5469E11D2EA5930C00D46CE7 /* Entitlements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469E11C2EA5930C00D46CE7 /* Entitlements.swift */; }; - 5478996F2EB38EBB00F96B80 /* CoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54D3A6F52EA4610B001EF4F6 /* CoreUI.framework */; }; - 547899702EB38EBB00F96B80 /* CoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54D3A6F52EA4610B001EF4F6 /* CoreUI.framework */; }; 547899712EB38F3D00F96B80 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5405CF642EA1376B00613856 /* Zip.swift */; }; 547899722EB38F3D00F96B80 /* MetaInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5405CF5D2EA1199B00613856 /* MetaInfo.swift */; }; 547899732EB38F3D00F96B80 /* NSBezierPath+RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D3A6EF2EA3F49F001EF4F6 /* NSBezierPath+RoundedRect.swift */; }; 547899752EB38F3D00F96B80 /* AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D3A6ED2EA39CC6001EF4F6 /* AppIcon.swift */; }; - 547899762EB38FD400F96B80 /* AppIcon+Car.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545459C62EA4773A002892E5 /* AppIcon+Car.swift */; }; - 547899772EB38FD400F96B80 /* AppIcon+Car.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545459C62EA4773A002892E5 /* AppIcon+Car.swift */; }; 547F52DE2EB2C15D002B6D5F /* ExpirationStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52DC2EB2C15D002B6D5F /* ExpirationStatus.swift */; }; 547F52E42EB2C3D8002B6D5F /* Html+iTunesPurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52E32EB2C3D8002B6D5F /* Html+iTunesPurchase.swift */; }; 547F52E82EB2C41C002B6D5F /* HtmlGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52E62EB2C41C002B6D5F /* HtmlGenerator.swift */; }; @@ -41,6 +38,12 @@ 547F52F72EB2CAC7002B6D5F /* Html+Footer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52F62EB2CAC7002B6D5F /* Html+Footer.swift */; }; 547F52F92EB2CBAB002B6D5F /* Date+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52F82EB2CBAB002B6D5F /* Date+Format.swift */; }; 54AE5BFF2EB3DB1000B4CFC7 /* ThumbnailProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AE5BFD2EB3DB1000B4CFC7 /* ThumbnailProvider.swift */; }; + 54B6FFEE2EB6A847007397C0 /* AssetCarReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B6FFEC2EB6A847007397C0 /* AssetCarReader.swift */; }; + 54B6FFEF2EB6A8E0007397C0 /* CoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54D3A6F52EA4610B001EF4F6 /* CoreUI.framework */; }; + 54B6FFF02EB6AA0F007397C0 /* AssetCarReader.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54352E8A2EB6A79A0082F61D /* AssetCarReader.framework */; }; + 54B6FFF12EB6AA0F007397C0 /* AssetCarReader.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 54352E8A2EB6A79A0082F61D /* AssetCarReader.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 54B6FFF52EB6AA14007397C0 /* AssetCarReader.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54352E8A2EB6A79A0082F61D /* AssetCarReader.framework */; }; + 54B6FFF62EB6AA14007397C0 /* AssetCarReader.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 54352E8A2EB6A79A0082F61D /* AssetCarReader.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 54D3A6EC2EA31B52001EF4F6 /* AppCategories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D3A6EB2EA31B52001EF4F6 /* AppCategories.swift */; }; 54D3A6EE2EA39CC6001EF4F6 /* AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D3A6ED2EA39CC6001EF4F6 /* AppIcon.swift */; }; 54D3A6F02EA3F49F001EF4F6 /* NSBezierPath+RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D3A6EF2EA3F49F001EF4F6 /* NSBezierPath+RoundedRect.swift */; }; @@ -63,6 +66,20 @@ remoteGlobalIDString = 54581FCE2EB29A0B0043A0B3; remoteInfo = QLThumbnail; }; + 54B6FFF22EB6AA0F007397C0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 54442BEC2E378B71008A870E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 54352E892EB6A79A0082F61D; + remoteInfo = AssetCarReader; + }; + 54B6FFF72EB6AA14007397C0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 54442BEC2E378B71008A870E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 54352E892EB6A79A0082F61D; + remoteInfo = AssetCarReader; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -78,14 +95,36 @@ name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + 54B6FFF42EB6AA0F007397C0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 54B6FFF12EB6AA0F007397C0 /* AssetCarReader.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 54B6FFF92EB6AA14007397C0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 54B6FFF62EB6AA14007397C0 /* AssetCarReader.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 5405CF5D2EA1199B00613856 /* MetaInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaInfo.swift; sourceTree = ""; }; 5405CF642EA1376B00613856 /* Zip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Zip.swift; sourceTree = ""; }; + 54352E8A2EB6A79A0082F61D /* AssetCarReader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AssetCarReader.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 543FE5732EB3BB5E0059F98B /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = ""; }; 543FE5752EB3BC740059F98B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 543FE5772EB3DAA20059F98B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 54442BF42E378B71008A870E /* QLAppBundle (debug).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "QLAppBundle (debug).app"; sourceTree = BUILT_PRODUCTS_DIR; }; 54442C202E378BAF008A870E /* QLPreview.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = QLPreview.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 54442C222E378BAF008A870E /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; @@ -96,7 +135,7 @@ 54442C742E378BE0008A870E /* PreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewViewController.swift; sourceTree = ""; }; 54442C752E378BE0008A870E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PreviewViewController.xib; sourceTree = ""; }; 54442C772E378BE0008A870E /* QLPreview.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QLPreview.entitlements; sourceTree = ""; }; - 545459C62EA4773A002892E5 /* AppIcon+Car.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppIcon+Car.swift"; sourceTree = ""; }; + 544AF3682EB6AAC0006837F2 /* AssetCarReader.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AssetCarReader.xcconfig; sourceTree = ""; }; 54581FCF2EB29A0B0043A0B3 /* QLThumbnail.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = QLThumbnail.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 54581FD02EB29A0B0043A0B3 /* QuickLookThumbnailing.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLookThumbnailing.framework; path = System/Library/Frameworks/QuickLookThumbnailing.framework; sourceTree = SDKROOT; }; 545D9F3D2EB54C6200C38917 /* QLAppBundle.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = QLAppBundle.xcconfig; sourceTree = ""; }; @@ -112,10 +151,10 @@ 547F52F82EB2CBAB002B6D5F /* Date+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Format.swift"; sourceTree = ""; }; 547F52FB2EB37F10002B6D5F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 547F52FC2EB37F3A002B6D5F /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - 5485EE362EB1460C009E3905 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; }; 54AE5BFB2EB3DB1000B4CFC7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54AE5BFC2EB3DB1000B4CFC7 /* QLThumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QLThumbnail.entitlements; sourceTree = ""; }; 54AE5BFD2EB3DB1000B4CFC7 /* ThumbnailProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailProvider.swift; sourceTree = ""; }; + 54B6FFEC2EB6A847007397C0 /* AssetCarReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCarReader.swift; sourceTree = ""; }; 54D3A6EB2EA31B52001EF4F6 /* AppCategories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCategories.swift; sourceTree = ""; }; 54D3A6ED2EA39CC6001EF4F6 /* AppIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIcon.swift; sourceTree = ""; }; 54D3A6EF2EA3F49F001EF4F6 /* NSBezierPath+RoundedRect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSBezierPath+RoundedRect.swift"; sourceTree = ""; }; @@ -123,18 +162,18 @@ 54D3A6F32EA4603B001EF4F6 /* template.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = template.html; sourceTree = ""; }; 54D3A6F52EA4610B001EF4F6 /* CoreUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = CoreUI.framework; sourceTree = ""; }; 54D3A6FA2EA46588001EF4F6 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 54D3A6FC2EA465A9001EF4F6 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; - 54D3A6FF2EA465C4001EF4F6 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - 54E0874C2EB1488300979D91 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; }; - 54E0874E2EB1489100979D91 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; - 54E087512EB148B700979D91 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; - 54E087532EB148DB00979D91 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; }; - 54E087552EB148DF00979D91 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - 54E087572EB148E700979D91 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 54E087592EB15DD000979D91 /* style.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; path = style.css; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 54352E872EB6A79A0082F61D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 54B6FFEF2EB6A8E0007397C0 /* CoreUI.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54442BF12E378B71008A870E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -146,9 +185,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 54B6FFF02EB6AA0F007397C0 /* AssetCarReader.framework in Frameworks */, 54D3A6FE2EA465B4001EF4F6 /* CoreGraphics.framework in Frameworks */, 54442C232E378BAF008A870E /* Quartz.framework in Frameworks */, - 5478996F2EB38EBB00F96B80 /* CoreUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -156,9 +195,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 54B6FFF52EB6AA14007397C0 /* AssetCarReader.framework in Frameworks */, 54581FD12EB29A0B0043A0B3 /* QuickLookThumbnailing.framework in Frameworks */, 54581FD22EB29A0B0043A0B3 /* Quartz.framework in Frameworks */, - 547899702EB38EBB00F96B80 /* CoreUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -171,7 +210,6 @@ 5405CF5D2EA1199B00613856 /* MetaInfo.swift */, 54D3A6EB2EA31B52001EF4F6 /* AppCategories.swift */, 54D3A6ED2EA39CC6001EF4F6 /* AppIcon.swift */, - 545459C62EA4773A002892E5 /* AppIcon+Car.swift */, 5469E11C2EA5930C00D46CE7 /* Entitlements.swift */, 547F52DC2EB2C15D002B6D5F /* ExpirationStatus.swift */, 547F52E62EB2C41C002B6D5F /* HtmlGenerator.swift */, @@ -194,12 +232,12 @@ 545D9F3D2EB54C6200C38917 /* QLAppBundle.xcconfig */, 547F52FC2EB37F3A002B6D5F /* LICENSE */, 547F52FB2EB37F10002B6D5F /* README.md */, - 54D3A6F62EA4610B001EF4F6 /* PrivateFrameworks */, 541051562E37AFC10083670B /* src */, 54D3A6F42EA46069001EF4F6 /* resources */, 54442C6F2E378BDD008A870E /* App */, 54442C782E378BE0008A870E /* QLPreview */, 54AE5BFE2EB3DB1000B4CFC7 /* QLThumbnail */, + 54B6FFED2EB6A847007397C0 /* AssetCarReader */, 54442C212E378BAF008A870E /* Frameworks */, 54442BF52E378B71008A870E /* Products */, ); @@ -211,6 +249,7 @@ 54442BF42E378B71008A870E /* QLAppBundle (debug).app */, 54442C202E378BAF008A870E /* QLPreview.appex */, 54581FCF2EB29A0B0043A0B3 /* QLThumbnail.appex */, + 54352E8A2EB6A79A0082F61D /* AssetCarReader.framework */, ); name = Products; sourceTree = ""; @@ -218,16 +257,6 @@ 54442C212E378BAF008A870E /* Frameworks */ = { isa = PBXGroup; children = ( - 543FE5772EB3DAA20059F98B /* Foundation.framework */, - 54E087572EB148E700979D91 /* WebKit.framework */, - 54E087552EB148DF00979D91 /* Security.framework */, - 54E087532EB148DB00979D91 /* QuickLook.framework */, - 54E087512EB148B700979D91 /* libz.tbd */, - 54E0874E2EB1489100979D91 /* CoreFoundation.framework */, - 54E0874C2EB1488300979D91 /* ApplicationServices.framework */, - 5485EE362EB1460C009E3905 /* Network.framework */, - 54D3A6FF2EA465C4001EF4F6 /* AppKit.framework */, - 54D3A6FC2EA465A9001EF4F6 /* CoreServices.framework */, 54D3A6FA2EA46588001EF4F6 /* CoreGraphics.framework */, 54442C222E378BAF008A870E /* Quartz.framework */, 54581FD02EB29A0B0043A0B3 /* QuickLookThumbnailing.framework */, @@ -267,6 +296,16 @@ path = QLThumbnail; sourceTree = ""; }; + 54B6FFED2EB6A847007397C0 /* AssetCarReader */ = { + isa = PBXGroup; + children = ( + 544AF3682EB6AAC0006837F2 /* AssetCarReader.xcconfig */, + 54B6FFEC2EB6A847007397C0 /* AssetCarReader.swift */, + 54D3A6F62EA4610B001EF4F6 /* PrivateFrameworks */, + ); + path = AssetCarReader; + sourceTree = ""; + }; 54D3A6F42EA46069001EF4F6 /* resources */ = { isa = PBXGroup; children = ( @@ -288,7 +327,37 @@ }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 54352E852EB6A79A0082F61D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ + 54352E892EB6A79A0082F61D /* AssetCarReader */ = { + isa = PBXNativeTarget; + buildConfigurationList = 54352E902EB6A79A0082F61D /* Build configuration list for PBXNativeTarget "AssetCarReader" */; + buildPhases = ( + 54352E852EB6A79A0082F61D /* Headers */, + 54352E862EB6A79A0082F61D /* Sources */, + 54352E872EB6A79A0082F61D /* Frameworks */, + 54352E882EB6A79A0082F61D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AssetCarReader; + packageProductDependencies = ( + ); + productName = AssetCarReader; + productReference = 54352E8A2EB6A79A0082F61D /* AssetCarReader.framework */; + productType = "com.apple.product-type.framework"; + }; 54442BF32E378B71008A870E /* QLAppBundle */ = { isa = PBXNativeTarget; buildConfigurationList = 54442C012E378B71008A870E /* Build configuration list for PBXNativeTarget "QLAppBundle" */; @@ -318,10 +387,12 @@ 54442C1C2E378BAF008A870E /* Sources */, 54442C1D2E378BAF008A870E /* Frameworks */, 54442C1E2E378BAF008A870E /* Resources */, + 54B6FFF42EB6AA0F007397C0 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 54B6FFF32EB6AA0F007397C0 /* PBXTargetDependency */, ); name = QLPreview; packageProductDependencies = ( @@ -337,10 +408,12 @@ 54581FCB2EB29A0B0043A0B3 /* Sources */, 54581FCC2EB29A0B0043A0B3 /* Frameworks */, 54581FCD2EB29A0B0043A0B3 /* Resources */, + 54B6FFF92EB6AA14007397C0 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 54B6FFF82EB6AA14007397C0 /* PBXTargetDependency */, ); name = QLThumbnail; packageProductDependencies = ( @@ -387,11 +460,20 @@ 54442BF32E378B71008A870E /* QLAppBundle */, 54442C1F2E378BAF008A870E /* QLPreview */, 54581FCE2EB29A0B0043A0B3 /* QLThumbnail */, + 54352E892EB6A79A0082F61D /* AssetCarReader */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 54352E882EB6A79A0082F61D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 544AF3692EB6AAC0006837F2 /* AssetCarReader.xcconfig in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54442BF22E378B71008A870E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -423,6 +505,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 54352E862EB6A79A0082F61D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 54B6FFEE2EB6A847007397C0 /* AssetCarReader.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54442BF02E378B71008A870E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -451,7 +541,6 @@ 547F52F92EB2CBAB002B6D5F /* Date+Format.swift in Sources */, 54D3A6EC2EA31B52001EF4F6 /* AppCategories.swift in Sources */, 5405CF652EA1376B00613856 /* Zip.swift in Sources */, - 547899762EB38FD400F96B80 /* AppIcon+Car.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -464,7 +553,6 @@ 547899732EB38F3D00F96B80 /* NSBezierPath+RoundedRect.swift in Sources */, 54AE5BFF2EB3DB1000B4CFC7 /* ThumbnailProvider.swift in Sources */, 547899752EB38F3D00F96B80 /* AppIcon.swift in Sources */, - 547899772EB38FD400F96B80 /* AppIcon+Car.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -481,6 +569,16 @@ target = 54581FCE2EB29A0B0043A0B3 /* QLThumbnail */; targetProxy = 54581FD82EB29A0B0043A0B3 /* PBXContainerItemProxy */; }; + 54B6FFF32EB6AA0F007397C0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 54352E892EB6A79A0082F61D /* AssetCarReader */; + targetProxy = 54B6FFF22EB6AA0F007397C0 /* PBXContainerItemProxy */; + }; + 54B6FFF82EB6AA14007397C0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 54352E892EB6A79A0082F61D /* AssetCarReader */; + targetProxy = 54B6FFF72EB6AA14007397C0 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -503,6 +601,74 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 54352E8E2EB6A79A0082F61D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 544AF3682EB6AAC0006837F2 /* AssetCarReader.xcconfig */; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = de.relikd.AssetCarReader; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 54352E8F2EB6A79A0082F61D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 544AF3682EB6AAC0006837F2 /* AssetCarReader.xcconfig */; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = de.relikd.AssetCarReader; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 54442BFF2E378B71008A870E /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 545D9F3D2EB54C6200C38917 /* QLAppBundle.xcconfig */; @@ -538,7 +704,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1436; + CURRENT_PROJECT_VERSION = 1600; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UY657LKNHJ; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -604,7 +771,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1436; + CURRENT_PROJECT_VERSION = 1600; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = UY657LKNHJ; ENABLE_NS_ASSERTIONS = NO; @@ -633,6 +801,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = App/App.entitlements; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = UY657LKNHJ; GENERATE_INFOPLIST_FILE = YES; @@ -658,6 +827,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = App/App.entitlements; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = UY657LKNHJ; GENERATE_INFOPLIST_FILE = YES; @@ -682,6 +852,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = QLPreview/QLPreview.entitlements; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = UY657LKNHJ; GENERATE_INFOPLIST_FILE = YES; @@ -706,6 +877,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = QLPreview/QLPreview.entitlements; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = UY657LKNHJ; GENERATE_INFOPLIST_FILE = YES; @@ -730,6 +902,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = QLThumbnail/QLThumbnail.entitlements; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = UY657LKNHJ; GENERATE_INFOPLIST_FILE = YES; @@ -754,6 +927,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = QLThumbnail/QLThumbnail.entitlements; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = UY657LKNHJ; GENERATE_INFOPLIST_FILE = YES; @@ -777,6 +951,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 54352E902EB6A79A0082F61D /* Build configuration list for PBXNativeTarget "AssetCarReader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 54352E8E2EB6A79A0082F61D /* Debug */, + 54352E8F2EB6A79A0082F61D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 54442BEF2E378B71008A870E /* Build configuration list for PBXProject "QLAppBundle" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/src/AppIcon.swift b/src/AppIcon.swift index 4d067ff..4f077ac 100644 --- a/src/AppIcon.swift +++ b/src/AppIcon.swift @@ -1,5 +1,6 @@ import Foundation import AppKit // NSImage +import AssetCarReader // CarReader import os // OSLog private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "AppIcon") @@ -50,6 +51,14 @@ struct AppIcon { let iconURL = Bundle.main.url(forResource: "defaultIcon", withExtension: "png")! return NSImage(contentsOf: iconURL)! } + + /// Extract an image from `Assets.car` + func imageFromAssetsCar(_ imageName: String) -> NSImage? { + guard let data = meta.readPayloadFile("Assets.car") else { + return nil + } + return CarReader(data)?.imageFromAssetsCar(imageName) + } }