diff --git a/Silicon.xcodeproj/project.pbxproj b/Silicon.xcodeproj/project.pbxproj index 9c8c45c..f1dad22 100644 --- a/Silicon.xcodeproj/project.pbxproj +++ b/Silicon.xcodeproj/project.pbxproj @@ -122,6 +122,7 @@ 0586D6202566CED200CBBD48 /* DropView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropView.swift; sourceTree = ""; }; 05F025522575FF1200ADB3EA /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainWindowController.strings"; sourceTree = ""; }; 05F025562575FF1A00ADB3EA /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/MainWindowController.strings"; sourceTree = ""; }; + A0765F9625763C1000A333A1 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainWindowController.strings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -380,6 +381,7 @@ Base, "zh-Hans", "zh-HK", + fr, ); mainGroup = 0586D53E256696D900CBBD48; productRefGroup = 0586D548256696D900CBBD48 /* Products */; @@ -499,6 +501,7 @@ children = ( 05F025522575FF1200ADB3EA /* zh-Hans */, 05F025562575FF1A00ADB3EA /* zh-HK */, + A0765F9625763C1000A333A1 /* fr */, ); name = MainWindowController.strings; sourceTree = ""; diff --git a/Silicon/Classes/App.swift b/Silicon/Classes/App.swift index 32d5dba..f9e7c06 100644 --- a/Silicon/Classes/App.swift +++ b/Silicon/Classes/App.swift @@ -30,7 +30,6 @@ import Cocoa @objc public private( set ) var path: String @objc public private( set ) var version: String? @objc public private( set ) var icon: NSImage? - @objc public private( set ) var architectures: [ String ] @objc public private( set ) var isAppleSiliconReady: Bool @objc public private( set ) var architecture: String @objc public private( set ) var bundleID: String? @@ -88,69 +87,9 @@ import Cocoa self.name = FileManager.default.displayName( atPath: path ) self.path = path self.icon = NSWorkspace.shared.icon( forFile: path ) - self.architectures = macho.architectures - if( macho.architectures.count == 1 ) - { - if( macho.architectures.contains( "arm64" ) ) - { - self.isAppleSiliconReady = true - self.architecture = "Apple" - } - else if( macho.architectures.contains( "x86_64" ) ) - { - self.isAppleSiliconReady = false - self.architecture = "Intel 64" - } - else if( macho.architectures.contains( "i386" ) ) - { - self.isAppleSiliconReady = false - self.architecture = "Intel 32" - } - else if( macho.architectures.contains( "ppc" ) ) - { - self.isAppleSiliconReady = false - self.architecture = "PowerPC" - } - else - { - self.isAppleSiliconReady = false - self.architecture = "Unknown" - } - } - else - { - if( macho.architectures.contains( "arm64" ) ) - { - self.isAppleSiliconReady = true - self.architecture = "Universal" - } - else if( macho.architectures.contains( "ppc" ) && macho.architectures.contains( "i386" ) && macho.architectures.contains( "x86_64" ) ) - { - self.isAppleSiliconReady = false - self.architecture = "PowerPC/Intel 32/64" - } - else if( macho.architectures.contains( "ppc" ) && macho.architectures.contains( "x86_64" ) ) - { - self.isAppleSiliconReady = false - self.architecture = "PowerPC/Intel 64" - } - else if( macho.architectures.contains( "ppc" ) && macho.architectures.contains( "i386" ) ) - { - self.isAppleSiliconReady = false - self.architecture = "PowerPC/Intel 32" - } - else if( macho.architectures.contains( "i386" ) && macho.architectures.contains( "x86_64" ) ) - { - self.isAppleSiliconReady = false - self.architecture = "Intel 32/64" - } - else - { - self.isAppleSiliconReady = false - self.architecture = "Unknown" - } - } + self.isAppleSiliconReady = macho.isAppleSiliconReady + self.architecture = macho.architecturesName } @IBAction public func showInFinder( _ sender: Any? ) diff --git a/Silicon/Classes/MachOFile.swift b/Silicon/Classes/MachOFile.swift index a78168d..8f27019 100644 --- a/Silicon/Classes/MachOFile.swift +++ b/Silicon/Classes/MachOFile.swift @@ -26,88 +26,210 @@ import Foundation public class MachOFile { - public private( set ) var architectures: [ String ] = [] + public enum Architecture: String { + case i386 + case x86_64 + case arm + case arm64 + case ppc + case unknown = "" + } + + public private( set ) var architectures: [ Architecture ] = [] public init?( path: String ) { do { - let stream = try BinaryStream( path: path ) - let magic = try stream.readBigEndianUnsignedInteger() - - if magic == 0xCAFEBABE - { - let count = try stream.readBigEndianUnsignedInteger() - - for _ in 0 ..< count - { - let cpu = try stream.readBigEndianUnsignedInteger() - let _ = try stream.readBigEndianUnsignedInteger() - let _ = try stream.readBigEndianUnsignedInteger() - let _ = try stream.readBigEndianUnsignedInteger() - let _ = try stream.readBigEndianUnsignedInteger() - - self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) - } - } - else if magic == 0xCEFAEDFE - { - let cpu = try stream.readLittleEndianUnsignedInteger() - - self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) - } - else if magic == 0xFEEDFACE - { - let cpu = try stream.readBigEndianUnsignedInteger() - - self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) - } - else if magic == 0xCFFAEDFE - { - let cpu = try stream.readLittleEndianUnsignedInteger() - - self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) - } - else if magic == 0xFEEDFACF - { - let cpu = try stream.readBigEndianUnsignedInteger() - - self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) - } - else - { - return nil - } + try setupArchitectures(path: path) } catch { return nil } } +} + + +// MARK: - Helpers + +extension MachOFile { - public static func cpuToArch( type: UInt32 ) -> String - { - if type == 7 - { - return "i386" + public var isAppleSiliconReady: Bool { + includesAppleArchitecture + } + + public var architecturesName: String { + [ + uniqueArchitectureName, + universalName, + legacyArchitecturesNames + ].compactMap { $0 }.first ?? "Unknown" + } +} + +extension MachOFile.Architecture { + + public var name: String { + switch self { + case .arm64: + return "Apple" + case .x86_64: + return "Intel 64" + case .i386: + return "Intel 32" + case .ppc: + return "PowerPC" + default: + return "Unknown" } - else if type == 7 | 0x01000000 - { - return "x86_64" + } + + public var isApple: Bool { + self == .arm64 + } + + public var isIntel32: Bool { + self == .i386 + } + + public var isIntel64: Bool { + self == .x86_64 + } + + public var isIntel: Bool { + isIntel32 || isIntel64 + } + + public var isPPC: Bool { + self == .ppc + } +} + +// MARK: - Private + +extension MachOFile { + + private enum Error: Swift.Error { + case failedToReadArchitectures + } + + private var isUniqueArchitecture: Bool { + architectures.count == 1 + } + + private var includesAppleArchitecture: Bool { + architectures.filter({ $0.isApple }).count > 0 + } + + private var includesIntelArchitecture: Bool { + architectures.filter({ $0.isIntel }).count > 0 + } + + private var includesIntel32Architecture: Bool { + architectures.filter({ $0.isIntel32 }).count > 0 + } + + private var includesIntel64Architecture: Bool { + architectures.filter({ $0.isIntel64 }).count > 0 + } + + private var includesPPCArchitecture: Bool { + architectures.filter({ $0.isPPC }).count > 0 + } + + private var includesLegacyArchitectures: Bool { + includesPPCArchitecture || includesIntelArchitecture + } + + private var universalName: String? { + includesAppleArchitecture ? "Universal" : nil + } + + private var ppcName: String? { + includesPPCArchitecture ? "PowerPC" : nil + } + + private var intelName: String? { + if includesIntel32Architecture && includesIntel64Architecture { + return "Intel 32/64" + } else if includesIntel32Architecture { + return "Intel 32" + } else if includesIntel64Architecture { + return "Intel 64" + } else { + return nil } - else if type == 12 - { - return "arm" + } + + private var legacyArchitecturesNames: String? { + if !includesAppleArchitecture && includesLegacyArchitectures { + return [ppcName, intelName] + .compactMap { $0 } + .joined(separator: "/") + } else { + return nil } - else if type == 12 | 0x01000000 - { - return "arm64" + } + + private var uniqueArchitectureName: String? { + isUniqueArchitecture ? architectures.first?.name : nil + } + + private func setupArchitectures ( path: String ) throws { + let stream = try BinaryStream( path: path ) + let magic = try stream.readBigEndianUnsignedInteger() + + switch magic { + case 0xCAFEBABE: + let count = try stream.readBigEndianUnsignedInteger() + + for _ in 0 ..< count + { + let cpu = try stream.readBigEndianUnsignedInteger() + let _ = try stream.readBigEndianUnsignedInteger() + let _ = try stream.readBigEndianUnsignedInteger() + let _ = try stream.readBigEndianUnsignedInteger() + let _ = try stream.readBigEndianUnsignedInteger() + + self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) + } + case 0xCEFAEDFE: + let cpu = try stream.readLittleEndianUnsignedInteger() + + self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) + case 0xFEEDFACE: + let cpu = try stream.readBigEndianUnsignedInteger() + + self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) + case 0xCFFAEDFE: + let cpu = try stream.readLittleEndianUnsignedInteger() + + self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) + case 0xFEEDFACF: + let cpu = try stream.readBigEndianUnsignedInteger() + + self.architectures.append( MachOFile.cpuToArch( type: cpu ) ) + default: + throw Error.failedToReadArchitectures } - else if type == 18 - { - return "ppc" + } + + private static func cpuToArch( type: UInt32 ) -> Architecture + { + switch type { + case 7: + return .i386 + case 7 | 0x01000000: + return .x86_64 + case 12: + return .arm + case 12 | 0x01000000: + return .arm64 + case 18: + return .ppc + default: + return .unknown } - - return "" } } diff --git a/Silicon/Classes/MainWindowController.swift b/Silicon/Classes/MainWindowController.swift index e5dfa6f..27b587d 100644 --- a/Silicon/Classes/MainWindowController.swift +++ b/Silicon/Classes/MainWindowController.swift @@ -98,7 +98,7 @@ public class MainWindowController: NSWindowController return true } - if app.architectures.contains( "arm64" ) + if app.isAppleSiliconReady { let alert = NSAlert() diff --git a/Silicon/UI/fr.lproj/MainWindowController.strings b/Silicon/UI/fr.lproj/MainWindowController.strings new file mode 100644 index 0000000..1432e05 --- /dev/null +++ b/Silicon/UI/fr.lproj/MainWindowController.strings @@ -0,0 +1,36 @@ + +/* Class = "NSWindow"; title = "Silicon"; ObjectID = "4ss-uf-DBv"; */ +"4ss-uf-DBv.title" = "Silicon"; + +/* Class = "NSTextFieldCell"; title = "You can also drop an application here to verify it."; ObjectID = "9uz-mz-Can"; */ +"9uz-mz-Can.title" = "Vous pouvez aussi déposer ici une application pour la vérifier."; + +/* Class = "NSTextFieldCell"; title = "Identify Intel-Only Apps on your Mac"; ObjectID = "DFM-dV-koX"; */ +"DFM-dV-koX.title" = "Identifier les Apps Seulement Intel de votre Mac"; + +/* Class = "NSButton"; ibShadowedToolTip = "Reveal in Finder"; ObjectID = "GPh-5t-rfW"; */ +"GPh-5t-rfW.ibShadowedToolTip" = "Afficher dans le Finder"; + +/* Class = "NSButtonCell"; title = "Recurse into Applications"; ObjectID = "HUW-pw-w0O"; */ +"HUW-pw-w0O.title" = "Analyser Récursivement les Applications"; + +/* Class = "NSButtonCell"; title = "Start Scanning"; ObjectID = "MEb-Ef-ukk"; */ +"MEb-Ef-ukk.title" = "Lancer l'analyse"; + +/* Class = "NSTextFieldCell"; title = "Applications Found:"; ObjectID = "MJD-Yn-xAC"; */ +"MJD-Yn-xAC.title" = "Applications Trouvées :"; + +/* Class = "NSMenuItem"; title = "Show All Apps"; ObjectID = "QoR-x6-4UR"; */ +"QoR-x6-4UR.title" = "Afficher Toutes les Apps"; + +/* Class = "NSButtonCell"; title = "Only Scan the Applications Folder"; ObjectID = "b2W-HI-GII"; */ +"b2W-HI-GII.title" = "Analyser Uniquement le Répertoire Applications"; + +/* Class = "NSButtonCell"; title = "Exclude Apple Applications"; ObjectID = "d0l-gf-yml"; */ +"d0l-gf-yml.title" = "Exclure les Applications Apple"; + +/* Class = "NSMenuItem"; title = "Show Apple SIlicon Compatible Apps"; ObjectID = "uJw-yo-gLb"; */ +"uJw-yo-gLb.title" = "Afficher les Apps Compatibles Apple Silicon"; + +/* Class = "NSMenuItem"; title = "Show Intel-Only Apps"; ObjectID = "ux4-9b-ut4"; */ +"ux4-9b-ut4.title" = "Afficher les Apps Seulement Intel";