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 DRM 1
|
||||||
|
#define CS_DEBUGGED 0x10000000
|
||||||
|
|
||||||
#ifndef RyujinxHeader
|
#ifndef RyujinxHeader
|
||||||
#define 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)
|
__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");
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user