forked from MeloNX/MeloNX
cool
This commit is contained in:
parent
ac3958a363
commit
b41251e360
@ -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;
|
||||
|
Binary file not shown.
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "64A8AF27-6696-4D7A-8C62-06216A95ECF0"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
</Bucket>
|
@ -12,10 +12,16 @@ 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 {
|
||||
ZStack {
|
||||
|
||||
|
||||
VStack {
|
||||
Text("NX iOS")
|
||||
Button {
|
||||
showFileImporter.toggle()
|
||||
} label: {
|
||||
@ -23,15 +29,16 @@ struct ContentView: View {
|
||||
}
|
||||
if let gameUrl {
|
||||
Button {
|
||||
DispatchQueue.main.async {
|
||||
emulationStarted = true
|
||||
gameUrl.startAccessingSecurityScopedResource()
|
||||
showVirtualController(url: gameUrl)
|
||||
}
|
||||
} label: {
|
||||
Text("Go!")
|
||||
}
|
||||
.padding(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.fileImporter(isPresented: $showFileImporter, allowedContentTypes: [.item]) { result in
|
||||
switch result {
|
||||
@ -55,11 +62,9 @@ func startEmulation(game: URL) {
|
||||
enableKeyboard: false,
|
||||
graphicsBackend: "Vulkan"
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
patchMakeKeyAndVisible()
|
||||
SDL_SetMainReady()
|
||||
SDL_iPhoneSetEventPump(SDL_TRUE)
|
||||
patchMakeKeyAndVisible()
|
||||
}
|
||||
let emulator = RyujinxEmulator()
|
||||
do {
|
||||
try emulator.startWithRunLoop(config: config)
|
||||
@ -78,15 +83,11 @@ 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.wdb_makeKeyAndVisible()
|
||||
theWindow = self
|
||||
if #available(iOS 15.0, *) {
|
||||
reconnectVirtualController()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
|
@ -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 {
|
||||
@ -103,6 +105,7 @@ class RyujinxEmulator {
|
||||
let port = Port()
|
||||
runLoop.add(port, forMode: .default)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
do {
|
||||
try Self.start(with: config)
|
||||
} catch {
|
||||
@ -110,6 +113,8 @@ class RyujinxEmulator {
|
||||
self.isRunning = false
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
while self.isRunning && runLoop.run(mode: .default, before: .distantFuture) {
|
||||
@ -122,6 +127,8 @@ class RyujinxEmulator {
|
||||
}
|
||||
|
||||
emulationThread?.name = "RyujinxEmulationThread"
|
||||
emulationThread?.qualityOfService = .userInteractive
|
||||
emulationThread?.threadPriority = 0.9
|
||||
emulationThread?.start()
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
<dict>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
210
MeloNX-XC/MeloNX/MetalVIew.swift
Normal file
210
MeloNX-XC/MeloNX/MetalVIew.swift
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent the shared memory shared between applications for input.
|
||||
/// Represents the shared memory used for input, shared between applications.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x40000)]
|
||||
struct SharedMemory
|
||||
{
|
||||
// Ensure each struct has a defined size and is properly aligned in memory.
|
||||
|
||||
/// <summary>
|
||||
/// Debug controller.
|
||||
/// Debug controller state (size: approximately 0x400).
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public RingLifo<DebugPadState> DebugPad;
|
||||
|
||||
/// <summary>
|
||||
/// Touchscreen.
|
||||
/// Touchscreen state (size: approximately 0x3000).
|
||||
/// </summary>
|
||||
[FieldOffset(0x400)]
|
||||
public RingLifo<TouchScreenState> TouchScreen;
|
||||
|
||||
/// <summary>
|
||||
/// Mouse.
|
||||
/// Mouse state (size: approximately 0x400).
|
||||
/// </summary>
|
||||
[FieldOffset(0x3400)]
|
||||
public RingLifo<MouseState> Mouse;
|
||||
|
||||
/// <summary>
|
||||
/// Keyboard.
|
||||
/// Keyboard state (size: approximately 0x400).
|
||||
/// </summary>
|
||||
[FieldOffset(0x3800)]
|
||||
public RingLifo<KeyboardState> Keyboard;
|
||||
|
||||
/// <summary>
|
||||
/// Nintendo Pads.
|
||||
/// Nintendo Pads (size: approximately 0x800).
|
||||
/// </summary>
|
||||
[FieldOffset(0x9A00)]
|
||||
[FieldOffset(0x3C00)]
|
||||
public Array10<NpadState> Npads;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SharedMemory instance with each component initialized.
|
||||
/// </summary>
|
||||
public static SharedMemory Create()
|
||||
{
|
||||
SharedMemory result = new()
|
||||
{
|
||||
DebugPad = RingLifo<DebugPadState>.Create(),
|
||||
TouchScreen = RingLifo<TouchScreenState>.Create(),
|
||||
Mouse = RingLifo<MouseState>.Create(),
|
||||
Keyboard = RingLifo<KeyboardState>.Create(),
|
||||
};
|
||||
// Initialize each component separately to avoid potential layout issues.
|
||||
SharedMemory result = new SharedMemory();
|
||||
|
||||
result.DebugPad = RingLifo<DebugPadState>.Create();
|
||||
result.TouchScreen = RingLifo<TouchScreenState>.Create();
|
||||
result.Mouse = RingLifo<MouseState>.Create();
|
||||
result.Keyboard = RingLifo<KeyboardState>.Create();
|
||||
|
||||
// Initialize each Npad state in a loop
|
||||
for (int i = 0; i < result.Npads.Length; i++)
|
||||
{
|
||||
result.Npads[i] = NpadState.Create();
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user