forked from MeloNX/MeloNX
Add Config Editor
This commit is contained in:
parent
098467f3f3
commit
e02927cadb
Binary file not shown.
@ -23,7 +23,7 @@
|
|||||||
<BreakpointActionProxy
|
<BreakpointActionProxy
|
||||||
ActionExtensionID = "Xcode.BreakpointAction.DebuggerCommand">
|
ActionExtensionID = "Xcode.BreakpointAction.DebuggerCommand">
|
||||||
<ActionContent
|
<ActionContent
|
||||||
consoleCommand = "process handle --stop false SIGUSR1">
|
consoleCommand = "process handle -s false -n false">
|
||||||
</ActionContent>
|
</ActionContent>
|
||||||
</BreakpointActionProxy>
|
</BreakpointActionProxy>
|
||||||
</Actions>
|
</Actions>
|
||||||
@ -45,5 +45,30 @@
|
|||||||
landmarkType = "7">
|
landmarkType = "7">
|
||||||
</BreakpointContent>
|
</BreakpointContent>
|
||||||
</BreakpointProxy>
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "900AC928-2B0F-4BBE-A47D-D6B17570E9BB"
|
||||||
|
shouldBeEnabled = "Yes"
|
||||||
|
nameForDebugger = "SIGUSR1-Disable"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "MeloNX/Views/ContentView.swift"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "131"
|
||||||
|
endingLineNumber = "131"
|
||||||
|
landmarkName = "start(displayid:)"
|
||||||
|
landmarkType = "7">
|
||||||
|
<Actions>
|
||||||
|
<BreakpointActionProxy
|
||||||
|
ActionExtensionID = "Xcode.BreakpointAction.DebuggerCommand">
|
||||||
|
<ActionContent
|
||||||
|
consoleCommand = "process handle --stop false SIGUSR1">
|
||||||
|
</ActionContent>
|
||||||
|
</BreakpointActionProxy>
|
||||||
|
</Actions>
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
</Breakpoints>
|
</Breakpoints>
|
||||||
</Bucket>
|
</Bucket>
|
||||||
|
@ -20,25 +20,48 @@ class Ryujinx {
|
|||||||
|
|
||||||
@Published var controllerMap: [Controller] = []
|
@Published var controllerMap: [Controller] = []
|
||||||
|
|
||||||
public struct Configuration {
|
public struct Configuration : Codable {
|
||||||
let gamepath: String
|
var gamepath: String
|
||||||
let inputids: [String]
|
var inputids: [String]
|
||||||
let debuglogs: Bool
|
var debuglogs: Bool
|
||||||
let tracelogs: Bool
|
var tracelogs: Bool
|
||||||
let listinputids: Bool
|
var listinputids: Bool
|
||||||
let fullscreen: Bool
|
var fullscreen: Bool
|
||||||
|
var hostMappedMemory: Bool
|
||||||
|
var disableVSync: Bool
|
||||||
|
var disableShaderCache: Bool
|
||||||
|
var disableDockedMode: Bool
|
||||||
|
var enableTextureRecompression: Bool
|
||||||
var additionalArgs: [String]
|
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.gamepath = gamepath
|
||||||
|
self.inputids = inputids
|
||||||
self.debuglogs = debuglogs
|
self.debuglogs = debuglogs
|
||||||
self.tracelogs = tracelogs
|
self.tracelogs = tracelogs
|
||||||
self.inputids = inputids
|
|
||||||
self.listinputids = listinputids
|
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.additionalArgs = additionalArgs
|
||||||
|
self.hostMappedMemory = hostMappedMemory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func start(with config: Configuration) throws {
|
func start(with config: Configuration) throws {
|
||||||
guard !isRunning else {
|
guard !isRunning else {
|
||||||
@ -94,25 +117,36 @@ class Ryujinx {
|
|||||||
args.append("Vulkan")
|
args.append("Vulkan")
|
||||||
|
|
||||||
// Fixes the Stubs.DispatchLoop Crash
|
// Fixes the Stubs.DispatchLoop Crash
|
||||||
// args.append(contentsOf: ["--memory-manager-mode", "HostMapped"])
|
if config.hostMappedMemory {
|
||||||
args.append(contentsOf: ["--memory-manager-mode", "SoftwarePageTable"])
|
args.append(contentsOf: ["--memory-manager-mode", "HostMapped"])
|
||||||
|
} else {
|
||||||
|
args.append(contentsOf: ["--memory-manager-mode", "SoftwarePageTable"])
|
||||||
|
}
|
||||||
if config.fullscreen {
|
if config.fullscreen {
|
||||||
// args.append(contentsOf: ["--fullscreen", String(config.fullscreen)])
|
|
||||||
args.append(contentsOf: ["--exclusive-fullscreen", String(config.fullscreen)])
|
args.append(contentsOf: ["--exclusive-fullscreen", String(config.fullscreen)])
|
||||||
args.append(contentsOf: ["--exclusive-fullscreen-width", "1280"])
|
args.append(contentsOf: ["--exclusive-fullscreen-width", "1280"])
|
||||||
args.append(contentsOf: ["--exclusive-fullscreen-height", "720"])
|
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"])
|
// Adding default args directly into additionalArgs
|
||||||
args.append(contentsOf: ["--disable-docked-mode", "true"])
|
if config.disableVSync {
|
||||||
args.append(contentsOf: ["--enable-texture-recompression", "true"])
|
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 {
|
if config.debuglogs {
|
||||||
args.append(contentsOf: ["--enable-debug-logs", String(config.debuglogs)])
|
args.append(contentsOf: ["--enable-debug-logs"])
|
||||||
}
|
}
|
||||||
if config.tracelogs {
|
if config.tracelogs {
|
||||||
args.append(contentsOf: ["--enable-trace-logs", String(config.tracelogs)])
|
args.append(contentsOf: ["--enable-trace-logs"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// List the input ids
|
// List the input ids
|
||||||
|
@ -20,6 +20,7 @@ struct ContentView: View {
|
|||||||
@State var game: URL? = nil
|
@State var game: URL? = nil
|
||||||
@State var controllersList: [Controller] = []
|
@State var controllersList: [Controller] = []
|
||||||
@State var currentControllers: [Controller] = []
|
@State var currentControllers: [Controller] = []
|
||||||
|
@State var config: Ryujinx.Configuration = Ryujinx.Configuration(gamepath: "")
|
||||||
|
|
||||||
@State private var settings: [MoltenVKSettings] = [
|
@State private var settings: [MoltenVKSettings] = [
|
||||||
MoltenVKSettings(string: "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", value: "0"),
|
MoltenVKSettings(string: "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", value: "0"),
|
||||||
@ -77,26 +78,35 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List {
|
List {
|
||||||
Button {
|
Section("Settings") {
|
||||||
controllersList = Ryujinx().getConnectedControllers()
|
NavigationLink {
|
||||||
controllersList.removeAll(where: { $0.id == "0" })
|
SettingsView(config: $config)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Refresh")
|
Text("Config")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ForEach(controllersList, id: \.self) { controller in
|
Section("Controller") {
|
||||||
HStack {
|
Button {
|
||||||
Button {
|
controllersList = Ryujinx().getConnectedControllers()
|
||||||
if currentControllers.contains(where: { $0.id == controller.id }) {
|
controllersList.removeAll(where: { $0.id == "0" })
|
||||||
currentControllers.removeAll(where: { $0.id == controller.id })
|
} label: {
|
||||||
} else {
|
Text("Refresh")
|
||||||
currentControllers.append(controller)
|
}
|
||||||
|
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) {
|
func start(displayid: UInt32) {
|
||||||
|
|
||||||
let config = Ryujinx.Configuration(
|
if let game {
|
||||||
gamepath: game!.path,
|
self.config.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
|
|
||||||
|
|
||||||
)
|
allocateSixGB()
|
||||||
|
|
||||||
|
|
||||||
// Start the emulation
|
|
||||||
do {
|
|
||||||
setupVirtualController()
|
|
||||||
|
|
||||||
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<UInt8>.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() {
|
func patchMakeKeyAndVisible() {
|
||||||
@ -145,10 +166,32 @@ struct ContentView: View {
|
|||||||
|
|
||||||
|
|
||||||
private func setMoltenVKSettings() {
|
private func setMoltenVKSettings() {
|
||||||
|
|
||||||
|
if let configs = loadSettings() {
|
||||||
|
self.config = configs
|
||||||
|
print(configs)
|
||||||
|
}
|
||||||
|
|
||||||
settings.forEach { setting in
|
settings.forEach { setting in
|
||||||
setenv(setting.string, setting.value, 1)
|
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 {
|
extension UIWindow {
|
||||||
|
74
src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift
Normal file
74
src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift
Normal file
@ -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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user