forked from MeloNX/MeloNX
Add Built in JitStreamer-EB implementation, rework the shortcut and more
This commit is contained in:
parent
007cb026a4
commit
c3ade6f5cd
Binary file not shown.
@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
#define DRM 1
|
||||
#define CS_DEBUGGED 0x10000000
|
||||
|
||||
#ifndef RyujinxHeader
|
||||
#define RyujinxHeader
|
||||
|
19
src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift
Normal file
19
src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// IsJITEnabled.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 10/02/2025.
|
||||
//
|
||||
|
||||
|
||||
|
||||
func isJITEnabled() -> Bool {
|
||||
var flags: Int = 0
|
||||
|
||||
csops(getpid(), 0, &flags, sizeof(flags))
|
||||
return (Int32(flags) & CS_DEBUGGED) != 0;
|
||||
}
|
||||
|
||||
func sizeof<T>(_ value: T) -> Int {
|
||||
return MemoryLayout<T>.size
|
||||
}
|
78
src/MeloNX/MeloNX/App/Core/JIT/JitStreamerEB/EnableJIT.swift
Normal file
78
src/MeloNX/MeloNX/App/Core/JIT/JitStreamerEB/EnableJIT.swift
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// EnableJIT.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 10/02/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
func enableJITEB() {
|
||||
guard let bundleID = Bundle.main.bundleIdentifier else {
|
||||
return
|
||||
}
|
||||
let address = URL(string: "http://[fd00::]:9172/launch_app/\(bundleID)")!
|
||||
|
||||
let task = URLSession.shared.dataTask(with: address) { data, response, error in
|
||||
if error != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
showLaunchAppAlert(jsonData: data!, in: UIApplication.shared.windows.last!.rootViewController!)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
task.resume()
|
||||
}
|
||||
|
||||
struct LaunchApp: Codable {
|
||||
let ok: Bool
|
||||
let error: String?
|
||||
let launching: Bool
|
||||
let position: Int?
|
||||
let mounting: Bool
|
||||
}
|
||||
|
||||
func showLaunchAppAlert(jsonData: Data, in viewController: UIViewController) {
|
||||
do {
|
||||
let result = try JSONDecoder().decode(LaunchApp.self, from: jsonData)
|
||||
|
||||
var message = ""
|
||||
|
||||
if let error = result.error {
|
||||
message = "Error: \(error)"
|
||||
} else if result.mounting {
|
||||
message = "App is mounting..."
|
||||
} else if result.launching {
|
||||
message = "App is launching..."
|
||||
} else {
|
||||
message = "App launch status unknown."
|
||||
}
|
||||
|
||||
if let position = result.position {
|
||||
message += "\nPosition: \(position)"
|
||||
}
|
||||
|
||||
let alert = UIAlertController(title: "Launch Status", message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
||||
|
||||
DispatchQueue.main.async {
|
||||
viewController.present(alert, animated: true)
|
||||
}
|
||||
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Decoding Error", message: error.localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
||||
|
||||
DispatchQueue.main.async {
|
||||
viewController.present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
@ -60,15 +60,6 @@ void ShowAlert(NSString* title, NSString* message, _Bool* showok)
|
||||
|
||||
__attribute__((constructor)) static void entry(int argc, char **argv)
|
||||
{
|
||||
if (isJITEnabled()) {
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setBool:YES forKey:@"JIT"];
|
||||
[defaults synchronize]; // Ensure the value is saved immediately
|
||||
} else {
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setBool:NO forKey:@"JIT"];
|
||||
[defaults synchronize]; // Ensure the value is saved immediately
|
||||
}
|
||||
|
||||
if (getEntitlementValue(@"com.apple.developer.kernel.increased-memory-limit")) {
|
||||
NSLog(@"Entitlement Does Exist");
|
||||
|
@ -32,7 +32,9 @@ struct LaunchGameIntentDef: AppIntent {
|
||||
|
||||
let ryujinx = Ryujinx.shared.games
|
||||
|
||||
let urlString = "melonx://game?\(ryujinx.contains(where: { $0.titleName.localizedCaseInsensitiveContains(gameName) }) ? "name" : "id")=\(gameName)"
|
||||
let name = findClosestGameName(input: gameName, games: ryujinx.flatMap(\.titleName))
|
||||
|
||||
let urlString = "melonx://game?name=\(name ?? gameName)"
|
||||
print(urlString)
|
||||
if let url = URL(string: urlString) {
|
||||
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
||||
@ -40,14 +42,44 @@ struct LaunchGameIntentDef: AppIntent {
|
||||
|
||||
return .result()
|
||||
}
|
||||
|
||||
func levenshteinDistance(_ a: String, _ b: String) -> Int {
|
||||
let aCount = a.count
|
||||
let bCount = b.count
|
||||
var matrix = [[Int]](repeating: [Int](repeating: 0, count: bCount + 1), count: aCount + 1)
|
||||
|
||||
for i in 0...aCount {
|
||||
matrix[i][0] = i
|
||||
}
|
||||
|
||||
for j in 0...bCount {
|
||||
matrix[0][j] = j
|
||||
}
|
||||
|
||||
for i in 1...aCount {
|
||||
for j in 1...bCount {
|
||||
let cost = a[a.index(a.startIndex, offsetBy: i - 1)] == b[b.index(b.startIndex, offsetBy: j - 1)] ? 0 : 1
|
||||
matrix[i][j] = min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost)
|
||||
}
|
||||
}
|
||||
|
||||
return matrix[aCount][bCount]
|
||||
}
|
||||
|
||||
func findClosestGameName(input: String, games: [String]) -> String? {
|
||||
let closestGame = games.min { a, b in
|
||||
let distanceA = levenshteinDistance(input, a)
|
||||
let distanceB = levenshteinDistance(input, b)
|
||||
return distanceA < distanceB
|
||||
}
|
||||
return closestGame
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
struct GameOptionsProvider: DynamicOptionsProvider {
|
||||
func results() async throws -> [String] {
|
||||
Ryujinx.shared.games = Ryujinx.shared.loadGames()
|
||||
|
||||
let dynamicGames = Ryujinx.shared.games
|
||||
let dynamicGames = Ryujinx.shared.loadGames()
|
||||
|
||||
return dynamicGames.map { $0.titleName }
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ struct ContentView: View {
|
||||
@AppStorage("useTrollStore") var useTrollStore: Bool = false
|
||||
|
||||
// JIT
|
||||
@AppStorage("JIT") var isJITEnabled: Bool = false
|
||||
@AppStorage("jitStreamerEB") var jitStreamerEB: Bool = false
|
||||
|
||||
// Other Configuration
|
||||
@State var isMK8: Bool = false
|
||||
@ -71,7 +71,7 @@ struct ContentView: View {
|
||||
|
||||
_settings = State(initialValue: defaultSettings)
|
||||
|
||||
print("JIT Enabled: \(isJITEnabled)")
|
||||
print("JIT Enabled: \(isJITEnabled())")
|
||||
|
||||
initializeSDL()
|
||||
}
|
||||
@ -122,6 +122,7 @@ struct ContentView: View {
|
||||
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||
components.host == "game" {
|
||||
if let text = components.queryItems?.first(where: { $0.name == "id" })?.value {
|
||||
|
||||
game = Ryujinx.shared.games.first(where: { $0.titleId == text })
|
||||
} else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value {
|
||||
game = Ryujinx.shared.games.first(where: { $0.titleName == text })
|
||||
@ -247,12 +248,16 @@ struct ContentView: View {
|
||||
|
||||
))
|
||||
|
||||
let isJIT = UserDefaults.standard.bool(forKey: "JIT-ENABLED")
|
||||
let isJIT = isJITEnabled()
|
||||
|
||||
if !isJIT, useTrollStore {
|
||||
askForJIT()
|
||||
}
|
||||
|
||||
if !isJIT, jitStreamerEB {
|
||||
enableJITEB()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,10 @@ struct EmulationView: View {
|
||||
if isAirplaying {
|
||||
Text("")
|
||||
.onAppear {
|
||||
Air.play(AnyView(MetalView().ignoresSafeArea()))
|
||||
Air.play(AnyView(MetalView(airplay: true).ignoresSafeArea()))
|
||||
}
|
||||
} else {
|
||||
MetalView() // The Emulation View
|
||||
MetalView(airplay: false) // The Emulation View
|
||||
.ignoresSafeArea()
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
|
@ -10,8 +10,9 @@ import MetalKit
|
||||
|
||||
struct MetalView: UIViewRepresentable {
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
var airplay: Bool // just in case :3
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
let metalLayer = Ryujinx.shared.metalLayer!
|
||||
metalLayer.frame = Ryujinx.shared.emulationUIView.bounds
|
||||
Ryujinx.shared.emulationUIView.contentScaleFactor = metalLayer.contentsScale // Right size and Fix Touch :3
|
||||
|
@ -18,6 +18,8 @@ struct SettingsView: View {
|
||||
@Binding var onscreencontroller: Controller
|
||||
@AppStorage("useTrollStore") var useTrollStore: Bool = false
|
||||
|
||||
@AppStorage("jitStreamerEB") var jitStreamerEB: Bool = false
|
||||
|
||||
@AppStorage("ignoreJIT") var ignoreJIT: Bool = false
|
||||
|
||||
var memoryManagerModes = [
|
||||
@ -354,21 +356,50 @@ struct SettingsView: View {
|
||||
}
|
||||
.tint(.blue)
|
||||
|
||||
Toggle(isOn: $useTrollStore) {
|
||||
labelWithIcon("TrollStore", iconName: "troll.svg")
|
||||
}
|
||||
.tint(.blue)
|
||||
if #available(iOS 17.0.1, *) {
|
||||
Toggle(isOn: $jitStreamerEB) {
|
||||
labelWithIcon("JitStreamer EB", iconName: "bolt.heart")
|
||||
}
|
||||
.tint(.blue)
|
||||
.contextMenu {
|
||||
Button {
|
||||
if let mainWindow = UIApplication.shared.windows.last {
|
||||
let alertController = UIAlertController(title: "About JitStreamer EB", message: "JitStreamer EB is an Amazing Application to Enable JIT on the go, made by one of the best iOS developers of all time jkcoxson <3", preferredStyle: .alert)
|
||||
|
||||
Toggle(isOn: $config.debuglogs) {
|
||||
labelWithIcon("Debug Logs", iconName: "exclamationmark.bubble")
|
||||
}
|
||||
.tint(.blue)
|
||||
let learnMoreButton = UIAlertAction(title: "Learn More", style: .default) {_ in
|
||||
UIApplication.shared.open(URL(string: "https://jkcoxson.com/jitstreamer")!)
|
||||
}
|
||||
alertController.addAction(learnMoreButton)
|
||||
|
||||
Toggle(isOn: $config.tracelogs) {
|
||||
labelWithIcon("Trace Logs", iconName: "waveform.path")
|
||||
}
|
||||
.tint(.blue)
|
||||
let doneButton = UIAlertAction(title: "Done", style: .cancel, handler: nil)
|
||||
alertController.addAction(doneButton)
|
||||
|
||||
mainWindow.rootViewController?.present(alertController, animated: true)
|
||||
}
|
||||
} label: {
|
||||
Text("About")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Toggle(isOn: $useTrollStore) {
|
||||
labelWithIcon("TrollStore", iconName: "troll.svg")
|
||||
}
|
||||
.tint(.blue)
|
||||
}
|
||||
|
||||
DisclosureGroup {
|
||||
Toggle(isOn: $config.debuglogs) {
|
||||
labelWithIcon("Debug Logs", iconName: "exclamationmark.bubble")
|
||||
}
|
||||
.tint(.blue)
|
||||
|
||||
Toggle(isOn: $config.tracelogs) {
|
||||
labelWithIcon("Trace Logs", iconName: "waveform.path")
|
||||
}
|
||||
.tint(.blue)
|
||||
} label: {
|
||||
Text("Logs")
|
||||
}
|
||||
|
||||
} header: {
|
||||
Text("Miscellaneous Options")
|
||||
@ -381,6 +412,8 @@ struct SettingsView: View {
|
||||
|
||||
// Advanced
|
||||
Section {
|
||||
labelWithIcon("JIT Acquisition: \(isJITEnabled() ? "Aquired" : "Not Aquired" )", iconName: "bolt.fill")
|
||||
|
||||
DisclosureGroup {
|
||||
|
||||
Toggle(isOn: $mVKPreFillBuffer) {
|
||||
|
@ -16,6 +16,7 @@ struct MeloNXApp: App {
|
||||
|
||||
@State var showed = false
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@State var alert: UIAlertController? = nil
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
@ -65,7 +66,14 @@ struct MeloNXApp: App {
|
||||
withAnimation {
|
||||
showed = false
|
||||
}
|
||||
showDMCAAlert()
|
||||
if !(alert?.isViewLoaded ?? false) {
|
||||
alert = showDMCAAlert()
|
||||
}
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
alert?.dismiss(animated: true)
|
||||
showed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,7 +93,7 @@ struct MeloNXApp: App {
|
||||
}
|
||||
|
||||
|
||||
func showAlert() {
|
||||
func showAlert() -> UIAlertController? {
|
||||
// Create the alert controller
|
||||
if let mainWindow = UIApplication.shared.windows.last {
|
||||
let alertController = UIAlertController(title: "Enter license", message: "Enter license key:", preferredStyle: .alert)
|
||||
@ -118,23 +126,26 @@ struct MeloNXApp: App {
|
||||
|
||||
// Present the alert
|
||||
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
|
||||
} else {
|
||||
|
||||
return alertController
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func showDMCAAlert() {
|
||||
DispatchQueue.main.async {
|
||||
if let mainWindow = UIApplication.shared.windows.last {
|
||||
let alertController = UIAlertController(title: "Unauthorized Copy Notice", message: "This app was illegally leaked. Please report the download on the MeloNX Discord. In the meantime, check out Pomelo! \n -Stossy11", preferredStyle: .alert)
|
||||
func showDMCAAlert() -> UIAlertController? {
|
||||
if let mainWindow = UIApplication.shared.windows.last {
|
||||
let alertController = UIAlertController(title: "Unauthorized Copy Notice", message: "This app was illegally leaked. Please report the download on the MeloNX Discord. In the meantime, check out Pomelo! \n -Stossy11", preferredStyle: .alert)
|
||||
|
||||
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
|
||||
} else {
|
||||
// uhoh
|
||||
}
|
||||
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
|
||||
|
||||
return alertController
|
||||
} else {
|
||||
// uhoh
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user