mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-24 11:45:27 +01:00
00e5e5e7a7
Now the theme manager can resize images based on the screen resolution detected into the config.plist or from the main screen of the pc. The resize of the icons only trigger when icons have different size.
461 lines
13 KiB
Swift
461 lines
13 KiB
Swift
//
|
|
// Extensions.swift
|
|
// Clover
|
|
//
|
|
// Created by vector sigma on 19/10/2019.
|
|
// Copyright © 2019 CloverHackyColor. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
import CommonCrypto
|
|
|
|
extension String {
|
|
/// Create `Data` from hexadecimal string representation
|
|
///
|
|
/// This takes a hexadecimal representation and creates a `Data` object. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.
|
|
///
|
|
/// - returns: Data represented by this hexadecimal string.
|
|
|
|
func hexadecimal() -> Data? {
|
|
var data = Data(capacity: self.count / 2)
|
|
|
|
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
|
|
regex.enumerateMatches(in: self, range: NSMakeRange(0, utf16.count)) { match, flags, stop in
|
|
let byteString = (self as NSString).substring(with: match!.range)
|
|
var num = UInt8(byteString, radix: 16)!
|
|
data.append(&num, count: 1)
|
|
}
|
|
|
|
guard data.count > 0 else { return nil }
|
|
|
|
return data
|
|
}
|
|
|
|
/// - returns: A quoted string for nvram
|
|
var nvramString: String {
|
|
get {
|
|
return "'\(self)'"
|
|
}
|
|
}
|
|
|
|
|
|
/// - returns: Bool value indicating that our string start with the same prefix ignoring casing.
|
|
func hasPrefixIgnoringCase(_ str: String) -> Bool {
|
|
return self.lowercased().hasPrefix(str.lowercased())
|
|
}
|
|
|
|
/// - returns: only digits contained from the given string
|
|
var keepNumericsOnly: String {
|
|
get {
|
|
return self.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined(separator: "")
|
|
}
|
|
}
|
|
|
|
//: ### Base64 encoding a string
|
|
func base64Encoded() -> String? {
|
|
if let data = self.data(using: .utf8) {
|
|
return data.base64EncodedString()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//: ### Base64 decoding a string
|
|
func base64Decoded() -> String? {
|
|
if let data = Data(base64Encoded: self) {
|
|
return String(data: data, encoding: .utf8)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//: ### Base64 encoding data
|
|
func base64EncodedHex() -> String? {
|
|
if let data = self.hexadecimal() {
|
|
return data.base64EncodedString()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//: ### Base64 decoding data
|
|
func base64DecodedHex() -> String? {
|
|
if let data = Data(base64Encoded: self, options: Data.Base64DecodingOptions.ignoreUnknownCharacters) {
|
|
return data.hexadecimal()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
public func noSpaces() -> String {
|
|
return self.trimmingCharacters(in: CharacterSet.whitespaces)
|
|
}
|
|
|
|
var lastPath: String {
|
|
get {
|
|
return (self as NSString).lastPathComponent
|
|
}
|
|
}
|
|
|
|
var fileExtension: String {
|
|
get {
|
|
return (self as NSString).pathExtension
|
|
}
|
|
}
|
|
|
|
var deletingLastPath: String {
|
|
get {
|
|
return (self as NSString).deletingLastPathComponent
|
|
}
|
|
}
|
|
|
|
var deletingFileExtension: String {
|
|
get {
|
|
return (self as NSString).deletingPathExtension
|
|
}
|
|
}
|
|
|
|
var componentsPath: [String] {
|
|
return (self as NSString).pathComponents
|
|
}
|
|
|
|
func addPath(_ path: String) -> String {
|
|
let nsSt = self as NSString
|
|
return nsSt.appendingPathComponent(path)
|
|
}
|
|
|
|
func appendingFileExtension(ext: String) -> String? {
|
|
let nsSt = self as NSString
|
|
return nsSt.appendingPathExtension(ext)
|
|
}
|
|
|
|
/// Replace any occurences of a string (word) with a new one (newWord)
|
|
///
|
|
/// - returns: String where word search is case insensitive while newWord replacing is case sensitive..
|
|
|
|
func replacingOccurrencesOf(inSensitive word: String, withSensitive newWord: String) -> String {
|
|
//print("word = \(word)")
|
|
//print("newWord = \(newWord)")
|
|
var newText = self
|
|
if let range = newText.lowercased().range(of: word.lowercased()) {
|
|
newText = newText.replacingOccurrences(of: word,
|
|
with: newWord,
|
|
options: String.CompareOptions.caseInsensitive,
|
|
range: range)
|
|
}
|
|
// check again
|
|
let r = newText.lowercased().range(of: word.lowercased())
|
|
if (r != nil) {
|
|
newText = newText.replacingOccurrencesOf(inSensitive: word, withSensitive: newWord)
|
|
}
|
|
return newText
|
|
}
|
|
|
|
// NSUserInterfaceItemIdentifier
|
|
func interfaceId() -> NSUserInterfaceItemIdentifier {
|
|
return NSUserInterfaceItemIdentifier(self)
|
|
}
|
|
|
|
// https://stackoverflow.com/questions/25554986/how-to-convert-string-to-unsafepointeruint8-and-length#
|
|
func toPointer() -> UnsafePointer<UInt8>? {
|
|
guard let data = self.data(using: String.Encoding.utf8) else { return nil }
|
|
|
|
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
|
|
let stream = OutputStream(toBuffer: buffer, capacity: data.count)
|
|
|
|
stream.open()
|
|
let value = data.withUnsafeBytes {
|
|
$0.baseAddress?.assumingMemoryBound(to: UInt8.self)
|
|
}
|
|
guard let val = value else {
|
|
return nil
|
|
}
|
|
|
|
stream.write(val, maxLength: data.count)
|
|
|
|
stream.close()
|
|
|
|
return UnsafePointer<UInt8>(buffer)
|
|
}
|
|
|
|
/// Escape XML special characthers such:
|
|
/// & as &, \ as ", ' as &apos, < as <, and > as >
|
|
var escapingXMLCharacters: String {
|
|
get {
|
|
/*
|
|
" "
|
|
' '
|
|
< <
|
|
> >
|
|
& &
|
|
*/
|
|
|
|
var s = self
|
|
s = s.replacingOccurrences(of: "&", with: "&")
|
|
s = s.replacingOccurrences(of: "\"", with: """)
|
|
s = s.replacingOccurrences(of: "'", with: "'")
|
|
s = s.replacingOccurrences(of: "<", with: "<")
|
|
s = s.replacingOccurrences(of: ">", with: ">")
|
|
return s
|
|
}
|
|
}
|
|
|
|
/// Convert XML characters such:
|
|
/// & to &, " to \ , ' to &apos, < to <, and > to >
|
|
var convertingXMLCharacters: String {
|
|
get {
|
|
/*
|
|
" "
|
|
' '
|
|
< <
|
|
> >
|
|
& &
|
|
*/
|
|
|
|
var s = self
|
|
s = s.replacingOccurrences(of: "&", with: "&")
|
|
s = s.replacingOccurrences(of: """, with: "\"")
|
|
s = s.replacingOccurrences(of: "'", with: "'")
|
|
s = s.replacingOccurrences(of: "<", with: "<")
|
|
s = s.replacingOccurrences(of: ">", with: ">")
|
|
return s
|
|
}
|
|
}
|
|
}
|
|
|
|
extension NSNumber {
|
|
var hexString: String {
|
|
get {
|
|
if (self.intValue > 0xFFFF) {
|
|
return String(format: "0x%08llx", self.intValue)
|
|
} else {
|
|
return String(format: "0x%04llx", self.intValue)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Int {
|
|
var data: Data {
|
|
get {
|
|
var num = self
|
|
return Data(bytes: &num, count: MemoryLayout<Int>.size)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension UInt8 {
|
|
var data: Data {
|
|
get {
|
|
var num = self
|
|
return Data(bytes: &num, count: MemoryLayout<UInt8>.size)
|
|
}
|
|
}
|
|
}
|
|
extension UInt16 {
|
|
var data: Data {
|
|
get {
|
|
var num = self
|
|
return Data(bytes: &num, count: MemoryLayout<UInt16>.size)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension UInt32 {
|
|
var data: Data {
|
|
get {
|
|
var num = self
|
|
return Data(bytes: &num, count: MemoryLayout<UInt32>.size)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Data {
|
|
/// Create hexadecimal string representation of `Data` object.
|
|
///
|
|
/// - returns: `String` representation of this `Data` object.
|
|
|
|
func hexadecimal() -> String {
|
|
return map { String(format: "%02x", $0) }
|
|
.joined(separator: "")
|
|
}
|
|
|
|
var sha1: String {
|
|
get {
|
|
var h = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
|
|
|
|
self.withUnsafeBytes {
|
|
_ = CC_SHA1($0.baseAddress, CC_LONG(self.count), &h)
|
|
}
|
|
return Data(h).hexadecimal()
|
|
}
|
|
}
|
|
|
|
func castToCPointer<T>() -> T {
|
|
let mem = UnsafeMutablePointer<T>.allocate(capacity: 1)
|
|
_ = self.copyBytes(to: UnsafeMutableBufferPointer(start: mem, count: 1))
|
|
let val = mem.move()
|
|
mem.deallocate()
|
|
return val
|
|
}
|
|
|
|
func toPointer() -> UnsafePointer<UInt8>? {
|
|
|
|
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: self.count)
|
|
let stream = OutputStream(toBuffer: buffer, capacity: self.count)
|
|
|
|
stream.open()
|
|
let value = self.withUnsafeBytes {
|
|
$0.baseAddress?.assumingMemoryBound(to: UInt8.self)
|
|
}
|
|
guard let val = value else {
|
|
return nil
|
|
}
|
|
|
|
stream.write(val, maxLength: self.count)
|
|
|
|
stream.close()
|
|
|
|
return UnsafePointer<UInt8>(buffer)
|
|
}
|
|
}
|
|
|
|
extension URL {
|
|
// https://stackoverflow.com/questions/38343186/write-extend-file-attributes-swift-example?answertab=active#tab-top
|
|
/// Get extended attribute.
|
|
func extendedAttribute(forName name: String) throws -> Data {
|
|
|
|
let data = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> Data in
|
|
|
|
// Determine attribute size:
|
|
let length = getxattr(fileSystemPath, name, nil, 0, 0, 0)
|
|
guard length >= 0 else { throw URL.posixError(errno) }
|
|
|
|
// Create buffer with required size:
|
|
var data = Data(count: length)
|
|
|
|
// Retrieve attribute:
|
|
let result = data.withUnsafeMutableBytes { [count = data.count] in
|
|
getxattr(fileSystemPath, name, $0.baseAddress, count, 0, 0)
|
|
}
|
|
guard result >= 0 else { throw URL.posixError(errno) }
|
|
return data
|
|
}
|
|
return data
|
|
}
|
|
|
|
/// Set extended attribute.
|
|
func setExtendedAttribute(data: Data, forName name: String) throws {
|
|
try self.withUnsafeFileSystemRepresentation { fileSystemPath in
|
|
let result = data.withUnsafeBytes {
|
|
setxattr(fileSystemPath, name, $0.baseAddress, data.count, 0, 0)
|
|
}
|
|
guard result >= 0 else { throw URL.posixError(errno) }
|
|
}
|
|
}
|
|
|
|
/// Remove extended attribute.
|
|
func removeExtendedAttribute(forName name: String) throws {
|
|
|
|
try self.withUnsafeFileSystemRepresentation { fileSystemPath in
|
|
let result = removexattr(fileSystemPath, name, 0)
|
|
guard result >= 0 else { throw URL.posixError(errno) }
|
|
}
|
|
}
|
|
|
|
/// Get list of all extended attributes.
|
|
func listExtendedAttributes() throws -> [String] {
|
|
|
|
let list = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> [String] in
|
|
let length = listxattr(fileSystemPath, nil, 0, 0)
|
|
guard length >= 0 else { throw URL.posixError(errno) }
|
|
|
|
// Create buffer with required size:
|
|
var namebuf = Array<CChar>(repeating: 0, count: length)
|
|
|
|
// Retrieve attribute list:
|
|
let result = listxattr(fileSystemPath, &namebuf, namebuf.count, 0)
|
|
guard result >= 0 else { throw URL.posixError(errno) }
|
|
|
|
// Extract attribute names:
|
|
let list = namebuf.split(separator: 0).compactMap {
|
|
$0.withUnsafeBufferPointer {
|
|
$0.withMemoryRebound(to: UInt8.self) {
|
|
String(bytes: $0, encoding: .utf8)
|
|
}
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
return list
|
|
}
|
|
|
|
/// Helper function to create an NSError from a Unix errno.
|
|
private static func posixError(_ err: Int32) -> NSError {
|
|
return NSError(domain: NSPOSIXErrorDomain, code: Int(err),
|
|
userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))])
|
|
}
|
|
}
|
|
|
|
extension io_object_t {
|
|
/// - Returns: The device's name.
|
|
func name() -> String? {
|
|
let buf = UnsafeMutablePointer<io_name_t>.allocate(capacity: 1)
|
|
defer { buf.deallocate() }
|
|
return buf.withMemoryRebound(to: CChar.self, capacity: MemoryLayout<io_name_t>.size) {
|
|
if IORegistryEntryGetName(self, $0) == KERN_SUCCESS {
|
|
return String(cString: $0)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func info() -> NSDictionary? {
|
|
var serviceDictionary : Unmanaged<CFMutableDictionary>?
|
|
if IORegistryEntryCreateCFProperties(self, &serviceDictionary, kCFAllocatorDefault, 0) == KERN_SUCCESS {
|
|
if let info : NSDictionary = serviceDictionary?.takeRetainedValue() {
|
|
return info
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
extension NSImage {
|
|
func resize(to newSize: NSSize) -> NSImage {
|
|
let frame = NSRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
|
|
if let representation = self.bestRepresentation(for: frame, context: nil, hints: nil) {
|
|
let image = NSImage(size: newSize, flipped: false, drawingHandler: { (_) -> Bool in
|
|
return representation.draw(in: frame)
|
|
})
|
|
|
|
return image
|
|
}
|
|
return self
|
|
}
|
|
}
|
|
|
|
extension NSBitmapImageRep {
|
|
var png: Data? {
|
|
get {
|
|
representation(using: .png, properties: [:])
|
|
}
|
|
}
|
|
}
|
|
|
|
extension NSView {
|
|
func removeAllConstraints() {
|
|
self.removeConstraints(self.constraints)
|
|
for view in self.subviews {
|
|
view.removeAllConstraints()
|
|
}
|
|
}
|
|
|
|
@IBInspectable var cornerRadius: CGFloat {
|
|
get {
|
|
return self.layer?.cornerRadius ?? 0
|
|
} set {
|
|
self.wantsLayer = true
|
|
self.layer?.masksToBounds = true
|
|
self.layer?.cornerRadius = CGFloat(Int(newValue * 100)) / 100
|
|
}
|
|
}
|
|
}
|
|
|