diff --git a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index a2a950573..e534e2930 100644 Binary files a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/MeloNX/MeloNX/Core/DetectJIT/utils.h b/src/MeloNX/MeloNX/Core/DetectJIT/utils.h new file mode 100644 index 000000000..380d18050 --- /dev/null +++ b/src/MeloNX/MeloNX/Core/DetectJIT/utils.h @@ -0,0 +1,27 @@ +#if __has_feature(modules) +@import UIKit; +@import Foundation; +#else +#import "UIKit/UIKit.h" +#import "Foundation/Foundation.h" +#endif + +#define DISPATCH_ASYNC_START dispatch_async(dispatch_get_main_queue(), ^{ +#define DISPATCH_ASYNC_CLOSE }); + +#define PT_TRACE_ME 0 +extern int ptrace(int, pid_t, caddr_t, int); + +#define CS_DEBUGGED 0x10000000 +extern int csops( + pid_t pid, + unsigned int ops, + void *useraddr, + size_t usersize + ); + +extern BOOL getEntitlementValue(NSString *key); +extern BOOL isJITEnabled(void); + +#define DLOG(format, ...) ShowAlert(@"DEBUG", [NSString stringWithFormat:@"\n %s [Line %d] \n %@", __PRETTY_FUNCTION__, __LINE__, [NSString stringWithFormat:format, ##__VA_ARGS__]]) +void ShowAlert(NSString* title, NSString* message, _Bool* showok); diff --git a/src/MeloNX/MeloNX/Core/DetectJIT/utils.m b/src/MeloNX/MeloNX/Core/DetectJIT/utils.m new file mode 100644 index 000000000..664ad43f1 --- /dev/null +++ b/src/MeloNX/MeloNX/Core/DetectJIT/utils.m @@ -0,0 +1,91 @@ +#import "utils.h" + +typedef struct __SecTask * SecTaskRef; +extern CFTypeRef SecTaskCopyValueForEntitlement( + SecTaskRef task, + NSString* entitlement, + CFErrorRef _Nullable *error + ) + __attribute__((weak_import)); + +extern SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator) + __attribute__((weak_import)); + +BOOL getEntitlementValue(NSString *key) +{ + if (SecTaskCreateFromSelf == NULL || SecTaskCopyValueForEntitlement == NULL) + return NO; + SecTaskRef sec_task = SecTaskCreateFromSelf(NULL); + if(!sec_task) return NO; + CFTypeRef value = SecTaskCopyValueForEntitlement(sec_task, key, nil); + if (value != nil) + { + CFRelease(value); + } + CFRelease(sec_task); + return value != nil && [(__bridge id)value boolValue]; +} + +BOOL isJITEnabled(void) +{ + if (getEntitlementValue(@"dynamic-codesigning")) + { + return YES; + } + + int flags; + csops(getpid(), 0, &flags, sizeof(flags)); + return (flags & CS_DEBUGGED) != 0; +} + +void ShowAlert(NSString* title, NSString* message, _Bool* showok) +{ + DISPATCH_ASYNC_START + UIWindow* mainWindow = [[UIApplication sharedApplication] windows].lastObject; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + if (showok) { + [alert addAction:[UIAlertAction actionWithTitle:@"ok!" + style:UIAlertActionStyleDefault + handler:nil]]; + } + [mainWindow.rootViewController presentViewController:alert + animated:true + completion:nil]; + DISPATCH_ASYNC_CLOSE +} + +#import + +__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"); + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setBool:YES forKey:@"increased-memory-limit"]; + [defaults synchronize]; // Ensure the value is saved immediately + } + + if (getEntitlementValue(@"com.apple.developer.kernel.increased-debugging-memory-limit")) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setBool:YES forKey:@"increased-debugging-memory-limit"]; + [defaults synchronize]; // Ensure the value is saved immediately + } + if (getEntitlementValue(@"com.apple.developer.kernel.extended-virtual-addressing")) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setBool:YES forKey:@"extended-virtual-addressing"]; + [defaults synchronize]; // Ensure the value is saved immediately + } + +} diff --git a/src/MeloNX/MeloNX/Core/MetalHUD/MTLHUD.swift b/src/MeloNX/MeloNX/Core/MetalHUD/MTLHUD.swift index d8bc92770..e89e9f6f4 100644 --- a/src/MeloNX/MeloNX/Core/MetalHUD/MTLHUD.swift +++ b/src/MeloNX/MeloNX/Core/MetalHUD/MTLHUD.swift @@ -7,6 +7,7 @@ import Foundation + class MTLHud { var canMetalHud: Bool { diff --git a/src/MeloNX/MeloNX/Views/ContentView.swift b/src/MeloNX/MeloNX/Views/ContentView.swift index f716d2c49..3bcc46b27 100644 --- a/src/MeloNX/MeloNX/Views/ContentView.swift +++ b/src/MeloNX/MeloNX/Views/ContentView.swift @@ -8,6 +8,8 @@ import SwiftUI import SDL2 import GameController +import Darwin +import UIKit struct MoltenVKSettings: Codable, Hashable { let string: String @@ -26,19 +28,24 @@ struct ContentView: View { @State private var settings: [MoltenVKSettings] @State private var isVirtualControllerActive: Bool = false @State var onscreencontroller: Controller = Controller(id: "", name: "") + @AppStorage("JIT") var isJITEnabled: Bool = false + @AppStorage("ignoreJIT") var ignoreJIT: Bool = false // MARK: - Initialization init() { - let defaultConfig = Ryujinx.Configuration(gamepath: "") + let defaultConfig = loadSettings() ?? Ryujinx.Configuration(gamepath: "") _config = State(initialValue: defaultConfig) let defaultSettings: [MoltenVKSettings] = [ - MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "1024"), + MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "2048"), MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", value: "1"), MoltenVKSettings(string: "MVK_CONFIG_RESUME_LOST_DEVICE", value: "1") ] + _settings = State(initialValue: defaultSettings) + print("JIT Enabled: \(isJITEnabled)") + initializeSDL() } @@ -91,12 +98,18 @@ struct ContentView: View { } } - Section("Controller") { + + Section { Button("Refresh", action: refreshControllersList) - Divider() ForEach(controllersList, id: \.self) { controller in controllerRow(for: controller) } + } header: { + Text("Controllers") + } footer: { + Text("If no controllers are selected, the keyboard will be used.") + .font(.footnote) + .foregroundColor(.gray) } } } @@ -148,40 +161,57 @@ struct ContentView: View { } private func setupEmulation() { - virtualController?.disconnect() + if !isJITEnabled { + virtualController?.disconnect() - guard let game = game else { return } - - if controllersList.first(where: { $0 == onscreencontroller}) != nil { controllerCallback = { DispatchQueue.main.async { controllersList = Ryujinx.shared.getConnectedControllers() + print(currentControllers) start(displayid: 1) } } + showVirtualController() } else { - DispatchQueue.main.async { - print(currentControllers) - start(displayid: 1) + showAlert(title: "JIT Not Enabled", message: "JIT is Required for Emulation. Please use a JIT enabler to Enable JIT", showOk: true) { pressedok in + if pressedok, !ignoreJIT { + game = nil + } else if pressedok, ignoreJIT { + virtualController?.disconnect() + controllerCallback = { + DispatchQueue.main.async { + controllersList = Ryujinx.shared.getConnectedControllers() + + print(currentControllers) + start(displayid: 1) + } + } + + + showVirtualController() + } } } } private func refreshControllersList() { - Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in - DispatchQueue.main.async { - controllersList = Ryujinx.shared.getConnectedControllers() - var controller = controllersList.first(where: { $0.name.hasPrefix("Apple")}) - self.onscreencontroller = (controller ?? Controller(id: "", name: "")) - if controllersList.count > 2 { - let controller = controllersList[2] - currentControllers.append(controller) - } else if let controller = controllersList.first(where: { $0.id == onscreencontroller.id }), !controllersList.isEmpty { - currentControllers.append(controller) - } + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in + controllersList = Ryujinx.shared.getConnectedControllers() + + if let onscreen = controllersList.first(where: { $0.name.hasPrefix("Apple")}) { + self.onscreencontroller = onscreen + } + + controllersList.removeAll(where: { $0.id == "0"}) + + if controllersList.count > 2 { + let controller = controllersList[2] + currentControllers.append(controller) + } else if let controller = controllersList.first(where: { $0.id == onscreencontroller.id }), !controllersList.isEmpty { + currentControllers.append(controller) } } } @@ -194,12 +224,37 @@ struct ContentView: View { } } + + func showAlert(title: String, message: String, showOk: Bool, completion: @escaping (Bool) -> Void) { + DispatchQueue.main.async { + if let mainWindow = UIApplication.shared.windows.last { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + + if showOk { + let okAction = UIAlertAction(title: "OK", style: .default) { _ in + completion(true) + } + + alert.addAction(okAction) + } else { + completion(false) + } + + mainWindow.rootViewController?.present(alert, animated: true, completion: nil) + } + } + } + + private func start(displayid: UInt32) { guard let game else { return } config.gamepath = game.path - config.inputids = currentControllers.map(\.id) + config.inputids = Array(Set(currentControllers.map(\.id))) + if config.inputids.isEmpty { + config.inputids.append("0") + } do { try Ryujinx.shared.start(with: config) @@ -207,12 +262,9 @@ struct ContentView: View { print("Error: \(error.localizedDescription)") } } - + private func setMoltenVKSettings() { - if let configs = loadSettings() { - self.config = configs - } settings.forEach { setting in setenv(setting.string, setting.value, 1) @@ -234,3 +286,4 @@ func loadSettings() -> Ryujinx.Configuration? { return nil } } + diff --git a/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift index ddd89965f..93012d024 100644 --- a/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift +++ b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift @@ -10,6 +10,7 @@ import SwiftUI struct SettingsView: View { @Binding var config: Ryujinx.Configuration @Binding var MoltenVKSettings: [MoltenVKSettings] + @AppStorage("ignoreJIT") var ignoreJIT: Bool = false var memoryManagerModes = [ ("HostMapped", "Host (fast)"), @@ -31,6 +32,7 @@ struct SettingsView: View { Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression) Toggle("Disable Docked Mode", isOn: $config.disableDockedMode) Resolution(value: $config.resscale) + Toggle("Enable Metal HUD", isOn: $metalHUDEnabled) .onChange(of: metalHUDEnabled) { newValue in if newValue { @@ -70,7 +72,7 @@ struct SettingsView: View { //TextField("Game Path", text: $config.gamepath) Text("PageSize \(String(Int(getpagesize())))") - + Toggle("Ignore JIT Enabeld Popup", isOn: $ignoreJIT) TextField("Additional Arguments", text: Binding( get: { config.additionalArgs.joined(separator: ", ")