Skip to content
This repository was archived by the owner on Jun 22, 2022. It is now read-only.

Commit f5c3d2e

Browse files
committed
Initial commit
0 parents  commit f5c3d2e

30 files changed

+1753
-0
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.gitignore

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
# Created by https://www.toptal.com/developers/gitignore/api/macos,xcode
3+
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,xcode
4+
5+
### macOS ###
6+
# General
7+
.DS_Store
8+
.AppleDouble
9+
.LSOverride
10+
11+
# Icon must end with two \r
12+
Icon
13+
14+
15+
# Thumbnails
16+
._*
17+
18+
# Files that might appear in the root of a volume
19+
.DocumentRevisions-V100
20+
.fseventsd
21+
.Spotlight-V100
22+
.TemporaryItems
23+
.Trashes
24+
.VolumeIcon.icns
25+
.com.apple.timemachine.donotpresent
26+
27+
# Directories potentially created on remote AFP share
28+
.AppleDB
29+
.AppleDesktop
30+
Network Trash Folder
31+
Temporary Items
32+
.apdisk
33+
34+
### Xcode ###
35+
## User settings
36+
xcuserdata/
37+
38+
## Xcode 8 and earlier
39+
*.xcscmblueprint
40+
*.xccheckout
41+
42+
### Xcode Patch ###
43+
*.xcodeproj/*
44+
!*.xcodeproj/project.pbxproj
45+
!*.xcodeproj/xcshareddata/
46+
!*.xcworkspace/contents.xcworkspacedata
47+
/*.gcno
48+
**/xcshareddata/WorkspaceSettings.xcsettings
49+
50+
# End of https://www.toptal.com/developers/gitignore/api/macos,xcode

LICENSE

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
This is free and unencumbered software released into the public domain.
2+
3+
Anyone is free to copy, modify, publish, use, compile, sell, or
4+
distribute this software, either in source code form or as a compiled
5+
binary, for any purpose, commercial or non-commercial, and by any
6+
means.
7+
8+
In jurisdictions that recognize copyright laws, the author or authors
9+
of this software dedicate any and all copyright interest in the
10+
software to the public domain. We make this dedication for the benefit
11+
of the public at large and to the detriment of our heirs and
12+
successors. We intend this dedication to be an overt act of
13+
relinquishment in perpetuity of all present and future rights to this
14+
software under copyright law.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
23+
24+
For more information, please refer to <http://unlicense.org>

Minecraft Silicon.xcodeproj/project.pbxproj

+445
Large diffs are not rendered by default.
+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//
2+
// Adapter.swift
3+
// Minecraft Silicon
4+
//
5+
// Created by Cole Feuer on 2021-12-25.
6+
//
7+
8+
import Foundation
9+
10+
class Adapter {
11+
///Adapts the base Minecraft version to an Apple Silicon-compatible version, returning its version ID
12+
static func adaptVersion(versionID: String) async throws -> String {
13+
let versionDir = Paths.versionsDir.appendingPathComponent(versionID, isDirectory: true)
14+
15+
//Make sure the version directory is actually a version directory
16+
let versionFileJSON = versionDir.appendingPathComponent("\(versionID).json")
17+
let versionFileJar = versionDir.appendingPathComponent("\(versionID).jar")
18+
guard FileManager.default.fileExists(atPath: versionFileJSON.path),
19+
FileManager.default.fileExists(atPath: versionFileJar.path) else {
20+
throw AdapterError("\(versionID) is not a valid Minecraft version")
21+
}
22+
23+
LoggingHelper.main.info("Adapting Minecraft version \(versionID) for arm64")
24+
25+
//Read the version file
26+
var jsonData = try JSONSerialization.jsonObject(with: Data(contentsOf: versionFileJSON), options: .mutableContainers) as! [String: Any]
27+
28+
//Read the base version
29+
//let baseVersion = jsonData["assets"] as! String
30+
31+
//Iterate over libraries
32+
for (i, var library) in (jsonData["libraries"] as! [[String: Any]]).enumerated() {
33+
//Match library rules
34+
if let ruleArray = library["rules"] as? [[String: Any]] {
35+
guard checkVersionRules(ruleArray) else { continue }
36+
}
37+
38+
//Parse the library name
39+
let libraryInfo = JavaLibrary(string: library["name"] as! String)
40+
41+
if libraryInfo.group == "org.lwjgl" {
42+
/**
43+
LWJGL provides Apple Silicon-compatible builds starting with version 3.3.0,
44+
so we'll swap out any references to LWJGL libraries to their official releases.
45+
*/
46+
LoggingHelper.main.debug("Checking library \(libraryInfo.id)...")
47+
48+
let downloads = library["downloads"] as! [String: Any]
49+
50+
do {
51+
//Get the official LWJGL file info
52+
let lwjglFileURL = "https://build.lwjgl.org/release/\(AdapterConstants.lwjglVersion)/bin/\(libraryInfo.id)/\(libraryInfo.id).jar"
53+
let (lwjglFileHash, lwjglFileSize) = try await analyzeLibraryURL(url: URL(string: lwjglFileURL)!)
54+
55+
//Upgrade the common artifact
56+
var libraryArtifact = (downloads["artifact"] as! [String: Any])
57+
libraryArtifact["sha1"] = lwjglFileHash
58+
libraryArtifact["size"] = lwjglFileSize
59+
libraryArtifact["url"] = lwjglFileURL
60+
61+
try DictionaryHelper.update(dictionary: &library, at: ["downloads", "artifact"], with: libraryArtifact)
62+
}
63+
64+
//Check for natives
65+
if let nativesID = (library["natives"] as? [String: String])?[AdapterConstants.launcherOSID] {
66+
LoggingHelper.main.debug("Checking library \(libraryInfo.id) natives...")
67+
68+
//Get the official LWJGL file info
69+
let lwjglFileURL = "https://build.lwjgl.org/release/\(AdapterConstants.lwjglVersion)/bin/\(libraryInfo.id)/\(libraryInfo.id)-natives-\(AdapterConstants.lwjglOSID)-arm64.jar"
70+
let (lwjglFileHash, lwjglFileSize) = try await analyzeLibraryURL(url: URL(string: lwjglFileURL)!)
71+
72+
//Update the library classifier
73+
var libraryClassifier = (downloads["classifiers"] as! [String: [String: Any]])[nativesID]!
74+
//let libraryPath = URL(fileURLWithPath: libraryClassifier["path"] as! String, isDirectory: false)
75+
//libraryClassifier["path"] = libraryPath.deletingLastPathComponent().appendingPathComponent(libraryPath.deletingPathExtension().lastPathComponent + Constants.suffixARM + "." + libraryPath.pathExtension).relativePath
76+
libraryClassifier["sha1"] = lwjglFileHash
77+
libraryClassifier["size"] = lwjglFileSize
78+
libraryClassifier["url"] = lwjglFileURL
79+
80+
try DictionaryHelper.update(dictionary: &library, at: ["downloads", "classifiers", nativesID], with: libraryClassifier)
81+
}
82+
83+
//Copy back to jsonData
84+
var jsonDataLibraries = jsonData["libraries"] as! [[String: Any]]
85+
jsonDataLibraries[i] = library
86+
jsonData["libraries"] = jsonDataLibraries
87+
} else if libraryInfo.group == "ca.weblite" && libraryInfo.id == "java-objc-bridge" {
88+
/**
89+
This is a library used make native Objective-C calls from Java.
90+
It has been updated to produce universal builds, but Minecraft uses an older version that doesn't support Apple Silicon.
91+
*/
92+
LoggingHelper.main.debug("Checking library \(libraryInfo.id)...")
93+
94+
let downloads = library["downloads"] as! [String: Any]
95+
96+
do {
97+
//Get the native file info
98+
let updatedFileURL = "https://repo1.maven.org/maven2/ca/weblite/java-objc-bridge/1.1/java-objc-bridge-1.1.jar"
99+
let (updatedFileHash, updatedFileSize) = try await analyzeLibraryURL(url: URL(string: updatedFileURL)!)
100+
101+
//Upgrade the common artifact
102+
var libraryArtifact = (downloads["artifact"] as! [String: Any])
103+
libraryArtifact["sha1"] = updatedFileHash
104+
libraryArtifact["size"] = updatedFileSize
105+
libraryArtifact["url"] = updatedFileURL
106+
107+
try DictionaryHelper.update(dictionary: &library, at: ["downloads", "artifact"], with: libraryArtifact)
108+
}
109+
110+
//Copy back to jsonData
111+
var jsonDataLibraries = jsonData["libraries"] as! [[String: Any]]
112+
jsonDataLibraries[i] = library
113+
jsonData["libraries"] = jsonDataLibraries
114+
}
115+
}
116+
117+
//Update version ID
118+
jsonData["id"] = (jsonData["id"] as! String) + Constants.suffixARM
119+
120+
//Create the new the version directory
121+
let updatedVersionID = versionID + Constants.suffixARM
122+
let updatedVersionDir = Paths.versionsDir.appendingPathComponent(updatedVersionID)
123+
let updatedVersionFileJSON = updatedVersionDir.appendingPathComponent("\(updatedVersionID).json")
124+
let updatedVersionFileJar = updatedVersionDir.appendingPathComponent("\(updatedVersionID).jar")
125+
126+
if !FileManager.default.fileExists(atPath: updatedVersionDir.path) {
127+
try FileManager.default.createDirectory(at: updatedVersionDir, withIntermediateDirectories: false, attributes: .none)
128+
}
129+
130+
//Copy over the jar file
131+
try FileManager.default.copyItem(at: versionFileJar, to: updatedVersionFileJar)
132+
133+
//Write the updated JSON file
134+
do {
135+
let outputStream = OutputStream(url: updatedVersionFileJSON, append: false)!
136+
outputStream.open()
137+
defer { outputStream.close() }
138+
139+
var error: NSError?
140+
JSONSerialization.writeJSONObject(jsonData, to: outputStream, options: [.withoutEscapingSlashes], error: &error)
141+
142+
if let error = error {
143+
throw error
144+
}
145+
}
146+
147+
LoggingHelper.main.info("Successfully created Minecraft version version \(updatedVersionID)")
148+
149+
return updatedVersionID
150+
}
151+
}
152+
153+
struct AdapterError: Error {
154+
let message: String
155+
156+
init(_ message: String) {
157+
self.message = message
158+
}
159+
160+
public var errorDescription: String { message }
161+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// AdapterConstants.swift
3+
// Minecraft Silicon
4+
//
5+
// Created by Cole Feuer on 2021-12-26.
6+
//
7+
8+
import Foundation
9+
10+
class AdapterConstants {
11+
//The LWJGL version to use
12+
static let lwjglVersion = "3.3.0"
13+
14+
///The ID of the current operating system, as used by Minecraft Launcher
15+
#if os(macOS)
16+
static let launcherOSID = "osx"
17+
#elseif os(Linux)
18+
static let launcherOSID = "linux"
19+
#elseif os(Windows)
20+
static let launcherOSID = "windows"
21+
#endif
22+
23+
///The ID of the current operating system, as used by LWJGL
24+
#if os(macOS)
25+
static let lwjglOSID = "macos"
26+
#elseif os(Linux)
27+
static let lwjglOSID = "linux"
28+
#elseif os(Windows)
29+
static let lwjglOSID = "windows"
30+
#endif
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// AdapterRules.swift
3+
// Minecraft Silicon
4+
//
5+
// Created by Cole Feuer on 2021-12-26.
6+
//
7+
8+
import Foundation
9+
10+
///Checks an array of rules, and returns whether the rules allow this system
11+
func checkVersionRules(_ ruleArray: [[String: Any]]) -> Bool {
12+
for rule in ruleArray {
13+
//Ignore rules that don't match this OS
14+
guard let rulesOS = rule["os"] as? [String: Any],
15+
rulesOS["name"] as! String == AdapterConstants.launcherOSID else { continue }
16+
17+
//Read the rule action
18+
let ruleAllowRaw = rule["action"] as! String
19+
let ruleAllow: Bool
20+
if ruleAllowRaw == "allow" {
21+
ruleAllow = true
22+
} else if ruleAllowRaw == "disallow" {
23+
ruleAllow = false
24+
} else {
25+
print("Unknown rule type: \(ruleAllowRaw)")
26+
exit(EXIT_FAILURE)
27+
}
28+
29+
//If this rule blocks this OS, return false
30+
guard ruleAllow else {
31+
return false
32+
}
33+
}
34+
35+
return true
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// AdapterURLResolver.swift
3+
// Minecraft Silicon
4+
//
5+
// Created by Cole Feuer on 2021-12-27.
6+
//
7+
8+
import Foundation
9+
import CryptoKit
10+
11+
typealias LibraryAnalysisResult = (hash: String, count: Int)
12+
13+
///Calculates the size and hash of a URL
14+
func analyzeLibraryURL(url: URL) async throws -> LibraryAnalysisResult {
15+
return try await withCheckedThrowingContinuation { continuation in
16+
let urlSessionDelegate = URLInfoSessionDelegate() { (result, error) in
17+
if let error = error {
18+
continuation.resume(throwing: error)
19+
} else {
20+
continuation.resume(returning: result!)
21+
}
22+
}
23+
let urlSession = URLSession(configuration: .default, delegate: urlSessionDelegate, delegateQueue: nil)
24+
25+
//Open the URL for reading
26+
let task = urlSession.dataTask(with: url)
27+
task.resume()
28+
}
29+
}
30+
31+
private class URLInfoSessionDelegate: NSObject, URLSessionDataDelegate {
32+
private let callback: (LibraryAnalysisResult?, Error?) -> Void
33+
34+
private var fileHash = Insecure.SHA1()
35+
private var fileLength = 0
36+
37+
init(callback: @escaping (LibraryAnalysisResult?, Error?) -> Void) {
38+
self.callback = callback
39+
}
40+
41+
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
42+
//Progressively calculate the file hash and tally the file length
43+
fileHash.update(data: data)
44+
fileLength += data.count
45+
}
46+
47+
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
48+
//Invoke the callback
49+
if let error = error {
50+
callback(nil, error)
51+
} else {
52+
let md5Hash = fileHash.finalize().map { String(format: "%02hhx", $0) }.joined()
53+
callback((md5Hash, fileLength), nil)
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)