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 2632406fd..193e5ea48 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.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/src/MeloNX/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index d2944cbe7..de2cee217 100644
--- a/src/MeloNX/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/src/MeloNX/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -23,7 +23,7 @@
+ consoleCommand = "process handle -s false -n false">
@@ -45,5 +45,30 @@
landmarkType = "7">
+
+
+
+
+
+
+
+
+
+
diff --git a/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift b/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift
index 38af03e4c..a910e8949 100644
--- a/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift
+++ b/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift
@@ -20,25 +20,48 @@ class Ryujinx {
@Published var controllerMap: [Controller] = []
- public struct Configuration {
- let gamepath: String
- let inputids: [String]
- let debuglogs: Bool
- let tracelogs: Bool
- let listinputids: Bool
- let fullscreen: Bool
+ public struct Configuration : Codable {
+ var gamepath: String
+ var inputids: [String]
+ var debuglogs: Bool
+ var tracelogs: Bool
+ var listinputids: Bool
+ var fullscreen: Bool
+ var hostMappedMemory: Bool
+ var disableVSync: Bool
+ var disableShaderCache: Bool
+ var disableDockedMode: Bool
+ var enableTextureRecompression: Bool
var additionalArgs: [String]
- init(gamepath: String, additionalArgs: [String] = [], debuglogs: Bool = false, tracelogs: Bool = false, listinputids: Bool = false, inputids: [String] = [], ryufullscreen: Bool = false) {
+ init(gamepath: String,
+ inputids: [String] = [],
+ debuglogs: Bool = false,
+ tracelogs: Bool = false,
+ listinputids: Bool = false,
+ fullscreen: Bool = false,
+ hostMappedMemory: Bool = false,
+ disableVSync: Bool = true,
+ disableShaderCache: Bool = false,
+ disableDockedMode: Bool = true,
+ enableTextureRecompression: Bool = true,
+ additionalArgs: [String] = []) {
self.gamepath = gamepath
+ self.inputids = inputids
self.debuglogs = debuglogs
self.tracelogs = tracelogs
- self.inputids = inputids
self.listinputids = listinputids
- self.fullscreen = ryufullscreen
+ self.fullscreen = fullscreen
+ self.disableVSync = disableVSync
+ self.disableShaderCache = disableShaderCache
+ self.disableDockedMode = disableDockedMode
+ self.enableTextureRecompression = enableTextureRecompression
self.additionalArgs = additionalArgs
+ self.hostMappedMemory = hostMappedMemory
}
}
+
+
func start(with config: Configuration) throws {
guard !isRunning else {
@@ -94,25 +117,36 @@ class Ryujinx {
args.append("Vulkan")
// Fixes the Stubs.DispatchLoop Crash
- // args.append(contentsOf: ["--memory-manager-mode", "HostMapped"])
- args.append(contentsOf: ["--memory-manager-mode", "SoftwarePageTable"])
+ if config.hostMappedMemory {
+ args.append(contentsOf: ["--memory-manager-mode", "HostMapped"])
+ } else {
+ args.append(contentsOf: ["--memory-manager-mode", "SoftwarePageTable"])
+ }
if config.fullscreen {
- // args.append(contentsOf: ["--fullscreen", String(config.fullscreen)])
args.append(contentsOf: ["--exclusive-fullscreen", String(config.fullscreen)])
args.append(contentsOf: ["--exclusive-fullscreen-width", "1280"])
args.append(contentsOf: ["--exclusive-fullscreen-height", "720"])
- // exclusive-fullscreen
}
- args.append(contentsOf: ["--disable-vsync", "true"]) // ios already forces vsync
- args.append(contentsOf: ["--disable-shader-cache", "false"])
- args.append(contentsOf: ["--disable-docked-mode", "true"])
- args.append(contentsOf: ["--enable-texture-recompression", "true"])
+
+ // Adding default args directly into additionalArgs
+ if config.disableVSync {
+ args.append("--disable-vsync")
+ }
+ if config.disableShaderCache {
+ args.append("--disable-shader-cache")
+ }
+ if config.disableDockedMode {
+ args.append("--disable-docked-mode")
+ }
+ if config.enableTextureRecompression {
+ args.append("--enable-texture-recompression")
+ }
if config.debuglogs {
- args.append(contentsOf: ["--enable-debug-logs", String(config.debuglogs)])
+ args.append(contentsOf: ["--enable-debug-logs"])
}
if config.tracelogs {
- args.append(contentsOf: ["--enable-trace-logs", String(config.tracelogs)])
+ args.append(contentsOf: ["--enable-trace-logs"])
}
// List the input ids
diff --git a/src/MeloNX/MeloNX/Views/ContentView.swift b/src/MeloNX/MeloNX/Views/ContentView.swift
index 28adbfbc3..925c1989a 100644
--- a/src/MeloNX/MeloNX/Views/ContentView.swift
+++ b/src/MeloNX/MeloNX/Views/ContentView.swift
@@ -20,6 +20,7 @@ struct ContentView: View {
@State var game: URL? = nil
@State var controllersList: [Controller] = []
@State var currentControllers: [Controller] = []
+ @State var config: Ryujinx.Configuration = Ryujinx.Configuration(gamepath: "")
@State private var settings: [MoltenVKSettings] = [
MoltenVKSettings(string: "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", value: "0"),
@@ -77,26 +78,35 @@ struct ContentView: View {
}
List {
- Button {
- controllersList = Ryujinx().getConnectedControllers()
- controllersList.removeAll(where: { $0.id == "0" })
- } label: {
- Text("Refresh")
+ Section("Settings") {
+ NavigationLink {
+ SettingsView(config: $config)
+ } label: {
+ Text("Config")
+ }
}
- ForEach(controllersList, id: \.self) { controller in
- HStack {
- Button {
- if currentControllers.contains(where: { $0.id == controller.id }) {
- currentControllers.removeAll(where: { $0.id == controller.id })
- } else {
- currentControllers.append(controller)
+ Section("Controller") {
+ Button {
+ controllersList = Ryujinx().getConnectedControllers()
+ controllersList.removeAll(where: { $0.id == "0" })
+ } label: {
+ Text("Refresh")
+ }
+ ForEach(controllersList, id: \.self) { controller in
+ HStack {
+ Button {
+ if currentControllers.contains(where: { $0.id == controller.id }) {
+ currentControllers.removeAll(where: { $0.id == controller.id })
+ } else {
+ currentControllers.append(controller)
+ }
+ } label: {
+ Text(controller.name)
+ }
+ Spacer()
+ if currentControllers.contains(where: { $0.id == controller.id }) {
+ Image(systemName: "checkmark.circle.fill")
}
- } label: {
- Text(controller.name)
- }
- Spacer()
- if currentControllers.contains(where: { $0.id == controller.id }) {
- Image(systemName: "checkmark.circle.fill")
}
}
}
@@ -109,30 +119,41 @@ struct ContentView: View {
func start(displayid: UInt32) {
- let config = Ryujinx.Configuration(
- gamepath: game!.path,
- additionalArgs: [
- // "--display-id", String(displayid)
- ],
- debuglogs: false,
- tracelogs: false,
- listinputids: false,
- inputids: currentControllers.map(\.id),// "1-dc180005-045e-0000-130b-0000ff870001"], // "2-1fd70005-057e-0000-0920-0000ff870000"],
- ryufullscreen: true
+ if let game {
+ self.config.gamepath = game.path
- )
-
-
- // Start the emulation
- do {
- setupVirtualController()
+ allocateSixGB()
- try Ryujinx().start(with: config)
+ // Start the emulation
+ do {
+ setupVirtualController()
+
+ try Ryujinx().start(with: config)
+
+
+ } catch {
+ print("Error \(error.localizedDescription)")
+ }
+ } else {
-
- } catch {
- print("Error \(error.localizedDescription)")
}
+
+ }
+
+ func allocateSixGB() -> UnsafeMutableRawPointer? {
+
+ let physicalMemory = ProcessInfo.processInfo.physicalMemory
+ let totalMemoryInGB = Double(physicalMemory) / (1024 * 1024 * 1024)
+ let mem = totalMemoryInGB - 1
+ print(mem)
+ // Allocate memory
+ let pointer = UnsafeMutableRawPointer.allocate(byteCount: Int(mem), alignment: MemoryLayout.alignment)
+
+ // Optionally initialize the memory
+ pointer.initializeMemory(as: UInt8.self, repeating: 0, count: Int(mem))
+
+ print("Successfully allocated 6GB of memory.")
+ return pointer
}
func patchMakeKeyAndVisible() {
@@ -145,10 +166,32 @@ struct ContentView: View {
private func setMoltenVKSettings() {
+
+ if let configs = loadSettings() {
+ self.config = configs
+ print(configs)
+ }
+
settings.forEach { setting in
setenv(setting.string, setting.value, 1)
}
}
+
+}
+func loadSettings() -> Ryujinx.Configuration? {
+ guard let jsonString = UserDefaults.standard.string(forKey: "config") else {
+ return nil
+ }
+
+ do {
+ let decoder = JSONDecoder()
+ if let data = jsonString.data(using: .utf8) {
+ return try decoder.decode(Ryujinx.Configuration.self, from: data)
+ }
+ } catch {
+ print("Failed to load settings: \(error)")
+ }
+ return nil
}
extension UIWindow {
diff --git a/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift
new file mode 100644
index 000000000..23f8291e7
--- /dev/null
+++ b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift
@@ -0,0 +1,74 @@
+//
+// SettingsView.swift
+// MeloNX
+//
+// Created by Stossy11 on 25/11/2024.
+//
+
+import SwiftUI
+
+struct SettingsView: View {
+ @Binding var config: Ryujinx.Configuration
+
+ var body: some View {
+ Form {
+
+ Section(header: Text("Graphics and Performance")) {
+ Toggle("Fullscreen", isOn: $config.fullscreen)
+ Toggle("Disable V-Sync", isOn: $config.disableVSync)
+ Toggle("Disable Shader Cache", isOn: $config.disableShaderCache)
+ Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression)
+ }
+
+ Section(header: Text("Input Settings")) {
+ Toggle("List Input IDs", isOn: $config.listinputids)
+ Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
+ Toggle("Disable Docked Mode", isOn: $config.disableDockedMode)
+ }
+
+ Section(header: Text("Logging Settings")) {
+ Toggle("Enable Debug Logs", isOn: $config.debuglogs)
+ Toggle("Enable Trace Logs", isOn: $config.tracelogs)
+ }
+
+ Section(header: Text("Game Settings")) {
+ //TextField("Game Path", text: $config.gamepath)
+
+ TextField("Additional Arguments", text: Binding(
+ get: {
+ config.additionalArgs.joined(separator: ", ")
+ },
+ set: { newValue in
+ config.additionalArgs = newValue.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespaces) }
+ }
+ ))
+ }
+ }
+ .onAppear {
+ if let configs = loadSettings() {
+ self.config = configs
+ print(configs)
+ }
+ }
+ .navigationTitle("Settings")
+ .navigationBarItems(trailing: Button("Save") {
+ saveSettings()
+ })
+ }
+
+ func saveSettings() {
+ do {
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = .prettyPrinted // Optional: Makes the JSON easier to read
+ let data = try encoder.encode(config)
+ let jsonString = String(data: data, encoding: .utf8)
+
+ // Save to UserDefaults
+ UserDefaults.standard.set(jsonString, forKey: "config")
+
+ print("Settings saved successfully!")
+ } catch {
+ print("Failed to save settings: \(error)")
+ }
+ }
+}