├── .gitignore
├── LICENSE.md
├── Microverse.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ └── Microverse.xcscheme
├── Microverse
├── Application
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Info.plist
│ ├── Microverse.entitlements
│ ├── MicroverseApp.swift
│ ├── MicroverseDocument.swift
│ └── Preview Content
│ │ └── Preview Assets.xcassets
│ │ └── Contents.json
├── Extensions
│ ├── KeyedCodingExtensions.swift
│ └── NSWindowExtensions.swift
├── Model
│ ├── AttachedDiskImage.swift
│ ├── ConfigurableVirtualMachine.swift
│ ├── MacOSVirtualMachine.swift
│ ├── MicroverseErrors.swift
│ ├── VirtualMachine.swift
│ ├── VirtualMachineConfiguration.swift
│ └── VirtualMachineController.swift
└── View
│ ├── AttachedDisksView.swift
│ ├── DiskCreationView.swift
│ ├── DocumentView.swift
│ ├── MacAuxiliaryStorageView.swift
│ ├── MacOSDocumentView.swift
│ ├── MacOSInstallView.swift
│ ├── MacRestoreImageView.swift
│ ├── PathField.swift
│ ├── VirtualMachineConfigurationView.swift
│ └── VirtualMachineView.swift
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2021 [Justin Spahr-Summers](https://jspahrsummers.com/).
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | ## SwiftTerm
24 |
25 | [SwiftTerm](https://github.com/migueldeicaza/SwiftTerm), released under the [MIT license](https://github.com/migueldeicaza/SwiftTerm/blob/731731c0290d03c96615ea34187fbbb1cfe3d852/LICENSE), is used as a dependency.
26 |
27 | Copyright (c) 2019-2021 Miguel de Icaza (https://github.com/migueldeicaza)
28 | Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
29 | Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
30 | Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
31 |
32 | ## @KhaosT
33 |
34 | Portions of this project were inspired by:
35 | * [KhaosT/MacVM](https://github.com/KhaosT/MacVM), released under the [Apache License 2.0](https://github.com/KhaosT/MacVM/blob/1c351e2e54c2988698d8540abadc0c5c80ada403/LICENSE).
36 | * [KhaosT/SimpleVM](https://github.com/KhaosT/SimpleVM), released under the [Apache License 2.0](https://github.com/KhaosT/SimpleVM/blob/9c04d4719f4ccb387ea34cf493ddf08aedbf1ab8/LICENSE).
37 |
--------------------------------------------------------------------------------
/Microverse.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 55;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3526598E26BFEFBF001D5029 /* VirtualMachineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3526598D26BFEFBF001D5029 /* VirtualMachineController.swift */; };
11 | 3526599026BFF2E2001D5029 /* MicroverseErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3526598F26BFF2E2001D5029 /* MicroverseErrors.swift */; };
12 | 352AF07626BAFDD700AFD751 /* PathField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 352AF07526BAFDD700AFD751 /* PathField.swift */; };
13 | 352AF07A26BB026900AFD751 /* MacRestoreImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 352AF07926BB026900AFD751 /* MacRestoreImageView.swift */; };
14 | 357457C426C044E700BFA2D7 /* KeyedCodingExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 357457C326C044E700BFA2D7 /* KeyedCodingExtensions.swift */; };
15 | 359B9B0626B8AAC100AA3989 /* MicroverseDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359B9B0526B8AAC100AA3989 /* MicroverseDocument.swift */; };
16 | 35C1E9EE26BF11E1005F47D0 /* AttachedDiskImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C1E9ED26BF11E1005F47D0 /* AttachedDiskImage.swift */; };
17 | 35CF8D6126B8300F00E83536 /* MicroverseApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D6026B8300F00E83536 /* MicroverseApp.swift */; };
18 | 35CF8D6526B8300F00E83536 /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D6426B8300F00E83536 /* DocumentView.swift */; };
19 | 35CF8D6726B8301000E83536 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 35CF8D6626B8301000E83536 /* Assets.xcassets */; };
20 | 35CF8D6A26B8301000E83536 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 35CF8D6926B8301000E83536 /* Preview Assets.xcassets */; };
21 | 35CF8D7626B836A900E83536 /* VirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D7526B836A900E83536 /* VirtualMachine.swift */; };
22 | 35CF8D7A26B8421B00E83536 /* VirtualMachineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D7926B8421B00E83536 /* VirtualMachineView.swift */; };
23 | 35CF8D7C26B8431300E83536 /* VirtualMachineConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D7B26B8431300E83536 /* VirtualMachineConfigurationView.swift */; };
24 | 35CF8D7E26B855E200E83536 /* VirtualMachineConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D7D26B855E200E83536 /* VirtualMachineConfiguration.swift */; };
25 | 35CF8D8026B8704200E83536 /* DiskCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D7F26B8704200E83536 /* DiskCreationView.swift */; };
26 | 35CF8D8726B8837500E83536 /* AttachedDisksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D8626B8837500E83536 /* AttachedDisksView.swift */; };
27 | 35CF8D8E26B89E9B00E83536 /* NSWindowExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF8D8D26B89E9B00E83536 /* NSWindowExtensions.swift */; };
28 | 35FE87D826BEBD62009B9B51 /* MacOSDocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FE87D726BEBD62009B9B51 /* MacOSDocumentView.swift */; };
29 | 35FE87DC26BEC049009B9B51 /* MacOSVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FE87DB26BEC049009B9B51 /* MacOSVirtualMachine.swift */; };
30 | 35FE87DE26BEC05C009B9B51 /* ConfigurableVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FE87DD26BEC05C009B9B51 /* ConfigurableVirtualMachine.swift */; };
31 | 35FE87E026BECD2E009B9B51 /* MacOSInstallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FE87DF26BECD2E009B9B51 /* MacOSInstallView.swift */; };
32 | 35FE87E226BEE980009B9B51 /* MacAuxiliaryStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FE87E126BEE980009B9B51 /* MacAuxiliaryStorageView.swift */; };
33 | /* End PBXBuildFile section */
34 |
35 | /* Begin PBXFileReference section */
36 | 3526598D26BFEFBF001D5029 /* VirtualMachineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineController.swift; sourceTree = ""; };
37 | 3526598F26BFF2E2001D5029 /* MicroverseErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MicroverseErrors.swift; sourceTree = ""; };
38 | 352AF07526BAFDD700AFD751 /* PathField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathField.swift; sourceTree = ""; };
39 | 352AF07926BB026900AFD751 /* MacRestoreImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacRestoreImageView.swift; sourceTree = ""; };
40 | 357457C326C044E700BFA2D7 /* KeyedCodingExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedCodingExtensions.swift; sourceTree = ""; };
41 | 359B9B0526B8AAC100AA3989 /* MicroverseDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MicroverseDocument.swift; sourceTree = ""; };
42 | 35C1E9ED26BF11E1005F47D0 /* AttachedDiskImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachedDiskImage.swift; sourceTree = ""; };
43 | 35CF8D5D26B8300F00E83536 /* Microverse.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Microverse.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 35CF8D6026B8300F00E83536 /* MicroverseApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MicroverseApp.swift; sourceTree = ""; };
45 | 35CF8D6426B8300F00E83536 /* DocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView.swift; sourceTree = ""; };
46 | 35CF8D6626B8301000E83536 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
47 | 35CF8D6926B8301000E83536 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
48 | 35CF8D6B26B8301000E83536 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
49 | 35CF8D7526B836A900E83536 /* VirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachine.swift; sourceTree = ""; };
50 | 35CF8D7926B8421B00E83536 /* VirtualMachineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineView.swift; sourceTree = ""; };
51 | 35CF8D7B26B8431300E83536 /* VirtualMachineConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineConfigurationView.swift; sourceTree = ""; };
52 | 35CF8D7D26B855E200E83536 /* VirtualMachineConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineConfiguration.swift; sourceTree = ""; };
53 | 35CF8D7F26B8704200E83536 /* DiskCreationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskCreationView.swift; sourceTree = ""; };
54 | 35CF8D8326B8829500E83536 /* Microverse.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Microverse.entitlements; sourceTree = ""; };
55 | 35CF8D8626B8837500E83536 /* AttachedDisksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachedDisksView.swift; sourceTree = ""; };
56 | 35CF8D8D26B89E9B00E83536 /* NSWindowExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSWindowExtensions.swift; sourceTree = ""; };
57 | 35FE87D726BEBD62009B9B51 /* MacOSDocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacOSDocumentView.swift; sourceTree = ""; };
58 | 35FE87DB26BEC049009B9B51 /* MacOSVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacOSVirtualMachine.swift; sourceTree = ""; };
59 | 35FE87DD26BEC05C009B9B51 /* ConfigurableVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurableVirtualMachine.swift; sourceTree = ""; };
60 | 35FE87DF26BECD2E009B9B51 /* MacOSInstallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacOSInstallView.swift; sourceTree = ""; };
61 | 35FE87E126BEE980009B9B51 /* MacAuxiliaryStorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAuxiliaryStorageView.swift; sourceTree = ""; };
62 | /* End PBXFileReference section */
63 |
64 | /* Begin PBXFrameworksBuildPhase section */
65 | 35CF8D5A26B8300F00E83536 /* Frameworks */ = {
66 | isa = PBXFrameworksBuildPhase;
67 | buildActionMask = 2147483647;
68 | files = (
69 | );
70 | runOnlyForDeploymentPostprocessing = 0;
71 | };
72 | /* End PBXFrameworksBuildPhase section */
73 |
74 | /* Begin PBXGroup section */
75 | 352AF07026BAFCCE00AFD751 /* View */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 35CF8D8626B8837500E83536 /* AttachedDisksView.swift */,
79 | 35CF8D7F26B8704200E83536 /* DiskCreationView.swift */,
80 | 35CF8D6426B8300F00E83536 /* DocumentView.swift */,
81 | 35FE87E126BEE980009B9B51 /* MacAuxiliaryStorageView.swift */,
82 | 35FE87D726BEBD62009B9B51 /* MacOSDocumentView.swift */,
83 | 35FE87DF26BECD2E009B9B51 /* MacOSInstallView.swift */,
84 | 352AF07926BB026900AFD751 /* MacRestoreImageView.swift */,
85 | 352AF07526BAFDD700AFD751 /* PathField.swift */,
86 | 35CF8D7B26B8431300E83536 /* VirtualMachineConfigurationView.swift */,
87 | 35CF8D7926B8421B00E83536 /* VirtualMachineView.swift */,
88 | );
89 | path = View;
90 | sourceTree = "";
91 | };
92 | 352AF07126BAFCE500AFD751 /* Extensions */ = {
93 | isa = PBXGroup;
94 | children = (
95 | 357457C326C044E700BFA2D7 /* KeyedCodingExtensions.swift */,
96 | 35CF8D8D26B89E9B00E83536 /* NSWindowExtensions.swift */,
97 | );
98 | path = Extensions;
99 | sourceTree = "";
100 | };
101 | 352AF07226BAFCEB00AFD751 /* Application */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 35CF8D6626B8301000E83536 /* Assets.xcassets */,
105 | 35CF8D6B26B8301000E83536 /* Info.plist */,
106 | 35CF8D8326B8829500E83536 /* Microverse.entitlements */,
107 | 35CF8D6026B8300F00E83536 /* MicroverseApp.swift */,
108 | 359B9B0526B8AAC100AA3989 /* MicroverseDocument.swift */,
109 | 35CF8D6826B8301000E83536 /* Preview Content */,
110 | );
111 | path = Application;
112 | sourceTree = "";
113 | };
114 | 352AF07426BAFCFE00AFD751 /* Model */ = {
115 | isa = PBXGroup;
116 | children = (
117 | 35C1E9ED26BF11E1005F47D0 /* AttachedDiskImage.swift */,
118 | 35FE87DD26BEC05C009B9B51 /* ConfigurableVirtualMachine.swift */,
119 | 35FE87DB26BEC049009B9B51 /* MacOSVirtualMachine.swift */,
120 | 3526598F26BFF2E2001D5029 /* MicroverseErrors.swift */,
121 | 35CF8D7526B836A900E83536 /* VirtualMachine.swift */,
122 | 35CF8D7D26B855E200E83536 /* VirtualMachineConfiguration.swift */,
123 | 3526598D26BFEFBF001D5029 /* VirtualMachineController.swift */,
124 | );
125 | path = Model;
126 | sourceTree = "";
127 | };
128 | 35CF8D5426B8300F00E83536 = {
129 | isa = PBXGroup;
130 | children = (
131 | 35CF8D5F26B8300F00E83536 /* Microverse */,
132 | 35CF8D5E26B8300F00E83536 /* Products */,
133 | );
134 | sourceTree = "";
135 | };
136 | 35CF8D5E26B8300F00E83536 /* Products */ = {
137 | isa = PBXGroup;
138 | children = (
139 | 35CF8D5D26B8300F00E83536 /* Microverse.app */,
140 | );
141 | name = Products;
142 | sourceTree = "";
143 | };
144 | 35CF8D5F26B8300F00E83536 /* Microverse */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 352AF07226BAFCEB00AFD751 /* Application */,
148 | 352AF07126BAFCE500AFD751 /* Extensions */,
149 | 352AF07426BAFCFE00AFD751 /* Model */,
150 | 352AF07026BAFCCE00AFD751 /* View */,
151 | );
152 | path = Microverse;
153 | sourceTree = "";
154 | };
155 | 35CF8D6826B8301000E83536 /* Preview Content */ = {
156 | isa = PBXGroup;
157 | children = (
158 | 35CF8D6926B8301000E83536 /* Preview Assets.xcassets */,
159 | );
160 | path = "Preview Content";
161 | sourceTree = "";
162 | };
163 | /* End PBXGroup section */
164 |
165 | /* Begin PBXNativeTarget section */
166 | 35CF8D5C26B8300F00E83536 /* Microverse */ = {
167 | isa = PBXNativeTarget;
168 | buildConfigurationList = 35CF8D6E26B8301000E83536 /* Build configuration list for PBXNativeTarget "Microverse" */;
169 | buildPhases = (
170 | 35CF8D5926B8300F00E83536 /* Sources */,
171 | 35CF8D5A26B8300F00E83536 /* Frameworks */,
172 | 35CF8D5B26B8300F00E83536 /* Resources */,
173 | );
174 | buildRules = (
175 | );
176 | dependencies = (
177 | );
178 | name = Microverse;
179 | packageProductDependencies = (
180 | );
181 | productName = Microverse;
182 | productReference = 35CF8D5D26B8300F00E83536 /* Microverse.app */;
183 | productType = "com.apple.product-type.application";
184 | };
185 | /* End PBXNativeTarget section */
186 |
187 | /* Begin PBXProject section */
188 | 35CF8D5526B8300F00E83536 /* Project object */ = {
189 | isa = PBXProject;
190 | attributes = {
191 | BuildIndependentTargetsInParallel = 1;
192 | LastSwiftUpdateCheck = 1300;
193 | LastUpgradeCheck = 1300;
194 | TargetAttributes = {
195 | 35CF8D5C26B8300F00E83536 = {
196 | CreatedOnToolsVersion = 13.0;
197 | };
198 | };
199 | };
200 | buildConfigurationList = 35CF8D5826B8300F00E83536 /* Build configuration list for PBXProject "Microverse" */;
201 | compatibilityVersion = "Xcode 13.0";
202 | developmentRegion = en;
203 | hasScannedForEncodings = 0;
204 | knownRegions = (
205 | en,
206 | Base,
207 | );
208 | mainGroup = 35CF8D5426B8300F00E83536;
209 | packageReferences = (
210 | );
211 | productRefGroup = 35CF8D5E26B8300F00E83536 /* Products */;
212 | projectDirPath = "";
213 | projectRoot = "";
214 | targets = (
215 | 35CF8D5C26B8300F00E83536 /* Microverse */,
216 | );
217 | };
218 | /* End PBXProject section */
219 |
220 | /* Begin PBXResourcesBuildPhase section */
221 | 35CF8D5B26B8300F00E83536 /* Resources */ = {
222 | isa = PBXResourcesBuildPhase;
223 | buildActionMask = 2147483647;
224 | files = (
225 | 35CF8D6A26B8301000E83536 /* Preview Assets.xcassets in Resources */,
226 | 35CF8D6726B8301000E83536 /* Assets.xcassets in Resources */,
227 | );
228 | runOnlyForDeploymentPostprocessing = 0;
229 | };
230 | /* End PBXResourcesBuildPhase section */
231 |
232 | /* Begin PBXSourcesBuildPhase section */
233 | 35CF8D5926B8300F00E83536 /* Sources */ = {
234 | isa = PBXSourcesBuildPhase;
235 | buildActionMask = 2147483647;
236 | files = (
237 | 35CF8D7626B836A900E83536 /* VirtualMachine.swift in Sources */,
238 | 35CF8D7C26B8431300E83536 /* VirtualMachineConfigurationView.swift in Sources */,
239 | 357457C426C044E700BFA2D7 /* KeyedCodingExtensions.swift in Sources */,
240 | 359B9B0626B8AAC100AA3989 /* MicroverseDocument.swift in Sources */,
241 | 35CF8D6126B8300F00E83536 /* MicroverseApp.swift in Sources */,
242 | 35CF8D8E26B89E9B00E83536 /* NSWindowExtensions.swift in Sources */,
243 | 35FE87D826BEBD62009B9B51 /* MacOSDocumentView.swift in Sources */,
244 | 35FE87DE26BEC05C009B9B51 /* ConfigurableVirtualMachine.swift in Sources */,
245 | 35FE87E226BEE980009B9B51 /* MacAuxiliaryStorageView.swift in Sources */,
246 | 35CF8D7A26B8421B00E83536 /* VirtualMachineView.swift in Sources */,
247 | 35FE87E026BECD2E009B9B51 /* MacOSInstallView.swift in Sources */,
248 | 35CF8D8026B8704200E83536 /* DiskCreationView.swift in Sources */,
249 | 35CF8D8726B8837500E83536 /* AttachedDisksView.swift in Sources */,
250 | 35C1E9EE26BF11E1005F47D0 /* AttachedDiskImage.swift in Sources */,
251 | 3526599026BFF2E2001D5029 /* MicroverseErrors.swift in Sources */,
252 | 35CF8D7E26B855E200E83536 /* VirtualMachineConfiguration.swift in Sources */,
253 | 3526598E26BFEFBF001D5029 /* VirtualMachineController.swift in Sources */,
254 | 35FE87DC26BEC049009B9B51 /* MacOSVirtualMachine.swift in Sources */,
255 | 352AF07A26BB026900AFD751 /* MacRestoreImageView.swift in Sources */,
256 | 352AF07626BAFDD700AFD751 /* PathField.swift in Sources */,
257 | 35CF8D6526B8300F00E83536 /* DocumentView.swift in Sources */,
258 | );
259 | runOnlyForDeploymentPostprocessing = 0;
260 | };
261 | /* End PBXSourcesBuildPhase section */
262 |
263 | /* Begin XCBuildConfiguration section */
264 | 35CF8D6C26B8301000E83536 /* Debug */ = {
265 | isa = XCBuildConfiguration;
266 | buildSettings = {
267 | ALWAYS_SEARCH_USER_PATHS = NO;
268 | ARCHS = arm64;
269 | CLANG_ANALYZER_NONNULL = YES;
270 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
272 | CLANG_CXX_LIBRARY = "libc++";
273 | CLANG_ENABLE_MODULES = YES;
274 | CLANG_ENABLE_OBJC_ARC = YES;
275 | CLANG_ENABLE_OBJC_WEAK = YES;
276 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
277 | CLANG_WARN_BOOL_CONVERSION = YES;
278 | CLANG_WARN_COMMA = YES;
279 | CLANG_WARN_CONSTANT_CONVERSION = YES;
280 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
281 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
282 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
283 | CLANG_WARN_EMPTY_BODY = YES;
284 | CLANG_WARN_ENUM_CONVERSION = YES;
285 | CLANG_WARN_INFINITE_RECURSION = YES;
286 | CLANG_WARN_INT_CONVERSION = YES;
287 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
288 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
289 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
290 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
291 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
292 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
293 | CLANG_WARN_STRICT_PROTOTYPES = YES;
294 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
295 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
296 | CLANG_WARN_UNREACHABLE_CODE = YES;
297 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
298 | COPY_PHASE_STRIP = NO;
299 | DEBUG_INFORMATION_FORMAT = dwarf;
300 | ENABLE_STRICT_OBJC_MSGSEND = YES;
301 | ENABLE_TESTABILITY = YES;
302 | GCC_C_LANGUAGE_STANDARD = gnu11;
303 | GCC_DYNAMIC_NO_PIC = NO;
304 | GCC_NO_COMMON_BLOCKS = YES;
305 | GCC_OPTIMIZATION_LEVEL = 0;
306 | GCC_PREPROCESSOR_DEFINITIONS = (
307 | "DEBUG=1",
308 | "$(inherited)",
309 | );
310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
312 | GCC_WARN_UNDECLARED_SELECTOR = YES;
313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
314 | GCC_WARN_UNUSED_FUNCTION = YES;
315 | GCC_WARN_UNUSED_VARIABLE = YES;
316 | MACOSX_DEPLOYMENT_TARGET = 12.0;
317 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
318 | MTL_FAST_MATH = YES;
319 | ONLY_ACTIVE_ARCH = YES;
320 | SDKROOT = macosx;
321 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
322 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
323 | };
324 | name = Debug;
325 | };
326 | 35CF8D6D26B8301000E83536 /* Release */ = {
327 | isa = XCBuildConfiguration;
328 | buildSettings = {
329 | ALWAYS_SEARCH_USER_PATHS = NO;
330 | ARCHS = arm64;
331 | CLANG_ANALYZER_NONNULL = YES;
332 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_ENABLE_OBJC_WEAK = YES;
338 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
339 | CLANG_WARN_BOOL_CONVERSION = YES;
340 | CLANG_WARN_COMMA = YES;
341 | CLANG_WARN_CONSTANT_CONVERSION = YES;
342 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
344 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
345 | CLANG_WARN_EMPTY_BODY = YES;
346 | CLANG_WARN_ENUM_CONVERSION = YES;
347 | CLANG_WARN_INFINITE_RECURSION = YES;
348 | CLANG_WARN_INT_CONVERSION = YES;
349 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
351 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
353 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
354 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
355 | CLANG_WARN_STRICT_PROTOTYPES = YES;
356 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
357 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
358 | CLANG_WARN_UNREACHABLE_CODE = YES;
359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
360 | COPY_PHASE_STRIP = NO;
361 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
362 | ENABLE_NS_ASSERTIONS = NO;
363 | ENABLE_STRICT_OBJC_MSGSEND = YES;
364 | GCC_C_LANGUAGE_STANDARD = gnu11;
365 | GCC_NO_COMMON_BLOCKS = YES;
366 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
367 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
368 | GCC_WARN_UNDECLARED_SELECTOR = YES;
369 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
370 | GCC_WARN_UNUSED_FUNCTION = YES;
371 | GCC_WARN_UNUSED_VARIABLE = YES;
372 | MACOSX_DEPLOYMENT_TARGET = 12.0;
373 | MTL_ENABLE_DEBUG_INFO = NO;
374 | MTL_FAST_MATH = YES;
375 | SDKROOT = macosx;
376 | SWIFT_COMPILATION_MODE = wholemodule;
377 | SWIFT_OPTIMIZATION_LEVEL = "-O";
378 | };
379 | name = Release;
380 | };
381 | 35CF8D6F26B8301000E83536 /* Debug */ = {
382 | isa = XCBuildConfiguration;
383 | buildSettings = {
384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
385 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
386 | CODE_SIGN_ENTITLEMENTS = Microverse/Application/Microverse.entitlements;
387 | CODE_SIGN_STYLE = Automatic;
388 | COMBINE_HIDPI_IMAGES = YES;
389 | CURRENT_PROJECT_VERSION = 1;
390 | DEVELOPMENT_ASSET_PATHS = "\"Microverse/Application/Preview Content\"";
391 | ENABLE_APP_SANDBOX = YES;
392 | ENABLE_HARDENED_RUNTIME = YES;
393 | ENABLE_PREVIEWS = YES;
394 | ENABLE_USER_SELECTED_FILES = readwrite;
395 | GENERATE_INFOPLIST_FILE = YES;
396 | INFOPLIST_FILE = Microverse/Application/Info.plist;
397 | INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Justin Spahr-Summers. Released under the MIT license.";
398 | LD_RUNPATH_SEARCH_PATHS = (
399 | "$(inherited)",
400 | "@executable_path/../Frameworks",
401 | );
402 | MACOSX_DEPLOYMENT_TARGET = 12.0;
403 | MARKETING_VERSION = 0.2;
404 | PRODUCT_BUNDLE_IDENTIFIER = com.metacognitive.Microverse;
405 | PRODUCT_NAME = "$(TARGET_NAME)";
406 | SWIFT_EMIT_LOC_STRINGS = YES;
407 | SWIFT_VERSION = 5.0;
408 | };
409 | name = Debug;
410 | };
411 | 35CF8D7026B8301000E83536 /* Release */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
415 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
416 | CODE_SIGN_ENTITLEMENTS = Microverse/Application/Microverse.entitlements;
417 | CODE_SIGN_STYLE = Automatic;
418 | COMBINE_HIDPI_IMAGES = YES;
419 | CURRENT_PROJECT_VERSION = 1;
420 | DEVELOPMENT_ASSET_PATHS = "\"Microverse/Application/Preview Content\"";
421 | ENABLE_APP_SANDBOX = YES;
422 | ENABLE_HARDENED_RUNTIME = YES;
423 | ENABLE_PREVIEWS = YES;
424 | ENABLE_USER_SELECTED_FILES = readwrite;
425 | GENERATE_INFOPLIST_FILE = YES;
426 | INFOPLIST_FILE = Microverse/Application/Info.plist;
427 | INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Justin Spahr-Summers. Released under the MIT license.";
428 | LD_RUNPATH_SEARCH_PATHS = (
429 | "$(inherited)",
430 | "@executable_path/../Frameworks",
431 | );
432 | MACOSX_DEPLOYMENT_TARGET = 12.0;
433 | MARKETING_VERSION = 0.2;
434 | PRODUCT_BUNDLE_IDENTIFIER = com.metacognitive.Microverse;
435 | PRODUCT_NAME = "$(TARGET_NAME)";
436 | SWIFT_EMIT_LOC_STRINGS = YES;
437 | SWIFT_VERSION = 5.0;
438 | };
439 | name = Release;
440 | };
441 | /* End XCBuildConfiguration section */
442 |
443 | /* Begin XCConfigurationList section */
444 | 35CF8D5826B8300F00E83536 /* Build configuration list for PBXProject "Microverse" */ = {
445 | isa = XCConfigurationList;
446 | buildConfigurations = (
447 | 35CF8D6C26B8301000E83536 /* Debug */,
448 | 35CF8D6D26B8301000E83536 /* Release */,
449 | );
450 | defaultConfigurationIsVisible = 0;
451 | defaultConfigurationName = Release;
452 | };
453 | 35CF8D6E26B8301000E83536 /* Build configuration list for PBXNativeTarget "Microverse" */ = {
454 | isa = XCConfigurationList;
455 | buildConfigurations = (
456 | 35CF8D6F26B8301000E83536 /* Debug */,
457 | 35CF8D7026B8301000E83536 /* Release */,
458 | );
459 | defaultConfigurationIsVisible = 0;
460 | defaultConfigurationName = Release;
461 | };
462 | /* End XCConfigurationList section */
463 | };
464 | rootObject = 35CF8D5526B8300F00E83536 /* Project object */;
465 | }
466 |
--------------------------------------------------------------------------------
/Microverse.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Microverse.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Microverse.xcodeproj/xcshareddata/xcschemes/Microverse.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
44 |
46 |
52 |
53 |
54 |
55 |
59 |
60 |
61 |
62 |
68 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/Microverse/Application/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Microverse/Application/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Microverse/Application/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Microverse/Application/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDocumentTypes
6 |
7 |
8 | CFBundleTypeIconSystemGenerated
9 | 1
10 | CFBundleTypeName
11 | macOS virtual machine
12 | CFBundleTypeRole
13 | Editor
14 | LSHandlerRank
15 | Owner
16 | LSItemContentTypes
17 |
18 | com.metacognitive.vm.macos
19 |
20 |
21 |
22 | LSApplicationCategoryType
23 | public.app-category.developer-tools
24 | NSMicrophoneUsageDescription
25 | Microverse requires microphone access to pass audio input to virtual machines.
26 | UTExportedTypeDeclarations
27 |
28 |
29 | UTTypeConformsTo
30 |
31 | com.apple.package
32 |
33 | UTTypeDescription
34 | Virtual machine
35 | UTTypeIcons
36 |
37 | UTTypeIconBadgeName
38 |
39 | UTTypeIconText
40 | VM
41 |
42 | UTTypeIdentifier
43 | com.metacognitive.vm
44 | UTTypeTagSpecification
45 |
46 | public.filename-extension
47 |
48 |
49 |
50 |
51 | UTTypeConformsTo
52 |
53 | com.metacognitive.vm
54 |
55 | UTTypeDescription
56 | macOS virtual machine
57 | UTTypeIcons
58 |
59 | UTTypeIconText
60 | Mac VM
61 |
62 | UTTypeIdentifier
63 | com.metacognitive.vm.macos
64 | UTTypeTagSpecification
65 |
66 | public.filename-extension
67 |
68 | macvm
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/Microverse/Application/Microverse.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.device.audio-input
8 |
9 | com.apple.security.files.bookmarks.app-scope
10 |
11 | com.apple.security.files.user-selected.read-write
12 |
13 | com.apple.security.network.client
14 |
15 | com.apple.security.network.server
16 |
17 | com.apple.security.virtualization
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Microverse/Application/MicroverseApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MicroverseApp.swift
3 | // Microverse
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct MicroverseApp: App {
12 | var body: some Scene {
13 | DocumentGroup(newDocument: MicroverseDocument()) { file in
14 | DocumentView(document: file.$document)
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Microverse/Application/MicroverseDocument.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MicroverseDocument.swift
3 | // Microverse
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 |
11 | extension UTType {
12 | static let VM = UTType(exportedAs: "com.metacognitive.vm", conformingTo: UTType.package)
13 | static let macVM = UTType(exportedAs: "com.metacognitive.vm.macos", conformingTo: UTType.VM)
14 | }
15 |
16 | extension VirtualMachine {
17 | var contentType: UTType {
18 | switch self {
19 | #if arch(arm64)
20 | case .macOS:
21 | return .macVM
22 | #endif
23 | }
24 | }
25 | }
26 |
27 | struct MicroverseDocument: FileDocument {
28 | #if arch(arm64)
29 | static var readableContentTypes: [UTType] { [.VM, .macVM] }
30 | static var writableContentTypes: [UTType] { [.macVM] }
31 | #else
32 | static var readableContentTypes: [UTType] { [.VM] }
33 | static var writableContentTypes: [UTType] { [] }
34 | #endif
35 |
36 | enum PackageItem: String {
37 | case MetadataJSON = "metadata.json"
38 | case MetadataPlist = "metadata.plist"
39 | }
40 |
41 | var virtualMachine: VirtualMachine
42 |
43 | init(virtualMachine: VirtualMachine = .macOS(MacOSVirtualMachine(configuration: VirtualMachineConfiguration()))) {
44 | self.virtualMachine = virtualMachine
45 | }
46 |
47 | init(configuration: ReadConfiguration) throws {
48 | guard configuration.contentType.conforms(to: .VM) else {
49 | throw CocoaError(.fileReadCorruptFile)
50 | }
51 |
52 | let wrappers = configuration.file.fileWrappers
53 |
54 | do {
55 | if let metadata = wrappers?[PackageItem.MetadataPlist.rawValue]?.regularFileContents {
56 | virtualMachine = try PropertyListDecoder().decode(VirtualMachine.self, from: metadata)
57 | } else if let metadata = wrappers?[PackageItem.MetadataJSON.rawValue]?.regularFileContents {
58 | virtualMachine = try JSONDecoder().decode(VirtualMachine.self, from: metadata)
59 | } else {
60 | throw CocoaError(.fileReadCorruptFile)
61 | }
62 | } catch {
63 | NSLog("Could not decode virtual machine metadata: \(error)")
64 | throw error
65 | }
66 |
67 | if !configuration.contentType.conforms(to: virtualMachine.contentType) {
68 | NSLog("Loaded virtual machine \(virtualMachine) had incorrect content type in document: \(configuration.contentType)")
69 | }
70 | }
71 |
72 | func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
73 | guard virtualMachine.contentType.conforms(to: configuration.contentType) else {
74 | throw CocoaError(.fileWriteInvalidFileName)
75 | }
76 |
77 | let metadata = try PropertyListEncoder().encode(virtualMachine)
78 | let metadataWrapper = FileWrapper(regularFileWithContents: metadata)
79 | metadataWrapper.preferredFilename = PackageItem.MetadataPlist.rawValue
80 |
81 | return FileWrapper(directoryWithFileWrappers: [
82 | PackageItem.MetadataPlist.rawValue: metadataWrapper
83 | ])
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Microverse/Application/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Microverse/Extensions/KeyedCodingExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeyedCodingExtensions.swift
3 | // KeyedCodingExtensions
4 | //
5 | // Created by Justin Spahr-Summers on 08/08/2021.
6 | //
7 |
8 | import Foundation
9 |
10 | extension KeyedEncodingContainerProtocol {
11 | mutating func encode(bookmarkForURL url: URL, options: URL.BookmarkCreationOptions, forKey key: Self.Key) throws {
12 | let data = try url.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
13 | try encode(data, forKey: key)
14 | }
15 |
16 | mutating func encodeIfPresent(bookmarkForURL url: URL?, options: URL.BookmarkCreationOptions, forKey key: Self.Key) throws {
17 | guard let url = url else {
18 | return
19 | }
20 |
21 | let data = try url.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
22 | try encode(data, forKey: key)
23 | }
24 | }
25 |
26 | extension KeyedDecodingContainerProtocol {
27 | func decodeURLFromBookmark(options: URL.BookmarkResolutionOptions, forKey key: Self.Key, stale: inout Bool) throws -> URL {
28 | let data = try decode(Data.self, forKey: key)
29 | return try URL(resolvingBookmarkData: data, options: options, relativeTo: nil, bookmarkDataIsStale: &stale)
30 | }
31 |
32 | func decodeURLFromBookmarkIfPresent(options: URL.BookmarkResolutionOptions, forKey key: Self.Key, stale: inout Bool) throws -> URL? {
33 | guard let data = try decodeIfPresent(Data.self, forKey: key) else {
34 | return nil
35 | }
36 |
37 | return try URL(resolvingBookmarkData: data, options: options, relativeTo: nil, bookmarkDataIsStale: &stale)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Microverse/Extensions/NSWindowExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSWindowExtensions.swift
3 | // NSWindowExtensions
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | extension NSWindow {
12 | convenience init(rootView: Content) {
13 | let viewController = NSHostingController(rootView: rootView)
14 | self.init(contentViewController: viewController)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Microverse/Model/AttachedDiskImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AttachedDiskImage.swift
3 | // AttachedDiskImage
4 | //
5 | // Created by Justin Spahr-Summers on 07/08/2021.
6 | //
7 |
8 | import Foundation
9 | import Virtualization
10 |
11 | struct AttachedDiskImage: Equatable, Hashable {
12 | enum SynchronizationMode: String, Codable, Equatable, Hashable, CaseIterable, Identifiable {
13 | case full = "Full"
14 | case fsync = "fsync-only"
15 | case none = "None"
16 |
17 | var id: String {
18 | self.rawValue
19 | }
20 | }
21 |
22 | var path: String = ""
23 | var isReadOnly: Bool = true
24 | var synchronizationMode: SynchronizationMode = .fsync
25 | }
26 |
27 | extension AttachedDiskImage: Codable {
28 | enum CodingKeys: String, CodingKey {
29 | case path = "data"
30 | case isReadOnly
31 | case synchronizationMode
32 | }
33 |
34 | func encode(to encoder: Encoder) throws {
35 | var container = encoder.container(keyedBy: CodingKeys.self)
36 | try container.encode(bookmarkForURL: URL(fileURLWithPath: path), options: isReadOnly ? [.withSecurityScope, .securityScopeAllowOnlyReadAccess] : .withSecurityScope, forKey: .path)
37 | try container.encode(isReadOnly, forKey: .isReadOnly)
38 | try container.encode(synchronizationMode, forKey: .synchronizationMode)
39 | }
40 |
41 | init(from decoder: Decoder) throws {
42 | let values = try decoder.container(keyedBy: CodingKeys.self)
43 |
44 | var stale = false
45 | let url = try values.decodeURLFromBookmark(options: .withSecurityScope, forKey: .path, stale: &stale)
46 |
47 | // FIXME: This is imbalanced right now!
48 | guard url.startAccessingSecurityScopedResource() else {
49 | throw CocoaError(.fileReadNoPermission)
50 | }
51 |
52 | path = url.path
53 | isReadOnly = try values.decode(Bool.self, forKey: .isReadOnly)
54 |
55 | if let synchronizationMode = try values.decodeIfPresent(SynchronizationMode.self, forKey: .synchronizationMode) {
56 | self.synchronizationMode = synchronizationMode
57 | }
58 | }
59 | }
60 |
61 | extension VZDiskImageSynchronizationMode {
62 | init(_ mode: AttachedDiskImage.SynchronizationMode) {
63 | switch mode {
64 | case .full:
65 | self = .full
66 | case .fsync:
67 | self = .fsync
68 | case .none:
69 | self = .none
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Microverse/Model/ConfigurableVirtualMachine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConfigurableVirtualMachine.swift
3 | // ConfigurableVirtualMachine
4 | //
5 | // Created by Justin Spahr-Summers on 07/08/2021.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol ConfigurableVirtualMachine {
11 | var configuration: VirtualMachineConfiguration { get set }
12 | }
13 |
--------------------------------------------------------------------------------
/Microverse/Model/MacOSVirtualMachine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MacOSVirtualMachine.swift
3 | // MacOSVirtualMachine
4 | //
5 | // Created by Justin Spahr-Summers on 07/08/2021.
6 | //
7 |
8 | import Foundation
9 | import Virtualization
10 |
11 | #if arch(arm64)
12 |
13 | struct MacMachine: Codable, Equatable, Hashable {
14 | var hardwareModelRepresentation: Data
15 | var hardwareModel: VZMacHardwareModel? {
16 | VZMacHardwareModel(dataRepresentation: hardwareModelRepresentation)
17 | }
18 |
19 | var machineIdentifierRepresentation: Data
20 | var machineIdentifier: VZMacMachineIdentifier? {
21 | VZMacMachineIdentifier(dataRepresentation: machineIdentifierRepresentation)
22 | }
23 | }
24 |
25 | struct MacOSVirtualMachine: ConfigurableVirtualMachine, Equatable {
26 | var configuration: VirtualMachineConfiguration
27 | var startupDiskURL: URL? = nil
28 | var auxiliaryStorageURL: URL? = nil
29 |
30 | var physicalMachine: MacMachine? = nil
31 | var osInstalled = false
32 | var attachedDiskImages: [AttachedDiskImage] = []
33 | }
34 |
35 | extension MacOSVirtualMachine: Codable {
36 | enum CodingKeys: CodingKey {
37 | case configuration
38 | case startupDiskBookmark
39 | case auxiliaryStorageBookmark
40 | case physicalMachine
41 | case osInstalled
42 | case attachedDiskImageBookmarks
43 | }
44 |
45 | func encode(to encoder: Encoder) throws {
46 | var container = encoder.container(keyedBy: CodingKeys.self)
47 | try container.encode(configuration, forKey: .configuration)
48 | try container.encodeIfPresent(bookmarkForURL: startupDiskURL, options: .withSecurityScope, forKey: .startupDiskBookmark)
49 | try container.encodeIfPresent(bookmarkForURL: auxiliaryStorageURL, options: .withSecurityScope, forKey: .auxiliaryStorageBookmark)
50 | try container.encodeIfPresent(physicalMachine, forKey: .physicalMachine)
51 | try container.encode(osInstalled, forKey: .osInstalled)
52 | try container.encode(attachedDiskImages, forKey: .attachedDiskImageBookmarks)
53 | }
54 |
55 | init(from decoder: Decoder) throws {
56 | let values = try decoder.container(keyedBy: CodingKeys.self)
57 | configuration = try values.decode(VirtualMachineConfiguration.self, forKey: .configuration)
58 |
59 | var stale = false
60 |
61 | startupDiskURL = try values.decodeURLFromBookmarkIfPresent(options: .withSecurityScope, forKey: .startupDiskBookmark, stale: &stale)
62 | if let startupDiskURL = startupDiskURL {
63 | // FIXME: This is imbalanced right now!
64 | guard startupDiskURL.startAccessingSecurityScopedResource() else {
65 | throw CocoaError(.fileReadNoPermission)
66 | }
67 | }
68 |
69 | auxiliaryStorageURL = try values.decodeURLFromBookmarkIfPresent(options: .withSecurityScope, forKey: .auxiliaryStorageBookmark, stale: &stale)
70 | if let auxiliaryStorageURL = auxiliaryStorageURL {
71 | // FIXME: This is imbalanced right now!
72 | guard auxiliaryStorageURL.startAccessingSecurityScopedResource() else {
73 | throw CocoaError(.fileReadNoPermission)
74 | }
75 | }
76 |
77 | physicalMachine = try values.decodeIfPresent(MacMachine.self, forKey: .physicalMachine)
78 | osInstalled = try values.decode(Bool.self, forKey: .osInstalled)
79 | attachedDiskImages = try values.decode([AttachedDiskImage].self, forKey: .attachedDiskImageBookmarks)
80 | }
81 | }
82 |
83 | extension VZVirtualMachineConfiguration {
84 | convenience init?(forMacOSVM vm: MacOSVirtualMachine) throws {
85 | self.init(vm.configuration)
86 |
87 | guard let startupDiskURL = vm.startupDiskURL else {
88 | return nil
89 | }
90 |
91 | var storageDevices = try vm.attachedDiskImages.filter { image in
92 | !image.path.isEmpty
93 | }.map { image in
94 | VZVirtioBlockDeviceConfiguration(attachment: try VZDiskImageStorageDeviceAttachment(url: URL(fileURLWithPath: image.path), readOnly: image.isReadOnly, cachingMode: .automatic, synchronizationMode: VZDiskImageSynchronizationMode(image.synchronizationMode)))
95 | }
96 |
97 | storageDevices.insert(VZVirtioBlockDeviceConfiguration(attachment: try VZDiskImageStorageDeviceAttachment(url: startupDiskURL, readOnly: false)), at: 0)
98 | self.storageDevices = storageDevices
99 |
100 | guard let hardwareModel = vm.physicalMachine?.hardwareModel, let machineIdentifier = vm.physicalMachine?.machineIdentifier, let auxiliaryStorageURL = vm.auxiliaryStorageURL else {
101 | return nil
102 | }
103 |
104 | let platform = VZMacPlatformConfiguration()
105 | platform.hardwareModel = hardwareModel
106 | platform.machineIdentifier = machineIdentifier
107 | platform.auxiliaryStorage = VZMacAuxiliaryStorage(contentsOf: auxiliaryStorageURL)
108 | self.platform = platform
109 |
110 | let graphics = VZMacGraphicsDeviceConfiguration()
111 | graphics.displays = [
112 | VZMacGraphicsDisplayConfiguration(
113 | widthInPixels: 2560,
114 | heightInPixels: 1600,
115 | pixelsPerInch: 220
116 | )
117 | ]
118 | self.graphicsDevices = [graphics]
119 |
120 | self.bootLoader = VZMacOSBootLoader()
121 | }
122 | }
123 |
124 | #endif
125 |
--------------------------------------------------------------------------------
/Microverse/Model/MicroverseErrors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MicroverseErrors.swift
3 | // MicroverseErrors
4 | //
5 | // Created by Justin Spahr-Summers on 08/08/2021.
6 | //
7 |
8 | import Foundation
9 |
10 | enum MicroverseError: Error {
11 |
12 | var localizedDescription: String {
13 | switch self {
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Microverse/Model/VirtualMachine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VirtualMachine.swift
3 | // VirtualMachine
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import Foundation
9 |
10 | enum VirtualMachine: Codable, ConfigurableVirtualMachine {
11 | #if arch(arm64)
12 | case macOS(MacOSVirtualMachine)
13 |
14 | var macOSVM: MacOSVirtualMachine? {
15 | switch self {
16 | case let .macOS(vm):
17 | return vm
18 | }
19 | }
20 | #endif
21 |
22 | var configuration: VirtualMachineConfiguration {
23 | get {
24 | switch self {
25 | #if arch(arm64)
26 | case let .macOS(vm):
27 | return vm.configuration
28 | #endif
29 | }
30 | }
31 | set(value) {
32 | switch self {
33 | #if arch(arm64)
34 | case var .macOS(vm):
35 | vm.configuration = value
36 | #endif
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Microverse/Model/VirtualMachineConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VirtualMachineConfiguration.swift
3 | // VirtualMachineConfiguration
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import AVFoundation
9 | import Foundation
10 | import Virtualization
11 |
12 | struct VirtualMachineConfiguration: Codable, Equatable {
13 | static let minimumCPUCount = VZVirtualMachineConfiguration.minimumAllowedCPUCount
14 | static let maximumCPUCount = VZVirtualMachineConfiguration.maximumAllowedCPUCount
15 | static let minimumMemoryMB = max(VZVirtualMachineConfiguration.minimumAllowedMemorySize / 1024 / 1024, 256)
16 | static let maximumMemoryMB = VZVirtualMachineConfiguration.maximumAllowedMemorySize / 1024 / 1024
17 |
18 | public var CPUCount = min(max(2, minimumCPUCount), maximumCPUCount)
19 | public var memoryMB = min(max(4096, minimumMemoryMB), maximumMemoryMB)
20 | }
21 |
22 | extension VZVirtualMachineConfiguration {
23 | convenience init(_ config: VirtualMachineConfiguration) {
24 | self.init()
25 | self.cpuCount = config.CPUCount
26 | self.memorySize = config.memoryMB * 1024 * 1024
27 | self.memoryBalloonDevices = [VZVirtioTraditionalMemoryBalloonDeviceConfiguration()]
28 | self.entropyDevices = [VZVirtioEntropyDeviceConfiguration()]
29 | self.pointingDevices = [VZUSBScreenCoordinatePointingDeviceConfiguration()]
30 | self.keyboards = [VZUSBKeyboardConfiguration()]
31 |
32 | let network = VZVirtioNetworkDeviceConfiguration()
33 | network.attachment = VZNATNetworkDeviceAttachment()
34 | self.networkDevices = [network]
35 |
36 | let audioOut = VZVirtioSoundDeviceOutputStreamConfiguration()
37 | audioOut.sink = VZHostAudioOutputStreamSink()
38 |
39 | AVCaptureDevice.requestAccess(for: .audio) { _ in }
40 |
41 | let audioIn = VZVirtioSoundDeviceInputStreamConfiguration()
42 | audioIn.source = VZHostAudioInputStreamSource()
43 |
44 | let audioDevice = VZVirtioSoundDeviceConfiguration()
45 | audioDevice.streams = [audioIn, audioOut]
46 | self.audioDevices = [audioDevice]
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Microverse/Model/VirtualMachineController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VirtualMachineController.swift
3 | // VirtualMachineController
4 | //
5 | // Created by Justin Spahr-Summers on 08/08/2021.
6 | //
7 |
8 | import Foundation
9 | import Virtualization
10 |
11 | final class VirtualMachineController: NSObject, VZVirtualMachineDelegate {
12 | let dispatchQueue = DispatchQueue(label: "com.metacognitive.Microverse.VirtualMachineController", qos: .userInitiated, attributes: .init(), autoreleaseFrequency: .workItem)
13 | let virtualMachine: VZVirtualMachine
14 |
15 | init(configuration: VZVirtualMachineConfiguration) throws {
16 | try configuration.validate()
17 | virtualMachine = VZVirtualMachine(configuration: configuration, queue: dispatchQueue)
18 | }
19 |
20 | func guestDidStop(_ virtualMachine: VZVirtualMachine) {
21 |
22 | }
23 |
24 | func virtualMachine(_ virtualMachine: VZVirtualMachine, didStopWithError error: Error) {
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Microverse/View/AttachedDisksView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AttachedDisksView.swift
3 | // AttachedDisksView
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 |
11 | struct AttachedDiskView: View {
12 | var label: String
13 | @Binding var diskImage: AttachedDiskImage
14 |
15 | var body: some View {
16 | PathField(title: label, path: $diskImage.path, allowedContentTypes: [UTType.diskImage])
17 | HStack {
18 | Picker("Synchronization Mode:", selection: $diskImage.synchronizationMode) {
19 | ForEach(AttachedDiskImage.SynchronizationMode.allCases) { mode in
20 | Text(mode.rawValue).tag(mode)
21 | }
22 | }
23 | Toggle("Read Only", isOn: $diskImage.isReadOnly)
24 | }
25 | }
26 | }
27 |
28 | struct AttachedDisksView: View {
29 | @Binding var diskImages: [AttachedDiskImage]
30 |
31 | var body: some View {
32 | GroupBox("Attached Disks") {
33 | HStack {
34 | Form {
35 | ForEach(Array($diskImages.enumerated()), id: \.offset) { index, element in
36 | AttachedDiskView(label: "Disk Image \(index + 1):", diskImage: $diskImages[index])
37 | HStack {
38 | Spacer()
39 | Button("Remove") {
40 | diskImages.remove(at: index)
41 | }
42 | }
43 | }
44 | HStack {
45 | Spacer()
46 | Button("Add") {
47 | diskImages.append(AttachedDiskImage())
48 | }
49 | }
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
56 | struct AttachedDisksView_Previews: PreviewProvider {
57 | struct Holder: View {
58 | @State var disks: [AttachedDiskImage] = [AttachedDiskImage()]
59 | var body: some View {
60 | return AttachedDisksView(diskImages: $disks)
61 | }
62 | }
63 |
64 | static var previews: some View {
65 | Holder()
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Microverse/View/DiskCreationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DiskCreationView.swift
3 | // DiskCreationView
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 |
11 | struct DiskCreationView: View {
12 | @State private var diskSizeGB: Float = 32
13 | var action: (URL, UInt64) -> ()
14 |
15 | var body: some View {
16 | let numberFormatter = NumberFormatter()
17 |
18 | HStack {
19 | Form {
20 | TextField("Disk Size (GB):", value: $diskSizeGB, formatter: numberFormatter)
21 | Button("Create…") {
22 | let panel = NSSavePanel()
23 | panel.allowedContentTypes = [UTType.diskImage]
24 | panel.nameFieldStringValue = "disk.img"
25 | panel.allowsOtherFileTypes = true
26 | panel.canCreateDirectories = true
27 | panel.begin { response in
28 | guard response == NSApplication.ModalResponse.OK, let url = panel.url else {
29 | return
30 | }
31 |
32 | let bytes = UInt64(diskSizeGB * 1024 * 1024 * 1024)
33 | action(url, bytes)
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
41 | struct DiskCreationView_Previews: PreviewProvider {
42 | static var previews: some View {
43 | DiskCreationView() { _, _ in }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Microverse/View/DocumentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Microverse
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import Virtualization
10 |
11 | struct DocumentView: View {
12 | @Binding var document: MicroverseDocument
13 |
14 | var body: some View {
15 | switch document.virtualMachine {
16 | #if arch(arm64)
17 | case .macOS:
18 | MacOSDocumentView(virtualMachine: Binding(get: {
19 | return document.virtualMachine.macOSVM!
20 | }, set: { vm in document.virtualMachine = .macOS(vm) }))
21 | #endif
22 | }
23 | }
24 | }
25 |
26 | struct ContentView_Previews: PreviewProvider {
27 | static var previews: some View {
28 | DocumentView(document: Binding.constant(MicroverseDocument()))
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Microverse/View/MacAuxiliaryStorageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MacAuxiliaryStorageView.swift
3 | // MacAuxiliaryStorageView
4 | //
5 | // Created by Justin Spahr-Summers on 07/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 | import Virtualization
11 |
12 | #if arch(arm64)
13 |
14 | struct MacAuxiliaryStorageView: View {
15 | var hardwareModel: VZMacHardwareModel
16 | @Binding var auxiliaryStorageURL: URL?
17 |
18 | var body: some View {
19 | GroupBox("macOS Auxiliary Storage") {
20 | HStack {
21 | Form {
22 | if let url = auxiliaryStorageURL {
23 | Text(url.path)
24 | Button("Reset") {
25 | auxiliaryStorageURL = nil
26 | }
27 | } else {
28 | Button("Create…") {
29 | let panel = NSSavePanel()
30 | panel.allowedContentTypes = [UTType.diskImage]
31 | panel.nameFieldStringValue = "aux.img"
32 | panel.allowsOtherFileTypes = true
33 | panel.canCreateDirectories = true
34 | panel.begin { response in
35 | guard response == NSApplication.ModalResponse.OK, let url = panel.url else {
36 | return
37 | }
38 |
39 | do {
40 | let auxStorage = try VZMacAuxiliaryStorage(creatingStorageAt: url, hardwareModel: hardwareModel, options: .allowOverwrite)
41 | self.auxiliaryStorageURL = auxStorage.url
42 | } catch {
43 | NSLog("Failed to create auxiliary storage at \(url): \(error)")
44 | }
45 | }
46 | }
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 | #endif
55 |
--------------------------------------------------------------------------------
/Microverse/View/MacOSDocumentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MacOSDocumentView.swift
3 | // MacOSDocumentView
4 | //
5 | // Created by Justin Spahr-Summers on 07/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import Virtualization
10 |
11 | #if arch(arm64)
12 |
13 | struct MacOSDocumentView: View {
14 | @Binding var virtualMachine: MacOSVirtualMachine
15 | @State var restoreImage: VZMacOSRestoreImage? = nil
16 | @State var virtualMachineController: VirtualMachineController? = nil
17 | @State var running = false
18 |
19 | var body: some View {
20 | if let virtualMachineController = virtualMachineController, running {
21 | VirtualMachineView(virtualMachine: virtualMachineController.virtualMachine)
22 | } else {
23 | HStack {
24 | Spacer()
25 | VStack {
26 | Spacer()
27 | GroupBox("Startup Disk") {
28 | if let startupDiskURL = virtualMachine.startupDiskURL {
29 | Text("\(startupDiskURL.path)")
30 | } else {
31 | DiskCreationView { url, bytes in
32 | let blockSize: UInt64 = 4096
33 | do {
34 | let process = Process()
35 | process.launchPath = "/bin/dd"
36 | process.arguments = [
37 | "if=/dev/zero",
38 | "of=\(url.path)",
39 | "bs=\(blockSize)",
40 | "seek=\(bytes / blockSize)",
41 | "count=0"
42 | ]
43 | try process.run()
44 | process.waitUntilExit()
45 |
46 | virtualMachine.startupDiskURL = url
47 | } catch {
48 | NSLog("Failed to create disk image")
49 | }
50 | }
51 | }
52 | }
53 |
54 | AttachedDisksView(diskImages: $virtualMachine.attachedDiskImages)
55 |
56 | if virtualMachine.startupDiskURL != nil {
57 | VirtualMachineConfigurationView(configuration: $virtualMachine.configuration)
58 |
59 | if virtualMachine.physicalMachine == nil {
60 | MacRestoreImageView(restoreImage: $restoreImage)
61 | }
62 | }
63 |
64 | if let hardwareModel = restoreImage?.mostFeaturefulSupportedConfiguration?.hardwareModel ?? virtualMachine.physicalMachine?.hardwareModel {
65 | MacAuxiliaryStorageView(hardwareModel: hardwareModel, auxiliaryStorageURL: $virtualMachine.auxiliaryStorageURL)
66 | }
67 |
68 | if let virtualMachineController = virtualMachineController {
69 | if !virtualMachine.osInstalled {
70 | MacOSInstallView(virtualMachineController: virtualMachineController, restoreImageURL: restoreImage!.url) {
71 | virtualMachine.osInstalled = true
72 | }
73 | } else {
74 | Button("Start") {
75 | virtualMachineController.dispatchQueue.async {
76 | virtualMachineController.virtualMachine.start { result in
77 | DispatchQueue.main.async {
78 | switch result {
79 | case .success:
80 | NSLog("Launched VM")
81 | running = true
82 | case let .failure(error):
83 | NSLog("Failed to start VM: \(error)")
84 | }
85 | }
86 | }
87 | }
88 | }
89 | }
90 | }
91 | Spacer()
92 | }
93 | Spacer()
94 | }.onChange(of: restoreImage) { _ in
95 | guard let hardwareModel = restoreImage?.mostFeaturefulSupportedConfiguration?.hardwareModel else {
96 | return
97 | }
98 |
99 | virtualMachine.physicalMachine = virtualMachine.physicalMachine ?? MacMachine(hardwareModelRepresentation: hardwareModel.dataRepresentation, machineIdentifierRepresentation: VZMacMachineIdentifier().dataRepresentation)
100 | }.task(id: virtualMachine) {
101 | do {
102 | guard let vmConfig = try VZVirtualMachineConfiguration(forMacOSVM: virtualMachine) else {
103 | DispatchQueue.main.async {
104 | virtualMachineController = nil
105 | }
106 |
107 | return
108 | }
109 |
110 | let controller = try VirtualMachineController(configuration: vmConfig)
111 | DispatchQueue.main.async {
112 | virtualMachineController = controller
113 | }
114 | } catch {
115 | NSLog("Error preparing virtual machine controller: \(error)")
116 | }
117 | }
118 | }
119 | }
120 | }
121 |
122 | struct MacOSDocumentView_Previews: PreviewProvider {
123 | struct Holder: View {
124 | @State var virtualMachine = MacOSVirtualMachine(configuration: VirtualMachineConfiguration())
125 | var body: some View {
126 | MacOSDocumentView(virtualMachine: $virtualMachine)
127 | }
128 | }
129 |
130 | static var previews: some View {
131 | Holder()
132 | }
133 | }
134 |
135 | #endif
136 |
--------------------------------------------------------------------------------
/Microverse/View/MacOSInstallView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MacOSInstallView.swift
3 | // MacOSInstallView
4 | //
5 | // Created by Justin Spahr-Summers on 07/08/2021.
6 | //
7 |
8 | import Combine
9 | import SwiftUI
10 | import Virtualization
11 |
12 | #if arch(arm64)
13 |
14 | struct MacOSInstallView: View {
15 | var virtualMachineController: VirtualMachineController
16 | var restoreImageURL: URL
17 | @State var installer: VZMacOSInstaller? = nil
18 | @State var progress = 0.0
19 | var action: () -> Void
20 |
21 | var body: some View {
22 | GroupBox("macOS Installation") {
23 | HStack {
24 | Form {
25 | if installer != nil {
26 | ProgressView(value: progress) {
27 | Text("Installing…")
28 | } currentValueLabel: {
29 | Text("\(Int(progress * 100))% completed")
30 | }
31 | } else {
32 | Button("Start") {
33 | virtualMachineController.dispatchQueue.async {
34 | let installer = VZMacOSInstaller(virtualMachine: virtualMachineController.virtualMachine, restoringFromImageAt: restoreImageURL)
35 |
36 | DispatchQueue.main.async {
37 | self.installer = installer
38 | self.progress = 0.0
39 | }
40 |
41 | let cancellable = installer.progress.publisher(for: \.completedUnitCount)
42 | .map { completed in Double(completed) / Double(installer.progress.totalUnitCount) }
43 | .receive(on: DispatchQueue.main)
44 | .sink { progress in
45 | self.progress = progress
46 | }
47 |
48 | NSLog("Starting installation into \(virtualMachineController) from \(restoreImageURL)")
49 | installer.install { [cancellable] result in
50 | cancellable.cancel()
51 |
52 | DispatchQueue.main.async {
53 | switch result {
54 | case .success:
55 | action()
56 | case let .failure(error):
57 | NSLog("Failed to install macOS into VM: \(error)")
58 | self.installer = nil
59 | }
60 | }
61 | }
62 | }
63 | }
64 | }
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
71 | #endif
72 |
--------------------------------------------------------------------------------
/Microverse/View/MacRestoreImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MacRestoreImageView.swift
3 | // MacRestoreImageView
4 | //
5 | // Created by Justin Spahr-Summers on 04/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 | import Virtualization
11 |
12 | #if arch(arm64)
13 |
14 | enum MacRestoreImageSource {
15 | case latest
16 | case fromFile
17 | }
18 |
19 | struct MacRestoreImageView: View {
20 | @Binding var restoreImage: VZMacOSRestoreImage?
21 | @State var ipswPath: String = ""
22 | @State var loading = false
23 | @State var restoreImageSource: MacRestoreImageSource = .fromFile
24 |
25 | var body: some View {
26 | let imageLoadCompleted = { (result: Result) in
27 | DispatchQueue.main.async {
28 | loading = false
29 | guard case let .success(image) = result else {
30 | print("Error loading macOS restore image:", result)
31 | return
32 | }
33 |
34 | restoreImage = image
35 | }
36 | }
37 |
38 |
39 | GroupBox("MacOS Installation") {
40 | HStack {
41 | Form {
42 | Picker("System Restore Image:", selection: $restoreImageSource) {
43 | Text("Latest").tag(MacRestoreImageSource.latest)
44 | HStack {
45 | Text("From File:")
46 | PathField(title: "Path", path: $ipswPath, allowedContentTypes: [UTType(filenameExtension: "ipsw") ?? .data]).onChange(of: ipswPath) { newValue in
47 | loading = true
48 | VZMacOSRestoreImage.load(from: URL(fileURLWithPath: ipswPath), completionHandler: imageLoadCompleted)
49 | }
50 | }.tag(MacRestoreImageSource.fromFile)
51 | }.pickerStyle(.inline).onChange(of: restoreImageSource) { newValue in
52 | restoreImage = nil
53 |
54 | switch newValue {
55 | case .latest:
56 | loading = true
57 | VZMacOSRestoreImage.fetchLatestSupported(completionHandler: imageLoadCompleted)
58 |
59 | case .fromFile:
60 | loading = false
61 | }
62 | }
63 |
64 | if loading {
65 | ProgressView()
66 | } else if let restoreImage = restoreImage {
67 | HStack {
68 | Text("Loaded image for macOS build \(restoreImage.buildVersion)")
69 | Button("Reset") {
70 | self.restoreImage = nil
71 | }
72 | }
73 | }
74 | }
75 | }
76 | }
77 | }
78 | }
79 |
80 | struct MacRestoreView_Previews: PreviewProvider {
81 | struct Holder: View {
82 | @State var restoreImage: VZMacOSRestoreImage? = nil
83 | var body: some View {
84 | return MacRestoreImageView(restoreImage: $restoreImage)
85 | }
86 | }
87 |
88 | static var previews: some View {
89 | Holder()
90 | }
91 | }
92 |
93 | #endif
94 |
--------------------------------------------------------------------------------
/Microverse/View/PathField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PathField.swift
3 | // PathField
4 | //
5 | // Created by Justin Spahr-Summers on 04/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 |
11 | struct PathField: View {
12 | var title: String
13 | @Binding var path: String
14 | var allowedContentTypes: [UTType]
15 | @State var presentingFileImporter = false
16 |
17 | var body: some View {
18 | HStack {
19 | TextField(title, text: $path)
20 | Button("Locate…") {
21 | presentingFileImporter = true
22 | }.fileImporter(isPresented: $presentingFileImporter, allowedContentTypes: allowedContentTypes) { result in
23 | guard case let .success(URL) = result, URL.isFileURL else {
24 | return
25 | }
26 |
27 | path = URL.path
28 | }
29 | }
30 | }
31 | }
32 |
33 | struct PathField_Previews: PreviewProvider {
34 | struct Holder: View {
35 | @State var path: String = ""
36 | var body: some View {
37 | PathField(title: "Path", path: $path, allowedContentTypes: [UTType.content])
38 | }
39 | }
40 |
41 | static var previews: some View {
42 | Holder()
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Microverse/View/VirtualMachineConfigurationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VirtualMachineConfigurationView.swift
3 | // VirtualMachineConfigurationView
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct VirtualMachineConfigurationView: View {
11 | @Binding var configuration: VirtualMachineConfiguration
12 |
13 | var body: some View {
14 | GroupBox("Virtual Machine") {
15 | HStack {
16 | Form {
17 | Picker("CPUs:", selection: $configuration.CPUCount) {
18 | ForEach(VirtualMachineConfiguration.minimumCPUCount...VirtualMachineConfiguration.maximumCPUCount, id: \.self) { count in
19 | Text("\(count)")
20 | }
21 | }
22 |
23 | Slider(value: Binding(get: { Float(configuration.memoryMB) }, set: { v in configuration.memoryMB = UInt64(v) }), in: Float(VirtualMachineConfiguration.minimumMemoryMB)...Float(VirtualMachineConfiguration.maximumMemoryMB), step: 256) {
24 | Text("Memory:")
25 | } minimumValueLabel: {
26 | Text("\(VirtualMachineConfiguration.minimumMemoryMB) MB")
27 | } maximumValueLabel: {
28 | Text("\(VirtualMachineConfiguration.maximumMemoryMB) MB")
29 | }
30 | HStack {
31 | Spacer()
32 | Text("\(Int(configuration.memoryMB)) MB").foregroundColor(Color.blue)
33 | Spacer()
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
41 | struct VirtualMachineConfigurationView_Previews: PreviewProvider {
42 | struct Holder: View {
43 | @State var configuration = VirtualMachineConfiguration()
44 | var body: some View {
45 | VirtualMachineConfigurationView(configuration: $configuration)
46 | }
47 | }
48 |
49 | static var previews: some View {
50 | Holder()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Microverse/View/VirtualMachineView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VirtualMachineView.swift
3 | // VirtualMachineView
4 | //
5 | // Created by Justin Spahr-Summers on 02/08/2021.
6 | //
7 |
8 | import SwiftUI
9 | import Virtualization
10 |
11 | struct VirtualMachineView: NSViewRepresentable {
12 | var virtualMachine: VZVirtualMachine?
13 |
14 | func makeNSView(context: Context) -> VZVirtualMachineView {
15 | let view = VZVirtualMachineView()
16 | view.capturesSystemKeys = true
17 | view.virtualMachine = virtualMachine
18 | return view
19 | }
20 |
21 | func updateNSView(_ nsView: VZVirtualMachineView, context: Context) {
22 | nsView.virtualMachine = virtualMachine
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Microverse
2 |
3 | [Microverse](https://rickandmorty.fandom.com/wiki/Microverse_Battery) is a thin virtualization app for running macOS guest virtual machines on M1/Apple Silicon (arm64) processors, achieved with [Apple's `Virtualization.framework`](https://developer.apple.com/documentation/virtualization).
4 |
5 | Note that this does not do any _emulation_—the virtual machines run on the same hardware as the host machine (and therefore have the same architecture). This is particularly useful to create sandbox environments with minimal performance impact.
6 |
7 | ## Requirements
8 |
9 | This project makes use of APIs from the macOS 12 (Monterey) [beta](https://beta.apple.com/sp/betaprogram/).
10 |
11 | macOS 11 (Big Sur) is unsupported.
12 |
13 | ## Known limitations
14 |
15 | For installing into the VM, only [macOS 12 (Monterey) ipsw files](https://mrmacintosh.com/apple-silicon-m1-full-macos-restore-ipsw-firmware-files-database/) are known to work.
16 |
17 | There's a known issue where the App Store cannot be contacted from within a VM. Any applications you want to run will need to be downloaded from the web, or imported into the VM using a disk image in UDRW format (see `man hdiutil` for details).
18 |
19 | For other known issues, please see the [GitHub issues list](https://github.com/jspahrsummers/Microverse/issues).
20 |
21 | ## License and credit
22 |
23 | Released under the [MIT license](LICENSE).
24 |
25 | I'm indebted to @KhaosT' [MacVM](https://github.com/KhaosT/MacVM) and [SimpleVM](https://github.com/KhaosT/SimpleVM) projects for demonstrating [`Virtualization.framework`](https://developer.apple.com/documentation/virtualization) usage.
26 |
--------------------------------------------------------------------------------