Add Built in JitStreamer-EB implementation, rework the shortcut and more

This commit is contained in:
Stossy11 2025-02-10 23:27:06 +11:00
parent 007cb026a4
commit c3ade6f5cd
11 changed files with 214 additions and 43 deletions

View File

@ -6,6 +6,7 @@
// //
#define DRM 1 #define DRM 1
#define CS_DEBUGGED 0x10000000
#ifndef RyujinxHeader #ifndef RyujinxHeader
#define RyujinxHeader #define RyujinxHeader

View 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
}

View 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)
}
}
}

View File

@ -60,15 +60,6 @@ void ShowAlert(NSString* title, NSString* message, _Bool* showok)
__attribute__((constructor)) static void entry(int argc, char **argv) __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")) { if (getEntitlementValue(@"com.apple.developer.kernel.increased-memory-limit")) {
NSLog(@"Entitlement Does Exist"); NSLog(@"Entitlement Does Exist");

View File

@ -32,7 +32,9 @@ struct LaunchGameIntentDef: AppIntent {
let ryujinx = Ryujinx.shared.games 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) print(urlString)
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil) UIApplication.shared.open(url, options: [:], completionHandler: nil)
@ -40,14 +42,44 @@ struct LaunchGameIntentDef: AppIntent {
return .result() 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, *) @available(iOS 16.0, *)
struct GameOptionsProvider: DynamicOptionsProvider { struct GameOptionsProvider: DynamicOptionsProvider {
func results() async throws -> [String] { func results() async throws -> [String] {
Ryujinx.shared.games = Ryujinx.shared.loadGames() let dynamicGames = Ryujinx.shared.loadGames()
let dynamicGames = Ryujinx.shared.games
return dynamicGames.map { $0.titleName } return dynamicGames.map { $0.titleName }
} }

View File

@ -35,7 +35,7 @@ struct ContentView: View {
@AppStorage("useTrollStore") var useTrollStore: Bool = false @AppStorage("useTrollStore") var useTrollStore: Bool = false
// JIT // JIT
@AppStorage("JIT") var isJITEnabled: Bool = false @AppStorage("jitStreamerEB") var jitStreamerEB: Bool = false
// Other Configuration // Other Configuration
@State var isMK8: Bool = false @State var isMK8: Bool = false
@ -71,7 +71,7 @@ struct ContentView: View {
_settings = State(initialValue: defaultSettings) _settings = State(initialValue: defaultSettings)
print("JIT Enabled: \(isJITEnabled)") print("JIT Enabled: \(isJITEnabled())")
initializeSDL() initializeSDL()
} }
@ -122,6 +122,7 @@ struct ContentView: View {
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
components.host == "game" { components.host == "game" {
if let text = components.queryItems?.first(where: { $0.name == "id" })?.value { if let text = components.queryItems?.first(where: { $0.name == "id" })?.value {
game = Ryujinx.shared.games.first(where: { $0.titleId == text }) game = Ryujinx.shared.games.first(where: { $0.titleId == text })
} else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value { } else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value {
game = Ryujinx.shared.games.first(where: { $0.titleName == text }) 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 { if !isJIT, useTrollStore {
askForJIT() askForJIT()
} }
if !isJIT, jitStreamerEB {
enableJITEB()
}
} }
} }

View File

@ -17,10 +17,10 @@ struct EmulationView: View {
if isAirplaying { if isAirplaying {
Text("") Text("")
.onAppear { .onAppear {
Air.play(AnyView(MetalView().ignoresSafeArea())) Air.play(AnyView(MetalView(airplay: true).ignoresSafeArea()))
} }
} else { } else {
MetalView() // The Emulation View MetalView(airplay: false) // The Emulation View
.ignoresSafeArea() .ignoresSafeArea()
.edgesIgnoringSafeArea(.all) .edgesIgnoringSafeArea(.all)
} }

View File

@ -10,8 +10,9 @@ import MetalKit
struct MetalView: UIViewRepresentable { struct MetalView: UIViewRepresentable {
var airplay: Bool // just in case :3
func makeUIView(context: Context) -> UIView { func makeUIView(context: Context) -> UIView {
let metalLayer = Ryujinx.shared.metalLayer! let metalLayer = Ryujinx.shared.metalLayer!
metalLayer.frame = Ryujinx.shared.emulationUIView.bounds metalLayer.frame = Ryujinx.shared.emulationUIView.bounds
Ryujinx.shared.emulationUIView.contentScaleFactor = metalLayer.contentsScale // Right size and Fix Touch :3 Ryujinx.shared.emulationUIView.contentScaleFactor = metalLayer.contentsScale // Right size and Fix Touch :3

View File

@ -18,6 +18,8 @@ struct SettingsView: View {
@Binding var onscreencontroller: Controller @Binding var onscreencontroller: Controller
@AppStorage("useTrollStore") var useTrollStore: Bool = false @AppStorage("useTrollStore") var useTrollStore: Bool = false
@AppStorage("jitStreamerEB") var jitStreamerEB: Bool = false
@AppStorage("ignoreJIT") var ignoreJIT: Bool = false @AppStorage("ignoreJIT") var ignoreJIT: Bool = false
var memoryManagerModes = [ var memoryManagerModes = [
@ -354,21 +356,50 @@ struct SettingsView: View {
} }
.tint(.blue) .tint(.blue)
Toggle(isOn: $useTrollStore) { if #available(iOS 17.0.1, *) {
labelWithIcon("TrollStore", iconName: "troll.svg") 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)
let learnMoreButton = UIAlertAction(title: "Learn More", style: .default) {_ in
UIApplication.shared.open(URL(string: "https://jkcoxson.com/jitstreamer")!)
}
alertController.addAction(learnMoreButton)
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)
} }
.tint(.blue)
Toggle(isOn: $config.debuglogs) { DisclosureGroup {
labelWithIcon("Debug Logs", iconName: "exclamationmark.bubble") 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")
} }
.tint(.blue)
Toggle(isOn: $config.tracelogs) {
labelWithIcon("Trace Logs", iconName: "waveform.path")
}
.tint(.blue)
} header: { } header: {
Text("Miscellaneous Options") Text("Miscellaneous Options")
@ -381,6 +412,8 @@ struct SettingsView: View {
// Advanced // Advanced
Section { Section {
labelWithIcon("JIT Acquisition: \(isJITEnabled() ? "Aquired" : "Not Aquired" )", iconName: "bolt.fill")
DisclosureGroup { DisclosureGroup {
Toggle(isOn: $mVKPreFillBuffer) { Toggle(isOn: $mVKPreFillBuffer) {

View File

@ -16,6 +16,7 @@ struct MeloNXApp: App {
@State var showed = false @State var showed = false
@Environment(\.scenePhase) var scenePhase @Environment(\.scenePhase) var scenePhase
@State var alert: UIAlertController? = nil
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
@ -65,7 +66,14 @@ struct MeloNXApp: App {
withAnimation { withAnimation {
showed = false 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 // Create the alert controller
if let mainWindow = UIApplication.shared.windows.last { if let mainWindow = UIApplication.shared.windows.last {
let alertController = UIAlertController(title: "Enter license", message: "Enter license key:", preferredStyle: .alert) let alertController = UIAlertController(title: "Enter license", message: "Enter license key:", preferredStyle: .alert)
@ -118,23 +126,26 @@ struct MeloNXApp: App {
// Present the alert // Present the alert
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil) mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
return alertController
} else { } else {
return nil
} }
} }
} }
func showDMCAAlert() { func showDMCAAlert() -> UIAlertController? {
DispatchQueue.main.async { if let mainWindow = UIApplication.shared.windows.last {
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)
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)
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
} else { return alertController
// uhoh } else {
} // uhoh
return nil
} }
} }