fix: CoreUI support via Framework abstraction

This commit is contained in:
relikd
2025-11-01 22:14:34 +01:00
parent 6d91972e97
commit 5902bf9aa3
11 changed files with 257 additions and 66 deletions

View File

@@ -0,0 +1,85 @@
import Foundation
import AppKit // NSImage
private import CoreUI // CUICatalog
private import os // OSLog
private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "AssetCarReader")
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, "[asset-car] ERROR: could not open catalog: %{public}@", error.localizedDescription)
return nil
}
}
/// 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)
}
}
return nil;
}
// 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.
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, "[asset-car] WARN: key '%{public}@' does not match any available key", imageName)
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, "[asset-car] available keys: %{public}@", catalog.allImageNames() ?? [])
return nil
}
return imageName;
}
/// 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"
private func searchAlternativeName(_ originalName: String, inAvailable availableNames: [String]) -> String? {
var bestOption: String? = nil
var bestDiff: Int = 999
for option in availableNames {
if option.hasPrefix(originalName) || originalName.hasPrefix(option) {
let thisDiff = max(originalName.count, option.count) - min(originalName.count, option.count)
if thisDiff < bestDiff {
bestDiff = thisDiff
bestOption = option
}
}
}
return bestOption
}
/// Given a list of `CUINamedImage`, return the one with the highest resolution. Vector graphics are ignored.
private func findHighestResolutionIcon(_ availableImages: [CUINamedImage]) -> CUINamedImage? {
var largestWidth: CGFloat = 0
var largestImage: CUINamedImage? = nil
// cast to NSArray is necessary as otherwise this will crash
for img in availableImages as NSArray {
guard let img = img as? CUINamedImage else {
continue // ignore CUINamedMultisizeImageSet
}
let w = img.size.width
if w > largestWidth {
largestWidth = w
largestImage = img
}
}
return largestImage
}
}

View File

@@ -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

View File

@@ -0,0 +1 @@
Versions/Current/Headers/

View File

@@ -0,0 +1,54 @@
#import <Foundation/Foundation.h>
@class NSBundle, NSCache, NSMapTable, NSString;
@class CUINamedImage, CUIStructuredThemeStore;
@interface CUICatalog: NSObject {
NSString * _assetStoreName;
NSBundle * _bundle;
unsigned int _fileHasDisplayGamutInKeySpace;
NSCache * _localObjectCache;
NSCache * _lookupCache;
NSCache * _negativeCache;
unsigned short _preferredLocalization;
unsigned int _purgeWhenFinished;
unsigned int _reserved;
NSMapTable * _storageMapTable;
unsigned long long _storageRef;
NSDictionary * _vibrantColorMatrixTints;
}
- (CUIStructuredThemeStore *)_themeStore;
+ (id)defaultUICatalogForBundle:(id)arg1;
- (id)initWithBytes:(const void*)arg1 length:(unsigned long long)arg2 error:(NSError **)arg3;
- (id)initWithName:(id)arg1 fromBundle:(id)arg2;
- (id)initWithName:(id)arg1 fromBundle:(id)arg2 error:(id*)arg3;
- (id)initWithURL:(id)arg1 error:(NSError **)arg2;
- (BOOL)imageExistsWithName:(id)arg1;
- (BOOL)imageExistsWithName:(id)arg1 scaleFactor:(double)arg2;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 appearanceName:(id)arg3;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 appearanceName:(id)arg4;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 appearanceName:(id)arg5;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 appearanceName:(id)arg9;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 memoryClass:(long long)arg9 graphicsClass:(long long)arg10;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 memoryClass:(unsigned long long)arg9 graphicsClass:(unsigned long long)arg10 appearanceIdentifier:(long long)arg11 graphicsFallBackOrder:(id)arg12 deviceSubtypeFallBackOrder:(id)arg13;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 memoryClass:(unsigned long long)arg9 graphicsClass:(unsigned long long)arg10 graphicsFallBackOrder:(id)arg11 deviceSubtypeFallBackOrder:(id)arg12;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 sizeClassHorizontal:(long long)arg5 sizeClassVertical:(long long)arg6;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 sizeClassHorizontal:(long long)arg5 sizeClassVertical:(long long)arg6 appearanceName:(id)arg7;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 layoutDirection:(long long)arg4 adjustRenditionKeyWithBlock:(id)arg5;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 displayGamut:(long long)arg3 layoutDirection:(long long)arg4;
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 displayGamut:(long long)arg3 layoutDirection:(long long)arg4 appearanceName:(id)arg5;
- (NSArray<CUINamedImage *> *)imagesWithName:(id)arg1;
- (NSArray<NSString *> *)allImageNames;
- (NSArray<NSString *> *)appearanceNames;
@end

View File

@@ -0,0 +1,54 @@
#import <Foundation/Foundation.h>
#import <CoreUI/CUINamedLookup.h>
#import <CoreGraphics/CoreGraphics.h>
@interface CUINamedImage: CUINamedLookup {
struct _cuiniproperties {
unsigned int isVectorBased : 1;
unsigned int hasSliceInformation : 1;
unsigned int hasAlignmentInformation : 1;
unsigned int resizingMode : 2;
unsigned int templateRenderingMode : 3;
unsigned int exifOrientation : 4;
unsigned int isAlphaCropped : 1;
unsigned int isFlippable : 1;
unsigned int isTintable : 1;
unsigned int preservedVectorRepresentation : 1;
unsigned int _reserved : 16;
} _imageProperties;
double _scale;
}
@property (readonly) CGRect NS_alignmentRect;
@property (nonatomic, readonly) NSEdgeInsets alignmentEdgeInsets;
@property (nonatomic, readonly) int blendMode;
@property (nonatomic, readonly) CGImageRef croppedImage;
@property (nonatomic, readonly) NSEdgeInsets edgeInsets;
@property (nonatomic, readonly) int exifOrientation;
@property (nonatomic, readonly) BOOL hasAlignmentInformation;
@property (nonatomic, readonly) BOOL hasSliceInformation;
@property (nonatomic, readonly) CGImageRef image;
@property (nonatomic, readonly) long long imageType;
@property (nonatomic, readonly) BOOL isAlphaCropped;
@property (nonatomic, readonly) BOOL isFlippable;
@property (nonatomic, readonly) BOOL isStructured;
@property (nonatomic, readonly) BOOL isTemplate;
@property (nonatomic, readonly) BOOL isVectorBased;
@property (nonatomic, readonly) double opacity;
@property (nonatomic, readonly) BOOL preservedVectorRepresentation;
@property (nonatomic, readonly) long long resizingMode;
@property (nonatomic, readonly) double scale;
@property (nonatomic, readonly) CGSize size;
@property (nonatomic, readonly) long long templateRenderingMode;
- (id)baseKey;
- (CGRect)alphaCroppedRect;
- (CGImageRef)createImageFromPDFRenditionWithScale:(double)arg1;
- (CGImageRef)croppedImage;
- (id)initWithName:(id)arg1 usingRenditionKey:(id)arg2 fromTheme:(unsigned long long)arg3;
- (CGSize)originalUncroppedSize;
- (double)positionOfSliceBoundary:(unsigned int)arg1;
@end

View File

@@ -0,0 +1,16 @@
#import <Foundation/Foundation.h>
@class CUIRenditionKey;
@interface CUINamedLookup: NSObject <NSLocking> {
unsigned int _distilledInVersion;
CUIRenditionKey * _key;
NSString * _name;
unsigned int _odContent;
NSString * _signature;
unsigned long long _storageRef;
}
- (id)initWithName:(id)arg1 usingRenditionKey:(id)arg2 fromTheme:(unsigned long long)arg3;
@end

View File

@@ -0,0 +1 @@
A

View File

@@ -0,0 +1,9 @@
module CoreUI {
// umbrella header "CoreUI.h"
// Here is the list of your private headers.
header "Headers/CUICatalog.h"
header "Headers/CUINamedLookup.h"
header "Headers/CUINamedImage.h"
export *
}