Skip to content

Commit

Permalink
Compress image assets and support XML export for VMware Fusion users.
Browse files Browse the repository at this point in the history
  • Loading branch information
cormiertyshawn895 committed Jan 21, 2020
1 parent 8fa97ed commit 2509eb4
Show file tree
Hide file tree
Showing 59 changed files with 938 additions and 22 deletions.
16 changes: 16 additions & 0 deletions README.md
Expand Up @@ -109,6 +109,22 @@ After successfully modifying or installing the app, you can play with it to your

---

### Workaround to install Final Cut Pro 7 in VMware Fusion

If you don't have a Mac released before Late 2019, you can still install Final Cut Pro 7 in VMware Fusion to export existing Final Cut Pro 7 projects into XML files. This lets SendToX, DaVinci Resolve, Media Composer, and Premiere Pro open them.

- [Install macOS Mojave virtual machine with VMware Fusion](https://www.huibdijkstra.nl/how-to-set-up-a-osx-mojave-vm-in-vmware-fusion/). Other virtual machine software, such as Parallels Desktop, is not supported and will not work.
- After mounting the Final Cut Studio installer in VMware Fusion, right click on Install Final Cut Studio.pkg > Show Original, then copy FinalCutStudio.mpkg and Packages to VMware Fusion's Desktop
- Right click on the copied FinalCutStudio.mpkg > Show Package Contents > Resources
- Right click on Requirements Checker.app > Show Package Contents > Contents > Resources
- Delete minsys.plist
- Double click on the modified FinalCutStudio.mpkg to start the install
- Use Retroactive as usual

To use editing features such as timeline and preview, install macOS Mojave on a real, older Mac released before Late 2019, then run Retroactive as usual.

---

### Last words
- If GateKeeper prevents you from running modified versions of your chosen app, [temporarily disable GateKeeper in Terminal with `sudo spctl --master-disable`](http://osxdaily.com/2015/05/04/disable-gatekeeper-command-line-mac-osx/).

Expand Down
8 changes: 6 additions & 2 deletions Retroactive.xcodeproj/project.pbxproj
Expand Up @@ -60,6 +60,7 @@
606CA155236666B3001C550E /* ConfettiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606CA151236666B3001C550E /* ConfettiView.swift */; };
607C3D822382665900FAE528 /* AudioToolbox.framework.zip in Resources */ = {isa = PBXBuildFile; fileRef = 607C3D812382665900FAE528 /* AudioToolbox.framework.zip */; };
60A58ABF2366318D00FFC1A3 /* SupportPath.plist in Resources */ = {isa = PBXBuildFile; fileRef = 32F763432364C8C000200ED7 /* SupportPath.plist */; };
60D728A023D5787B0006F233 /* VMFCPFixerScript in Resources */ = {isa = PBXBuildFile; fileRef = 60D7289F23D5787B0006F233 /* VMFCPFixerScript */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -129,6 +130,7 @@
607C3D7F2382409B00FAE528 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
607C3D802382409B00FAE528 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
607C3D812382665900FAE528 /* AudioToolbox.framework.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = AudioToolbox.framework.zip; sourceTree = "<group>"; };
60D7289F23D5787B0006F233 /* VMFCPFixerScript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = VMFCPFixerScript; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -214,6 +216,7 @@
606CA12023664BFC001C550E /* killpkg */,
046034E32381B6E800070C86 /* KeynoteScript */,
042E9E472381AC2B001D3F38 /* GeneralFixerScript */,
60D7289F23D5787B0006F233 /* VMFCPFixerScript */,
607C3D812382665900FAE528 /* AudioToolbox.framework.zip */,
046034E52381C18900070C86 /* BrowserKit.framework.zip */,
046034E62381C18A00070C86 /* ProKit.framework.zip */,
Expand Down Expand Up @@ -386,6 +389,7 @@
606CA154236666B3001C550E /* ConfettiView.bundle in Resources */,
046034E72381C18A00070C86 /* BrowserKit.framework.zip in Resources */,
046034EA2381DDB300070C86 /* ProApplicationsUpdate2010-02.dist in Resources */,
60D728A023D5787B0006F233 /* VMFCPFixerScript in Resources */,
042E9E4B2381AC2C001D3F38 /* GeneralFixerScript in Resources */,
32F763462364C9F800200ED7 /* insert_dylib in Resources */,
607C3D822382665900FAE528 /* AudioToolbox.framework.zip in Resources */,
Expand Down Expand Up @@ -602,7 +606,7 @@
CODE_SIGN_ENTITLEMENTS = Retroactive/Support/Retroactive.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 16;
CURRENT_PROJECT_VERSION = 21;
INFOPLIST_FILE = Retroactive/Support/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -623,7 +627,7 @@
CODE_SIGN_ENTITLEMENTS = Retroactive/Support/Retroactive.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 16;
CURRENT_PROJECT_VERSION = 21;
INFOPLIST_FILE = Retroactive/Support/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down
242 changes: 241 additions & 1 deletion Retroactive/AppManager.swift
Expand Up @@ -22,6 +22,12 @@ enum iTunesVersion {
case coverFlow
}

enum VirtualMachine {
case parallels
case vmware
case generic
}

let kCFBundleIdentifier = "CFBundleIdentifier"
let kCFBundleVersion = "CFBundleVersion"
let kCFBundleShortVersionString = "CFBundleShortVersionString"
Expand All @@ -36,6 +42,8 @@ let actionPresentTenseToken = "{actionPR}"

let kCustomSettingsPath = "/Library/Application Support/Final Cut Pro System Support/Custom Settings"

let lastHWForMojave = ["iMac19,2", "iMacPro1,1", "MacBook10,1", "MacBookAir8,2", "MacBookPro15,4", "Macmini8,1", "MacPro6,1"]

extension Bundle {
var cfBundleVersionInt: Int? {
get {
Expand Down Expand Up @@ -65,6 +73,57 @@ extension NSObject {
}
}

extension String {
var machineType: String? {
return self.components(separatedBy: .decimalDigits).first
}

var machineFullNumber: String? {
if let modelType = self.machineType {
return self.replacingOccurrences(of: modelType, with: "")
}
return nil
}

var machineGenerationNumber: Int? {
if let fullNumber = self.machineFullNumber {
let seperated = fullNumber.components(separatedBy: ",")
if seperated.count > 1 {
if let intGen = Int(seperated[0]) {
return intGen
}
}
}
return nil
}

var machineSubmodelNumber: Int? {
if let fullNumber = self.machineFullNumber {
let seperated = fullNumber.components(separatedBy: ",")
if seperated.count > 1 {
if let intSub = Int(seperated[1]) {
return intSub
}
}
}
return nil
}

func isNewerThan(otherMachine: String) -> Bool {
if (self.machineType == otherMachine.machineType) {
if let thisGen = self.machineGenerationNumber, let thisSub = self.machineSubmodelNumber, let otherGen = otherMachine.machineGenerationNumber, let otherSub = otherMachine.machineSubmodelNumber {
if thisGen > otherGen {
return true
}
if thisGen == otherGen && thisSub > otherSub {
return true
}
}
}
return false
}
}

class AppManager: NSObject {

static let shared = AppManager()
Expand Down Expand Up @@ -514,7 +573,13 @@ class AppManager: NSObject {
case .itunes:
fatalError()
case .finalCutPro7:
return "GeneralFixerScript"
if self.likelyInVirtualMachine {
print("In VM and FCP7, using VM fixer script")
return "VMFCPFixerScript"
} else {
print("Normal FCP7, using general fixer script")
return "GeneralFixerScript"
}
case .logicPro9:
return "GeneralFixerScript"
case .keynote5:
Expand Down Expand Up @@ -907,5 +972,180 @@ class AppManager: NSObject {
.replacingOccurrences(of: actionDetailToken, with: AppManager.shared.detailActionOfChosenApp)
.replacingOccurrences(of: systemNameToken, with: ProcessInfo.versionName)
}

var platform: String {
get {
var size = 0
sysctlbyname("hw.model", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.model", &machine, &size, nil, 0)
return String(cString: machine)
}
}

var platformShippedAfterMojave: Bool {
let hwIdentifier = self.platform
var machineTypeMatchedAtLeastOnce = false
for mac in lastHWForMojave {
if hwIdentifier.isNewerThan(otherMachine: mac) {
return true
}
if hwIdentifier.machineType == mac.machineType {
machineTypeMatchedAtLeastOnce = true
}
}
return !machineTypeMatchedAtLeastOnce
}

var chosenAppHasLimitedFeaturesInVirtualMachine: Bool {
get {
switch self.chosenApp {
case .aperture:
return true
case .iphoto:
return true
case .proVideoUpdate:
fallthrough
case .finalCutPro7:
return true
case .keynote5:
return true
case .logicPro9:
return false
case .itunes:
return false
default:
return false
}
}
}

var currentVM: VirtualMachine {
let platform = self.platform
if (platform.contains("VMware")) {
return .vmware
}
if (platform.contains("Parallels")) {
return .parallels
}
return .generic
}

var currentVMName: String {
switch self.currentVM {
case .vmware:
return "VMware Fusion".localized()
case .parallels:
return "Parallels Desktop".localized()
case .generic:
fallthrough
default:
return "a virtual machine".localized()
}
}

var currentVMImage: NSImage? {
switch self.currentVM {
case .vmware:
return NSImage(named: "vmware")
case .parallels:
return NSImage(named: "parallels")
case .generic:
fallthrough
default:
return NSImage(named: "generic-vm")
}
}

var chosenAppVMTitle: String {
get {
let basicFormat = "%1$@ in %2$@".localized()
switch self.chosenApp {
case .aperture:
return String(format: basicFormat, "Aperture has reduced functionality".localized(), self.currentVMName)
case .iphoto:
return String(format: basicFormat, "iPhoto has reduced functionality".localized(), self.currentVMName)
case .proVideoUpdate:
fallthrough
case .finalCutPro7:
return String(format: basicFormat, "Final Cut Pro 7 only supports XML exports".localized(), self.currentVMName)
case .keynote5:
return String(format: basicFormat, "Keynote ’09 only supports PPTX exports".localized(), self.currentVMName)
case .logicPro9:
return ""
case .itunes:
return ""
default:
return ""
}
}
}

var chosenAppVMDescription: String {
get {
switch self.chosenApp {
case .aperture:
return "You can open and organize your Aperture library, or export edited images. To use editing features such as image preview, adjustments, brushes, and effects, run Retroactive on a real Mac.".localized()
case .iphoto:
return "You can open and organize your iPhoto library, or export edited images. To use editing features such as image preview, Quick Fixes, effects, and adjustments, run Retroactive on a real Mac.".localized()
case .proVideoUpdate:
fallthrough
case .finalCutPro7:
return "You can export existing projects into XML files, so that SendToX, DaVinci Resolve, Media Composer, and Premiere Pro can open them. To use editing features such as timeline and preview, run Retroactive on a real Mac.".localized()
case .keynote5:
return "You can export existing Keynote presentations into PowerPoint presentations. To view and edit your Keynote slides, animations, and transitions, run Retroactive on a real Mac.".localized()
case .logicPro9:
return ""
case .itunes:
return ""
default:
return ""
}
}
}

private var _previouslyDetectedInVM: Bool = false

var likelyInVirtualMachine: Bool {
get {
if _previouslyDetectedInVM == true {
print("previously already detected in VM, skipping the check")
return true
}
print("main window is \(String(describing: NSApp.mainWindow)), screen is \(String(describing: NSApp.mainWindow?.screen))")
var window = NSApp.mainWindow
if (window == nil) {
let subwindows = NSApp.windows
print("main window is nil, NSApp.windows are \(subwindows)")
for subwindow in subwindows {
if let validWindow = subwindow as? RetroactiveWindow {
window = validWindow
}
}
if (window == nil && subwindows.count > 0) {
window = subwindows[0]
}
}
let deviceDescription = window?.screen?.deviceDescription
print("device description is \(String(describing: deviceDescription))")
if let description = deviceDescription {
print("screen number is \(String(describing: description[NSDeviceDescriptionKey("NSScreenNumber")]))")
if let screenNumber = description[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID {
let usesOpenGL = CGDisplayUsesOpenGLAcceleration(screenNumber)
print("usesOpenGL is \(usesOpenGL)")
if CGDisplayUsesOpenGLAcceleration(screenNumber) == 1 {
print("OpenGL acceleration present, likely not in virtual machine")
return false
} else {
print("OpenGL acceleration missing, likely in virtual machine")
_previouslyDetectedInVM = true
return true
}
}
}
print("can't find current screen, assuming not in virtual machine")
return false
}
}

}

0 comments on commit 2509eb4

Please sign in to comment.