diff --git a/MeloNX-XC/MeloNX.xcodeproj/project.pbxproj b/MeloNX-XC/MeloNX.xcodeproj/project.pbxproj
index 33a8f9a80..a55c8e096 100644
--- a/MeloNX-XC/MeloNX.xcodeproj/project.pbxproj
+++ b/MeloNX-XC/MeloNX.xcodeproj/project.pbxproj
@@ -347,6 +347,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -356,6 +357,8 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
+ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
+ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
@@ -385,6 +388,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -394,6 +398,8 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
+ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
+ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
diff --git a/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate
index 9664c52eb..db72fd225 100644
Binary files a/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/MeloNX-XC/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/MeloNX-XC/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 000000000..5d0175364
--- /dev/null
+++ b/MeloNX-XC/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,6 @@
+
+
+
diff --git a/MeloNX-XC/MeloNX/ContentView.swift b/MeloNX-XC/MeloNX/ContentView.swift
index a20c1545d..4cc494676 100644
--- a/MeloNX-XC/MeloNX/ContentView.swift
+++ b/MeloNX-XC/MeloNX/ContentView.swift
@@ -12,24 +12,31 @@ import GameController
var theWindow: UIWindow? = nil
struct ContentView: View {
+ @State var device: MTLDevice? = MTLCreateSystemDefaultDevice()
@State var gameUrl: URL?
@State var showFileImporter: Bool = false
+ @State var emulationStarted: Bool = false
var body: some View {
- VStack {
- Button {
- showFileImporter.toggle()
- } label: {
- Text("Select Game")
- }
- if let gameUrl {
+ ZStack {
+
+
+ VStack {
+ Text("NX iOS")
Button {
- DispatchQueue.main.async {
- showVirtualController(url: gameUrl)
- }
+ showFileImporter.toggle()
} label: {
- Text("Go!")
+ Text("Select Game")
+ }
+ if let gameUrl {
+ Button {
+ emulationStarted = true
+ gameUrl.startAccessingSecurityScopedResource()
+ showVirtualController(url: gameUrl)
+ } label: {
+ Text("Go!")
+ }
+ .padding(8)
}
- .padding(8)
}
}
.padding()
@@ -55,11 +62,9 @@ func startEmulation(game: URL) {
enableKeyboard: false,
graphicsBackend: "Vulkan"
)
- DispatchQueue.main.async {
- SDL_SetMainReady()
- SDL_iPhoneSetEventPump(SDL_TRUE)
- patchMakeKeyAndVisible()
- }
+ patchMakeKeyAndVisible()
+ SDL_SetMainReady()
+ SDL_iPhoneSetEventPump(SDL_TRUE)
let emulator = RyujinxEmulator()
do {
try emulator.startWithRunLoop(config: config)
@@ -78,14 +83,10 @@ func patchMakeKeyAndVisible() {
extension UIWindow {
@objc func wdb_makeKeyAndVisible() {
print("Making window key and visible...")
- if #available(iOS 13.0, *) {
- self.windowScene = (UIApplication.shared.connectedScenes.first! as! UIWindowScene)
- }
+ self.windowScene = (UIApplication.shared.connectedScenes.first! as! UIWindowScene)
self.wdb_makeKeyAndVisible()
theWindow = self
- if #available(iOS 15.0, *) {
- reconnectVirtualController()
- }
+ reconnectVirtualController()
}
}
diff --git a/MeloNX-XC/MeloNX/Core/Ryujinx.swift b/MeloNX-XC/MeloNX/Core/Ryujinx.swift
index 9082f39dc..6bf39257f 100644
--- a/MeloNX-XC/MeloNX/Core/Ryujinx.swift
+++ b/MeloNX-XC/MeloNX/Core/Ryujinx.swift
@@ -61,13 +61,14 @@ class RyujinxEmulator {
]
*/
- args.append("ryujinx")
-
+ args.append(config.inputPath)
args.append("--graphics-backend")
args.append(config.graphicsBackend)
-
+ args.append(contentsOf: ["--memory-manager-mode", "SoftwarePageTable"])
+ // args.append(contentsOf: ["--fullscreen", "true"])
+ args.append(contentsOf: ["--enable-debug-logs", "true"])
+ args.append(contentsOf: ["--enable-trace-logs", "true"])
// args.append("--input-path")
- args.append(config.inputPath)
args.append(contentsOf: config.additionalArgs)
@@ -82,6 +83,7 @@ class RyujinxEmulator {
var argvPtrs = cArgs
+
let result = ryujinxMain(Int32(args.count), &argvPtrs)
if result != 0 {
@@ -102,16 +104,19 @@ class RyujinxEmulator {
let port = Port()
runLoop.add(port, forMode: .default)
-
- do {
- try Self.start(with: config)
- } catch {
- Self.log("Emulation failed to start: \(error)")
- self.isRunning = false
- return
+
+ DispatchQueue.main.async {
+ do {
+ try Self.start(with: config)
+ } catch {
+ Self.log("Emulation failed to start: \(error)")
+ self.isRunning = false
+ return
+ }
}
+
while self.isRunning && runLoop.run(mode: .default, before: .distantFuture) {
autoreleasepool {
}
@@ -122,6 +127,8 @@ class RyujinxEmulator {
}
emulationThread?.name = "RyujinxEmulationThread"
+ emulationThread?.qualityOfService = .userInteractive
+ emulationThread?.threadPriority = 0.9
emulationThread?.start()
}
diff --git a/MeloNX-XC/MeloNX/Info.plist b/MeloNX-XC/MeloNX/Info.plist
index 0c67376eb..ff579a6ca 100644
--- a/MeloNX-XC/MeloNX/Info.plist
+++ b/MeloNX-XC/MeloNX/Info.plist
@@ -1,5 +1,8 @@
-
+
+ UIFileSharingEnabled
+
+
diff --git a/MeloNX-XC/MeloNX/MetalVIew.swift b/MeloNX-XC/MeloNX/MetalVIew.swift
new file mode 100644
index 000000000..99603bf91
--- /dev/null
+++ b/MeloNX-XC/MeloNX/MetalVIew.swift
@@ -0,0 +1,210 @@
+//
+// MetalVIew.swift
+// MeloNX
+//
+// Created by Stossy11 on 27/10/2024.
+//
+
+import SwiftUI
+import Metal
+import MetalKit
+
+struct MetalView: UIViewRepresentable {
+ let device: MTLDevice?
+ let configure: (UIView) -> Void
+
+ func makeUIView(context: Context) -> SudachiScreenView {
+ let view = SudachiScreenView()
+ configure(view.primaryScreen)
+ return view
+ }
+
+ func updateUIView(_ uiView: SudachiScreenView, context: Context) {
+ //
+ }
+}
+
+
+class SudachiScreenView: UIView {
+ var primaryScreen: UIView!
+ var portraitconstraints = [NSLayoutConstraint]()
+ var landscapeconstraints = [NSLayoutConstraint]()
+ var fullscreenconstraints = [NSLayoutConstraint]()
+ let userDefaults = UserDefaults.standard
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ if userDefaults.bool(forKey: "isfullscreen") {
+ // setupSudachiScreenforcools()
+ setupSudachiScreen2()
+ } else if userDefaults.bool(forKey: "isairplay") {
+ setupSudachiScreen2()
+ } else if userDefaults.bool(forKey: "169fullscreen") { // this is for the 16/9 aspect ratio full screen
+ setupSudachiScreenforcools()
+ } else if UIDevice.current.userInterfaceIdiom == .pad {
+ setupSudachiScreenforiPad()
+ } else {
+ setupSudachiScreen()
+ }
+ }
+
+ required init?(coder: NSCoder) {
+ super.init(coder: coder)
+ if userDefaults.bool(forKey: "isfullscreen") {
+ setupSudachiScreen2()
+ } else if userDefaults.bool(forKey: "isairplay") {
+ setupSudachiScreen2()
+ } else if UIDevice.current.userInterfaceIdiom == .pad {
+ setupSudachiScreenforiPad()
+ } else {
+ setupSudachiScreen()
+ }
+
+ }
+
+
+ func setupSudachiScreen2() {
+ primaryScreen = MTKView(frame: .zero, device: MTLCreateSystemDefaultDevice())
+ primaryScreen.translatesAutoresizingMaskIntoConstraints = false
+ primaryScreen.clipsToBounds = true
+ addSubview(primaryScreen)
+
+ fullscreenconstraints = [
+ primaryScreen.topAnchor.constraint(equalTo: topAnchor),
+ primaryScreen.leadingAnchor.constraint(equalTo: leadingAnchor),
+ primaryScreen.trailingAnchor.constraint(equalTo: trailingAnchor),
+ primaryScreen.bottomAnchor.constraint(equalTo: bottomAnchor)
+ ]
+
+ addConstraints(fullscreenconstraints)
+ }
+
+ func setupSudachiScreenforcools() { // oh god this took a long time, im going insane
+ primaryScreen = MTKView(frame: .zero, device: MTLCreateSystemDefaultDevice())
+ primaryScreen.translatesAutoresizingMaskIntoConstraints = false
+ primaryScreen.clipsToBounds = true
+
+ addSubview(primaryScreen)
+
+ primaryScreen.layer.cornerRadius = 5
+ primaryScreen.layer.masksToBounds = true
+
+
+ NSLayoutConstraint.activate([
+ primaryScreen.centerXAnchor.constraint(equalTo: centerXAnchor),
+ primaryScreen.centerYAnchor.constraint(equalTo: centerYAnchor),
+ primaryScreen.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor),
+ primaryScreen.heightAnchor.constraint(lessThanOrEqualTo: heightAnchor)
+ ])
+
+ let aspectRatio: CGFloat = 16.0/9.0
+ let aspectRatioConstraint = NSLayoutConstraint(
+ item: primaryScreen ?? UIView(),
+ attribute: .width,
+ relatedBy: .equal,
+ toItem: primaryScreen,
+ attribute: .height,
+ multiplier: aspectRatio,
+ constant: 0
+ )
+ aspectRatioConstraint.priority = .required - 1
+ primaryScreen.addConstraint(aspectRatioConstraint)
+
+ let heightConstraint = primaryScreen.heightAnchor.constraint(equalTo: heightAnchor)
+ heightConstraint.priority = .defaultHigh
+ let widthConstraint = primaryScreen.widthAnchor.constraint(equalTo: widthAnchor)
+ widthConstraint.priority = .defaultHigh
+
+ NSLayoutConstraint.activate([heightConstraint, widthConstraint])
+
+ // Make primaryScreen fill container
+ fullscreenconstraints = [
+ primaryScreen.topAnchor.constraint(equalTo: primaryScreen.topAnchor),
+ primaryScreen.bottomAnchor.constraint(equalTo: primaryScreen.bottomAnchor),
+ primaryScreen.leadingAnchor.constraint(equalTo: primaryScreen.leadingAnchor),
+ primaryScreen.trailingAnchor.constraint(equalTo: primaryScreen.trailingAnchor)
+ ]
+
+ NSLayoutConstraint.activate(fullscreenconstraints)
+ }
+
+ func setupSudachiScreenforiPad() {
+ primaryScreen = MTKView(frame: .zero, device: MTLCreateSystemDefaultDevice())
+ primaryScreen.translatesAutoresizingMaskIntoConstraints = false
+ primaryScreen.clipsToBounds = true
+ primaryScreen.layer.borderColor = UIColor.secondarySystemBackground.cgColor
+ primaryScreen.layer.borderWidth = 3
+ primaryScreen.layer.cornerCurve = .continuous
+ primaryScreen.layer.cornerRadius = 10
+ addSubview(primaryScreen)
+
+
+ portraitconstraints = [
+ primaryScreen.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 10),
+ primaryScreen.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 10),
+ primaryScreen.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -10),
+ primaryScreen.heightAnchor.constraint(equalTo: primaryScreen.widthAnchor, multiplier: 9 / 16),
+ ]
+
+ landscapeconstraints = [
+ primaryScreen.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 50),
+ primaryScreen.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -100),
+ primaryScreen.widthAnchor.constraint(equalTo: primaryScreen.heightAnchor, multiplier: 16 / 9),
+ primaryScreen.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor),
+ ]
+
+
+ updateConstraintsForOrientation()
+ }
+
+
+
+ func setupSudachiScreen() {
+ primaryScreen = MTKView(frame: .zero, device: MTLCreateSystemDefaultDevice())
+ primaryScreen.translatesAutoresizingMaskIntoConstraints = false
+ primaryScreen.clipsToBounds = true
+ primaryScreen.layer.borderColor = UIColor.secondarySystemBackground.cgColor
+ primaryScreen.layer.borderWidth = 3
+ primaryScreen.layer.cornerCurve = .continuous
+ primaryScreen.layer.cornerRadius = 10
+ addSubview(primaryScreen)
+
+
+ portraitconstraints = [
+ primaryScreen.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 10),
+ primaryScreen.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 10),
+ primaryScreen.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -10),
+ primaryScreen.heightAnchor.constraint(equalTo: primaryScreen.widthAnchor, multiplier: 9 / 16),
+ ]
+
+ landscapeconstraints = [
+ primaryScreen.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 10),
+ primaryScreen.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -10),
+ primaryScreen.widthAnchor.constraint(equalTo: primaryScreen.heightAnchor, multiplier: 16 / 9),
+ primaryScreen.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor),
+ ]
+
+ updateConstraintsForOrientation()
+ }
+
+ override func layoutSubviews() {
+ super.layoutSubviews()
+ updateConstraintsForOrientation()
+ }
+
+ private func updateConstraintsForOrientation() {
+
+ if userDefaults.bool(forKey: "isfullscreen") {
+ removeConstraints(portraitconstraints)
+ removeConstraints(landscapeconstraints)
+ removeConstraints(fullscreenconstraints)
+ addConstraints(fullscreenconstraints)
+ } else {
+ removeConstraints(portraitconstraints)
+ removeConstraints(landscapeconstraints)
+
+ let isPortrait = UIApplication.shared.statusBarOrientation.isPortrait
+ addConstraints(isPortrait ? portraitconstraints : landscapeconstraints)
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
index d6283eb57..d6d5a0160 100644
--- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
@@ -5,56 +5,63 @@ using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
+using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
{
///
- /// Represent the shared memory shared between applications for input.
+ /// Represents the shared memory used for input, shared between applications.
///
[StructLayout(LayoutKind.Explicit, Size = 0x40000)]
struct SharedMemory
{
+ // Ensure each struct has a defined size and is properly aligned in memory.
+
///
- /// Debug controller.
+ /// Debug controller state (size: approximately 0x400).
///
[FieldOffset(0)]
public RingLifo DebugPad;
///
- /// Touchscreen.
+ /// Touchscreen state (size: approximately 0x3000).
///
[FieldOffset(0x400)]
public RingLifo TouchScreen;
///
- /// Mouse.
+ /// Mouse state (size: approximately 0x400).
///
[FieldOffset(0x3400)]
public RingLifo Mouse;
///
- /// Keyboard.
+ /// Keyboard state (size: approximately 0x400).
///
[FieldOffset(0x3800)]
public RingLifo Keyboard;
///
- /// Nintendo Pads.
+ /// Nintendo Pads (size: approximately 0x800).
///
- [FieldOffset(0x9A00)]
+ [FieldOffset(0x3C00)]
public Array10 Npads;
+ ///
+ /// Creates a SharedMemory instance with each component initialized.
+ ///
public static SharedMemory Create()
{
- SharedMemory result = new()
- {
- DebugPad = RingLifo.Create(),
- TouchScreen = RingLifo.Create(),
- Mouse = RingLifo.Create(),
- Keyboard = RingLifo.Create(),
- };
+ // Initialize each component separately to avoid potential layout issues.
+ SharedMemory result = new SharedMemory();
+
+ result.DebugPad = RingLifo.Create();
+ result.TouchScreen = RingLifo.Create();
+ result.Mouse = RingLifo.Create();
+ result.Keyboard = RingLifo.Create();
+ // Initialize each Npad state in a loop
for (int i = 0; i < result.Npads.Length; i++)
{
result.Npads[i] = NpadState.Create();
diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs
index 0c7112c0a..4083328d0 100644
--- a/src/Ryujinx.HLE/Switch.cs
+++ b/src/Ryujinx.HLE/Switch.cs
@@ -52,7 +52,6 @@ namespace Ryujinx.HLE
Gpu = new GpuContext(Configuration.GpuRenderer);
System = new HOS.Horizon(this);
Statistics = new PerformanceStatistics();
- Hid = new Hid(this, System.HidStorage);
Processes = new ProcessLoader(this);
TamperMachine = new TamperMachine();