A lot of backend changes, New Display UI, StikDebug 2.0 support, TXM Checker, Get Device name from MGCopyAnswer instead of an array and so on
This commit is contained in:
parent
b575c61907
commit
a2229109ca
@ -47,13 +47,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
0xc3, // ret
|
0xc3, // ret
|
||||||
};
|
};
|
||||||
|
|
||||||
using MemoryBlock memGetXcr0 = new((ulong)asmGetXcr0.Length);
|
using MemoryBlock memGetXcr0 = new((ulong)asmGetXcr0.Length, MemoryAllocationFlags.DualMapping);
|
||||||
|
|
||||||
memGetXcr0.Write(0, asmGetXcr0);
|
memGetXcr0.Write(0, asmGetXcr0);
|
||||||
|
|
||||||
memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);
|
memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);
|
||||||
|
|
||||||
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.RxPointer);
|
||||||
|
|
||||||
return fGetXcr0();
|
return fGetXcr0();
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,6 @@
|
|||||||
remoteGlobalIDString = 4E80A98C2CD6F54500029585;
|
remoteGlobalIDString = 4E80A98C2CD6F54500029585;
|
||||||
remoteInfo = MeloNX;
|
remoteInfo = MeloNX;
|
||||||
};
|
};
|
||||||
4EFFCD182DFB766F00F78EA6 /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = 4E80A9852CD6F54500029585 /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = BD43C6212D1B248D003BBC42;
|
|
||||||
remoteInfo = com.Stossy11.MeloNX.RyujinxAg;
|
|
||||||
};
|
|
||||||
BD43C6252D1B249E003BBC42 /* PBXContainerItemProxy */ = {
|
BD43C6252D1B249E003BBC42 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 4E80A9852CD6F54500029585 /* Project object */;
|
containerPortal = 4E80A9852CD6F54500029585 /* Project object */;
|
||||||
@ -109,6 +102,10 @@
|
|||||||
CA0AE31D2D3EECBC00F6D350 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = {
|
CA0AE31D2D3EECBC00F6D350 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = {
|
||||||
isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;
|
isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;
|
||||||
attributesByRelativePath = {
|
attributesByRelativePath = {
|
||||||
|
"Dependencies/Dynamic Libraries/BreakpointJIT.framework" = (
|
||||||
|
CodeSignOnCopy,
|
||||||
|
RemoveHeadersOnCopy,
|
||||||
|
);
|
||||||
"Dependencies/Dynamic Libraries/Hypervisor.framework" = (
|
"Dependencies/Dynamic Libraries/Hypervisor.framework" = (
|
||||||
CodeSignOnCopy,
|
CodeSignOnCopy,
|
||||||
RemoveHeadersOnCopy,
|
RemoveHeadersOnCopy,
|
||||||
@ -120,10 +117,6 @@
|
|||||||
CodeSignOnCopy,
|
CodeSignOnCopy,
|
||||||
RemoveHeadersOnCopy,
|
RemoveHeadersOnCopy,
|
||||||
);
|
);
|
||||||
"Dependencies/Dynamic Libraries/StosJIT.framework" = (
|
|
||||||
CodeSignOnCopy,
|
|
||||||
RemoveHeadersOnCopy,
|
|
||||||
);
|
|
||||||
"Dependencies/Dynamic Libraries/libMoltenVK.dylib" = (
|
"Dependencies/Dynamic Libraries/libMoltenVK.dylib" = (
|
||||||
CodeSignOnCopy,
|
CodeSignOnCopy,
|
||||||
);
|
);
|
||||||
@ -172,13 +165,13 @@
|
|||||||
};
|
};
|
||||||
buildPhase = 4E80AA092CD6FAA800029585 /* Embed Libraries */;
|
buildPhase = 4E80AA092CD6FAA800029585 /* Embed Libraries */;
|
||||||
membershipExceptions = (
|
membershipExceptions = (
|
||||||
|
"Dependencies/Dynamic Libraries/BreakpointJIT.framework",
|
||||||
"Dependencies/Dynamic Libraries/Hypervisor.framework",
|
"Dependencies/Dynamic Libraries/Hypervisor.framework",
|
||||||
"Dependencies/Dynamic Libraries/libavcodec.dylib",
|
"Dependencies/Dynamic Libraries/libavcodec.dylib",
|
||||||
"Dependencies/Dynamic Libraries/libavutil.dylib",
|
"Dependencies/Dynamic Libraries/libavutil.dylib",
|
||||||
"Dependencies/Dynamic Libraries/libMoltenVK.dylib",
|
"Dependencies/Dynamic Libraries/libMoltenVK.dylib",
|
||||||
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib",
|
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib",
|
||||||
"Dependencies/Dynamic Libraries/RyujinxHelper.framework",
|
"Dependencies/Dynamic Libraries/RyujinxHelper.framework",
|
||||||
"Dependencies/Dynamic Libraries/StosJIT.framework",
|
|
||||||
Dependencies/XCFrameworks/libavcodec.xcframework,
|
Dependencies/XCFrameworks/libavcodec.xcframework,
|
||||||
Dependencies/XCFrameworks/libavfilter.xcframework,
|
Dependencies/XCFrameworks/libavfilter.xcframework,
|
||||||
Dependencies/XCFrameworks/libavformat.xcframework,
|
Dependencies/XCFrameworks/libavformat.xcframework,
|
||||||
@ -294,7 +287,6 @@
|
|||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
4EFFCD192DFB766F00F78EA6 /* PBXTargetDependency */,
|
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
4E80A98F2CD6F54500029585 /* MeloNX */,
|
4E80A98F2CD6F54500029585 /* MeloNX */,
|
||||||
@ -492,11 +484,6 @@
|
|||||||
target = 4E80A98C2CD6F54500029585 /* MeloNX */;
|
target = 4E80A98C2CD6F54500029585 /* MeloNX */;
|
||||||
targetProxy = 4E80A9A82CD6F54700029585 /* PBXContainerItemProxy */;
|
targetProxy = 4E80A9A82CD6F54700029585 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
4EFFCD192DFB766F00F78EA6 /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = BD43C6212D1B248D003BBC42 /* com.Stossy11.MeloNX.RyujinxAg */;
|
|
||||||
targetProxy = 4EFFCD182DFB766F00F78EA6 /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
BD43C6262D1B249E003BBC42 /* PBXTargetDependency */ = {
|
BD43C6262D1B249E003BBC42 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = BD43C61D2D1B23AB003BBC42 /* Ryujinx */;
|
target = BD43C61D2D1B23AB003BBC42 /* Ryujinx */;
|
||||||
@ -791,6 +778,10 @@
|
|||||||
"$(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",
|
"$(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",
|
||||||
);
|
);
|
||||||
GCC_OPTIMIZATION_LEVEL = z;
|
GCC_OPTIMIZATION_LEVEL = z;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -1046,6 +1037,10 @@
|
|||||||
"$(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",
|
"$(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 = "$(VERSION)";
|
MARKETING_VERSION = "$(VERSION)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
@ -1207,6 +1202,10 @@
|
|||||||
"$(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",
|
"$(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",
|
||||||
);
|
);
|
||||||
GCC_OPTIMIZATION_LEVEL = z;
|
GCC_OPTIMIZATION_LEVEL = z;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -1462,6 +1461,10 @@
|
|||||||
"$(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",
|
"$(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 = "$(VERSION)";
|
MARKETING_VERSION = "$(VERSION)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
|
Binary file not shown.
@ -65,6 +65,7 @@
|
|||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugXPCServices = "NO"
|
debugXPCServices = "NO"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUFrameCaptureMode = "3"
|
||||||
enableGPUValidationMode = "1"
|
enableGPUValidationMode = "1"
|
||||||
allowLocationSimulation = "YES"
|
allowLocationSimulation = "YES"
|
||||||
queueDebuggingEnabled = "No"
|
queueDebuggingEnabled = "No"
|
||||||
|
52
src/MeloNX/MeloNX/App/Core/Headers/MobileGestalt.h
Normal file
52
src/MeloNX/MeloNX/App/Core/Headers/MobileGestalt.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// MobileGestalt.h
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 11/07/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libMobileGestalt header.
|
||||||
|
* Mobile gestalt functions as a QA system. You ask it a question, and it gives you the answer! :)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 Cykey (David Murray)
|
||||||
|
* Improved by @PoomSmart (2020)
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBMOBILEGESTALT_H_
|
||||||
|
#define LIBMOBILEGESTALT_H_
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma mark - API
|
||||||
|
|
||||||
|
typedef CFPropertyListRef (*MGFuncType)(CFStringRef);
|
||||||
|
|
||||||
|
CFPropertyListRef CallMGCopyAnswer(CFStringRef key) {
|
||||||
|
static MGFuncType fn = NULL;
|
||||||
|
if (!fn) {
|
||||||
|
void *handle = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_LAZY);
|
||||||
|
if (!handle) return NULL;
|
||||||
|
|
||||||
|
char decoded[] = { 'M','G','C','o','p','y','A','n','s','w','e','r', '\0' };
|
||||||
|
fn = (MGFuncType)dlsym(handle, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn ? fn(key) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Device Information
|
||||||
|
|
||||||
|
static const CFStringRef kMGPhysicalHardwareNameString = CFSTR("PhysicalHardwareNameString");
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LIBMOBILEGESTALT_H_ */
|
@ -14,8 +14,7 @@
|
|||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <SDL2/SDL_syswm.h>
|
#include <SDL2/SDL_syswm.h>
|
||||||
#include <StosJIT/StosJIT-Swift.h>
|
#include "MobileGestalt.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -17,7 +17,11 @@ func isJITEnabled() -> Bool {
|
|||||||
return allocateTest()
|
return allocateTest()
|
||||||
}
|
}
|
||||||
|
|
||||||
return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0 ? allocateTest() : false
|
if #available(iOS 19, *) {
|
||||||
|
return checkDebugged()
|
||||||
|
} else {
|
||||||
|
return checkDebugged() && allocateTest()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDebugged() -> Bool {
|
func checkDebugged() -> Bool {
|
||||||
@ -70,3 +74,26 @@ func allocateTest() -> Bool {
|
|||||||
|
|
||||||
return checkMem
|
return checkMem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// thank you nikki (nythepegasus)
|
||||||
|
extension FileManager {
|
||||||
|
func filePath(atPath path: String, withLength length: Int) -> String? {
|
||||||
|
guard let file = try? contentsOfDirectory(atPath: path).filter({ $0.count == length }).first else { return nil }
|
||||||
|
return "\(path)/\(file)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func notnil(_ condition: Any?) -> Bool {
|
||||||
|
if let _ = condition {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension ProcessInfo {
|
||||||
|
var hasTXM: Bool {
|
||||||
|
{ if let boot = FileManager.default.filePath(atPath: "/System/Volumes/Preboot", withLength: 36), let file = FileManager.default.filePath(atPath: "\(boot)/boot", withLength: 96) { return access("\(file)/usr/standalone/firmware/FUD/Ap,TrustedExecutionMonitor.img4", F_OK) == 0 } else { return (FileManager.default.filePath(atPath: "/private/preboot", withLength: 96).map { access("\($0)/usr/standalone/firmware/FUD/Ap,TrustedExecutionMonitor.img4", F_OK) == 0 }) ?? false } }()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// ControllerInfo.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 28/06/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct Controller: Identifiable, Hashable {
|
||||||
|
var id: String
|
||||||
|
var name: String
|
||||||
|
}
|
@ -12,7 +12,8 @@ class NativeController: Hashable, BaseController {
|
|||||||
private var instanceID: SDL_JoystickID = -1
|
private var instanceID: SDL_JoystickID = -1
|
||||||
private var controller: OpaquePointer?
|
private var controller: OpaquePointer?
|
||||||
private var nativeController: GCController
|
private var nativeController: GCController
|
||||||
private var controllerMotionProvider: DSUMotionProvider?
|
private var controllerMotionProvider: ControllerMotionProvider?
|
||||||
|
private var deviceMotionProvider: DeviceMotionProvider?
|
||||||
|
|
||||||
private let controllerHaptics: CHHapticEngine?
|
private let controllerHaptics: CHHapticEngine?
|
||||||
private let rumbleController: RumbleController?
|
private let rumbleController: RumbleController?
|
||||||
@ -24,14 +25,14 @@ class NativeController: Hashable, BaseController {
|
|||||||
var ncontrollerHaptics = nativeController.haptics?.createEngine(withLocality: .default)
|
var ncontrollerHaptics = nativeController.haptics?.createEngine(withLocality: .default)
|
||||||
|
|
||||||
let vendorName = nativeController.vendorName ?? "Unknown"
|
let vendorName = nativeController.vendorName ?? "Unknown"
|
||||||
var usesdeviceHaptics = (ncontrollerHaptics == nil || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")
|
var usesdeviceHaptics = (ncontrollerHaptics == nil && (vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one"))
|
||||||
controllerHaptics = usesdeviceHaptics ? ncontrollerHaptics : try? CHHapticEngine()
|
controllerHaptics = usesdeviceHaptics ? try? CHHapticEngine() : ncontrollerHaptics
|
||||||
|
|
||||||
// Make sure the haptic engine exists before attempting to start it or initialize the controller.
|
// Make sure the haptic engine exists before attempting to start it or initialize the controller.
|
||||||
if let hapticsEngine = controllerHaptics {
|
if let hapticsEngine = controllerHaptics {
|
||||||
do {
|
do {
|
||||||
try hapticsEngine.start()
|
try hapticsEngine.start()
|
||||||
rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: 1.2)
|
rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: usesdeviceHaptics ? 2.0 : 2.5)
|
||||||
} catch {
|
} catch {
|
||||||
rumbleController = nil
|
rumbleController = nil
|
||||||
}
|
}
|
||||||
@ -51,14 +52,22 @@ class NativeController: Hashable, BaseController {
|
|||||||
let vendorName = nativeController.vendorName ?? "Unknown"
|
let vendorName = nativeController.vendorName ?? "Unknown"
|
||||||
var usesdevicemotion = (vendorName.lowercased() == "Joy-Con (l/R)".lowercased() || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")
|
var usesdevicemotion = (vendorName.lowercased() == "Joy-Con (l/R)".lowercased() || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")
|
||||||
|
|
||||||
controllerMotionProvider = usesdevicemotion ? DeviceMotionProvider(slot: slot) : ControllerMotionProvider(controller: nativeController, slot: slot)
|
usesdevicemotion ? (deviceMotionProvider = DeviceMotionProvider(slot: slot)) : (controllerMotionProvider = ControllerMotionProvider(controller: nativeController, slot: slot))
|
||||||
|
|
||||||
if let provider = controllerMotionProvider {
|
if let provider = controllerMotionProvider {
|
||||||
dsuServer.register(provider)
|
dsuServer.register(provider)
|
||||||
|
} else if let provider = deviceMotionProvider {
|
||||||
|
dsuServer.register(provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func tryGetMotionProvider() -> DSUMotionProvider? { return controllerMotionProvider }
|
internal func tryGetMotionProvider() -> DSUMotionProvider? {
|
||||||
|
if let deviceMotionProvider {
|
||||||
|
return deviceMotionProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
return controllerMotionProvider
|
||||||
|
}
|
||||||
|
|
||||||
private func setupHandheldController() {
|
private func setupHandheldController() {
|
||||||
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
||||||
|
@ -158,7 +158,7 @@ class VirtualController : BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VirtualControllerButton: Int {
|
enum VirtualControllerButton: Int, Codable {
|
||||||
case A
|
case A
|
||||||
case B
|
case B
|
||||||
case X
|
case X
|
||||||
@ -196,7 +196,7 @@ enum VirtualControllerButton: Int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ThumbstickType: Int {
|
enum ThumbstickType: Int, Codable {
|
||||||
case left
|
case left
|
||||||
case right
|
case right
|
||||||
}
|
}
|
||||||
|
@ -99,10 +99,7 @@ extension Notification.Name {
|
|||||||
static let newLogCaptured = Notification.Name("newLogCaptured")
|
static let newLogCaptured = Notification.Name("newLogCaptured")
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Controller: Identifiable, Hashable {
|
|
||||||
var id: String
|
|
||||||
var name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iOSNav<Content: View>: View {
|
struct iOSNav<Content: View>: View {
|
||||||
@ViewBuilder var content: () -> Content
|
@ViewBuilder var content: () -> Content
|
||||||
@ -136,6 +133,7 @@ class Ryujinx : ObservableObject {
|
|||||||
@Published var emulationUIView: MeloMTKView? = nil
|
@Published var emulationUIView: MeloMTKView? = nil
|
||||||
@Published var config: Ryujinx.Arguments? = nil
|
@Published var config: Ryujinx.Arguments? = nil
|
||||||
@Published var games: [Game] = []
|
@Published var games: [Game] = []
|
||||||
|
@Published var aspectRatio: AspectRatio = .fixed16x9
|
||||||
|
|
||||||
@Published var defMLContentSize: CGFloat?
|
@Published var defMLContentSize: CGFloat?
|
||||||
|
|
||||||
@ -240,7 +238,7 @@ class Ryujinx : ObservableObject {
|
|||||||
disablevsync: Bool = false,
|
disablevsync: Bool = false,
|
||||||
language: SystemLanguage = .americanEnglish,
|
language: SystemLanguage = .americanEnglish,
|
||||||
regioncode: SystemRegionCode = .usa,
|
regioncode: SystemRegionCode = .usa,
|
||||||
handHeldController: Bool = false,
|
handHeldController: Bool = false
|
||||||
) {
|
) {
|
||||||
self.gamepath = gamepath
|
self.gamepath = gamepath
|
||||||
self.inputids = inputids
|
self.inputids = inputids
|
||||||
@ -424,6 +422,42 @@ class Ryujinx : ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static func clearShaderCache(_ titleId: String = "") {
|
||||||
|
showAlert(title: "Clear Shader Cache", message: titleId.isEmpty ? "Are you sure you want to clear ALL shader cache?" : "Are you sure you want to clear your shader cache?",
|
||||||
|
actions: [
|
||||||
|
(title: "Cancel", style: .cancel, handler: nil),
|
||||||
|
(title: "Clear", style: .destructive, handler: {
|
||||||
|
if titleId.isEmpty {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let gamesURL = URL.documentsDirectory.appendingPathComponent("games")
|
||||||
|
|
||||||
|
do {
|
||||||
|
let contents = try fileManager.contentsOfDirectory(at: gamesURL, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles])
|
||||||
|
|
||||||
|
let folderURLs = contents.filter { url in
|
||||||
|
(try? url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
for folderURL in folderURLs {
|
||||||
|
try? fileManager.removeItem(at: folderURL.appendingPathComponent("cache"))
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Error reading games folder: \(error)")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let cacheURL = URL.documentsDirectory.appendingPathComponent("games").appendingPathComponent(titleId).appendingPathComponent("cache")
|
||||||
|
|
||||||
|
try? fileManager.removeItem(at: cacheURL)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
struct ExceptionInfo {
|
struct ExceptionInfo {
|
||||||
let exceptionType: String
|
let exceptionType: String
|
||||||
let message: String
|
let message: String
|
||||||
@ -563,7 +597,11 @@ class Ryujinx : ObservableObject {
|
|||||||
|
|
||||||
args.append(contentsOf: ["--system-region", config.regioncode.rawValue])
|
args.append(contentsOf: ["--system-region", config.regioncode.rawValue])
|
||||||
|
|
||||||
args.append(contentsOf: ["--aspect-ratio", config.aspectRatio.rawValue])
|
DispatchQueue.main.async {
|
||||||
|
self.aspectRatio = config.aspectRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
args.append(contentsOf: ["--aspect-ratio", "Stretched"])
|
||||||
|
|
||||||
args.append(contentsOf: ["--system-timezone", TimeZone.current.identifier])
|
args.append(contentsOf: ["--system-timezone", TimeZone.current.identifier])
|
||||||
|
|
||||||
@ -835,116 +873,8 @@ public extension UIDevice {
|
|||||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity
|
return CallMGCopyAnswer(kMGPhysicalHardwareNameString)?.takeUnretainedValue() as? String ?? identifier
|
||||||
#if os(iOS)
|
|
||||||
switch identifier {
|
|
||||||
case "iPod5,1": return "iPod touch (5th generation)"
|
|
||||||
case "iPod7,1": return "iPod touch (6th generation)"
|
|
||||||
case "iPod9,1": return "iPod touch (7th generation)"
|
|
||||||
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4"
|
|
||||||
case "iPhone4,1": return "iPhone 4s"
|
|
||||||
case "iPhone5,1", "iPhone5,2": return "iPhone 5"
|
|
||||||
case "iPhone5,3", "iPhone5,4": return "iPhone 5c"
|
|
||||||
case "iPhone6,1", "iPhone6,2": return "iPhone 5s"
|
|
||||||
case "iPhone7,2": return "iPhone 6"
|
|
||||||
case "iPhone7,1": return "iPhone 6 Plus"
|
|
||||||
case "iPhone8,1": return "iPhone 6s"
|
|
||||||
case "iPhone8,2": return "iPhone 6s Plus"
|
|
||||||
case "iPhone9,1", "iPhone9,3": return "iPhone 7"
|
|
||||||
case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus"
|
|
||||||
case "iPhone10,1", "iPhone10,4": return "iPhone 8"
|
|
||||||
case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
|
|
||||||
case "iPhone10,3", "iPhone10,6": return "iPhone X"
|
|
||||||
case "iPhone11,2": return "iPhone XS"
|
|
||||||
case "iPhone11,4", "iPhone11,6": return "iPhone XS Max"
|
|
||||||
case "iPhone11,8": return "iPhone XR"
|
|
||||||
case "iPhone12,1": return "iPhone 11"
|
|
||||||
case "iPhone12,3": return "iPhone 11 Pro"
|
|
||||||
case "iPhone12,5": return "iPhone 11 Pro Max"
|
|
||||||
case "iPhone13,1": return "iPhone 12 mini"
|
|
||||||
case "iPhone13,2": return "iPhone 12"
|
|
||||||
case "iPhone13,3": return "iPhone 12 Pro"
|
|
||||||
case "iPhone13,4": return "iPhone 12 Pro Max"
|
|
||||||
case "iPhone14,4": return "iPhone 13 mini"
|
|
||||||
case "iPhone14,5": return "iPhone 13"
|
|
||||||
case "iPhone14,2": return "iPhone 13 Pro"
|
|
||||||
case "iPhone14,3": return "iPhone 13 Pro Max"
|
|
||||||
case "iPhone14,7": return "iPhone 14"
|
|
||||||
case "iPhone14,8": return "iPhone 14 Plus"
|
|
||||||
case "iPhone15,2": return "iPhone 14 Pro"
|
|
||||||
case "iPhone15,3": return "iPhone 14 Pro Max"
|
|
||||||
case "iPhone15,4": return "iPhone 15"
|
|
||||||
case "iPhone15,5": return "iPhone 15 Plus"
|
|
||||||
case "iPhone16,1": return "iPhone 15 Pro"
|
|
||||||
case "iPhone16,2": return "iPhone 15 Pro Max"
|
|
||||||
case "iPhone17,3": return "iPhone 16"
|
|
||||||
case "iPhone17,4": return "iPhone 16 Plus"
|
|
||||||
case "iPhone17,1": return "iPhone 16 Pro"
|
|
||||||
case "iPhone17,2": return "iPhone 16 Pro Max"
|
|
||||||
case "iPhone17,5": return "iPhone 16e"
|
|
||||||
case "iPhone8,4": return "iPhone SE"
|
|
||||||
case "iPhone12,8": return "iPhone SE (2nd generation)"
|
|
||||||
case "iPhone14,6": return "iPhone SE (3rd generation)"
|
|
||||||
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2"
|
|
||||||
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad (3rd generation)"
|
|
||||||
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad (4th generation)"
|
|
||||||
case "iPad6,11", "iPad6,12": return "iPad (5th generation)"
|
|
||||||
case "iPad7,5", "iPad7,6": return "iPad (6th generation)"
|
|
||||||
case "iPad7,11", "iPad7,12": return "iPad (7th generation)"
|
|
||||||
case "iPad11,6", "iPad11,7": return "iPad (8th generation)"
|
|
||||||
case "iPad12,1", "iPad12,2": return "iPad (9th generation)"
|
|
||||||
case "iPad13,18", "iPad13,19": return "iPad (10th generation)"
|
|
||||||
case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air"
|
|
||||||
case "iPad5,3", "iPad5,4": return "iPad Air 2"
|
|
||||||
case "iPad11,3", "iPad11,4": return "iPad Air (3rd generation)"
|
|
||||||
case "iPad13,1", "iPad13,2": return "iPad Air (4th generation)"
|
|
||||||
case "iPad13,16", "iPad13,17": return "iPad Air (5th generation)"
|
|
||||||
case "iPad14,8", "iPad14,9": return "iPad Air (11-inch) (M2)"
|
|
||||||
case "iPad14,10", "iPad14,11": return "iPad Air (13-inch) (M2)"
|
|
||||||
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini"
|
|
||||||
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad mini 2"
|
|
||||||
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad mini 3"
|
|
||||||
case "iPad5,1", "iPad5,2": return "iPad mini 4"
|
|
||||||
case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)"
|
|
||||||
case "iPad14,1", "iPad14,2": return "iPad mini (6th generation)"
|
|
||||||
case "iPad16,1", "iPad16,2": return "iPad mini (A17 Pro)"
|
|
||||||
case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)"
|
|
||||||
case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)"
|
|
||||||
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return "iPad Pro (11-inch) (1st generation)"
|
|
||||||
case "iPad8,9", "iPad8,10": return "iPad Pro (11-inch) (2nd generation)"
|
|
||||||
case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": return "iPad Pro (11-inch) (3rd generation)"
|
|
||||||
case "iPad14,3", "iPad14,4": return "iPad Pro (11-inch) (4th generation)"
|
|
||||||
case "iPad16,3", "iPad16,4": return "iPad Pro (11-inch) (M4)"
|
|
||||||
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch) (1st generation)"
|
|
||||||
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)"
|
|
||||||
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return "iPad Pro (12.9-inch) (3rd generation)"
|
|
||||||
case "iPad8,11", "iPad8,12": return "iPad Pro (12.9-inch) (4th generation)"
|
|
||||||
case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11":return "iPad Pro (12.9-inch) (5th generation)"
|
|
||||||
case "iPad14,5", "iPad14,6": return "iPad Pro (12.9-inch) (6th generation)"
|
|
||||||
case "iPad16,5", "iPad16,6": return "iPad Pro (13-inch) (M4)"
|
|
||||||
case "AppleTV5,3": return "Apple TV"
|
|
||||||
case "AppleTV6,2": return "Apple TV 4K"
|
|
||||||
case "AudioAccessory1,1": return "HomePod"
|
|
||||||
case "AudioAccessory5,1": return "HomePod mini"
|
|
||||||
case "i386", "x86_64", "arm64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))"
|
|
||||||
default: return identifier
|
|
||||||
}
|
|
||||||
#elseif os(tvOS)
|
|
||||||
switch identifier {
|
|
||||||
case "AppleTV5,3": return "Apple TV 4"
|
|
||||||
case "AppleTV6,2", "AppleTV11,1", "AppleTV14,1": return "Apple TV 4K"
|
|
||||||
case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))"
|
|
||||||
default: return identifier
|
|
||||||
}
|
|
||||||
#elseif os(visionOS)
|
|
||||||
switch identifier {
|
|
||||||
case "RealityDevice14,1": return "Apple Vision Pro"
|
|
||||||
default: return identifier
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapToDevice(identifier: identifier)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// ToggleButtonsState.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 12/04/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
struct ToggleButtonsState: Codable, Equatable {
|
|
||||||
var toggle1: Bool
|
|
||||||
var toggle2: Bool
|
|
||||||
var toggle3: Bool
|
|
||||||
var toggle4: Bool
|
|
||||||
|
|
||||||
init() {
|
|
||||||
self = .default
|
|
||||||
}
|
|
||||||
|
|
||||||
init(toggle1: Bool, toggle2: Bool, toggle3: Bool, toggle4: Bool) {
|
|
||||||
self.toggle1 = toggle1
|
|
||||||
self.toggle2 = toggle2
|
|
||||||
self.toggle3 = toggle3
|
|
||||||
self.toggle4 = toggle4
|
|
||||||
}
|
|
||||||
|
|
||||||
static let `default` = ToggleButtonsState(toggle1: false, toggle2: false, toggle3: false, toggle4: false)
|
|
||||||
}
|
|
27
src/MeloNX/MeloNX/App/Views/Main/Elements/Alerts.swift
Normal file
27
src/MeloNX/MeloNX/App/Views/Main/Elements/Alerts.swift
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// Alerts.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 04/07/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
func showAlert(_ viewController: UIViewController? = nil,
|
||||||
|
title: String?,
|
||||||
|
message: String?,
|
||||||
|
actions: [(title: String, style: UIAlertAction.Style, handler: (() -> Void)?)]) {
|
||||||
|
|
||||||
|
|
||||||
|
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||||
|
|
||||||
|
for action in actions {
|
||||||
|
let uiAction = UIAlertAction(title: action.title, style: action.style) { _ in
|
||||||
|
action.handler?()
|
||||||
|
}
|
||||||
|
alert.addAction(uiAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
let coolVC = viewController ?? UIApplication.shared.windows.first?.rootViewController!
|
||||||
|
coolVC!.present(alert, animated: true, completion: nil)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -10,22 +10,23 @@ import SwiftUI
|
|||||||
|
|
||||||
struct Joystick: View {
|
struct Joystick: View {
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
|
@State var right = true
|
||||||
@Binding var position: CGPoint
|
@Binding var position: CGPoint
|
||||||
@State var joystickSize: CGFloat
|
@State var joystickSize: CGFloat
|
||||||
var boundarySize: CGFloat
|
var boundarySize: CGFloat
|
||||||
|
|
||||||
@State private var offset: CGSize = .zero
|
@State private var offset: CGSize = .zero
|
||||||
@Binding var showBackground: Bool
|
@Binding var showBackground: Bool
|
||||||
|
@State var joystickSmallSize = false
|
||||||
|
|
||||||
let sensitivity: CGFloat = 1.2
|
let sensitivity: CGFloat = 1.2
|
||||||
|
|
||||||
|
|
||||||
var dragGesture: some Gesture {
|
var dragGesture: some Gesture {
|
||||||
DragGesture()
|
DragGesture()
|
||||||
.onChanged { value in
|
.onChanged { value in
|
||||||
withAnimation(.easeIn) {
|
withAnimation(.easeIn) {
|
||||||
showBackground = true
|
showBackground = true
|
||||||
|
joystickSmallSize = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let translation = value.translation
|
let translation = value.translation
|
||||||
@ -44,16 +45,28 @@ struct Joystick: View {
|
|||||||
x: max(-1, min(1, (offset.width / extendedRadius) * sensitivity)),
|
x: max(-1, min(1, (offset.width / extendedRadius) * sensitivity)),
|
||||||
y: max(-1, min(1, (offset.height / extendedRadius) * sensitivity))
|
y: max(-1, min(1, (offset.height / extendedRadius) * sensitivity))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setPos()
|
||||||
}
|
}
|
||||||
.onEnded { _ in
|
.onEnded { _ in
|
||||||
offset = .zero
|
offset = .zero
|
||||||
position = .zero
|
position = .zero
|
||||||
|
setPos()
|
||||||
withAnimation(.easeOut) {
|
withAnimation(.easeOut) {
|
||||||
showBackground = false
|
showBackground = false
|
||||||
|
joystickSmallSize = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPos() {
|
||||||
|
if right {
|
||||||
|
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: position.x, y: position.y)
|
||||||
|
} else {
|
||||||
|
Ryujinx.shared.virtualController.thumbstickMoved(.left, x: position.x, y: position.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Circle()
|
Circle()
|
||||||
@ -82,7 +95,7 @@ struct Joystick: View {
|
|||||||
.scaleEffect(controllerScale)
|
.scaleEffect(controllerScale)
|
||||||
}
|
}
|
||||||
.frame(width: boundarySize, height: boundarySize)
|
.frame(width: boundarySize, height: boundarySize)
|
||||||
.onChange(of: showBackground) { newValue in
|
.onChange(of: joystickSmallSize) { newValue in
|
||||||
if newValue {
|
if newValue {
|
||||||
joystickSize *= 1.4
|
joystickSize *= 1.4
|
||||||
} else {
|
} else {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct JoystickController: View {
|
struct JoystickController: View {
|
||||||
@State var iscool: Bool? = nil
|
@State var iscool: Bool
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@Binding var showBackground: Bool
|
@Binding var showBackground: Bool
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
@ -25,15 +25,8 @@ struct JoystickController: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
VStack {
|
Group {
|
||||||
Joystick(position: $position, joystickSize: dragDiameter * 0.2, boundarySize: dragDiameter, showBackground: $showBackground)
|
Joystick(right: iscool, position: $position, joystickSize: dragDiameter * 0.2, boundarySize: dragDiameter, showBackground: $showBackground)
|
||||||
.onChange(of: position) { newValue in
|
|
||||||
if iscool != nil {
|
|
||||||
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: newValue.x, y: newValue.y)
|
|
||||||
} else {
|
|
||||||
Ryujinx.shared.virtualController.thumbstickMoved(.left, x: newValue.x, y: newValue.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,7 @@ struct EmulationView: View {
|
|||||||
|
|
||||||
@AppStorage("On-ScreenControllerOpacity") var controllerOpacity: Double = 1.0
|
@AppStorage("On-ScreenControllerOpacity") var controllerOpacity: Double = 1.0
|
||||||
|
|
||||||
@AppStorage("disableTouch") var blackScreen = false
|
@AppStorage("OldView") var oldView = true
|
||||||
|
|
||||||
@State var isPresentedThree: Bool = false
|
@State var isPresentedThree: Bool = false
|
||||||
@State var isAirplaying = Air.shared.connected
|
@State var isAirplaying = Air.shared.connected
|
||||||
@Binding var startgame: Game?
|
@Binding var startgame: Game?
|
||||||
@ -27,9 +26,18 @@ struct EmulationView: View {
|
|||||||
@State var showSettings = false
|
@State var showSettings = false
|
||||||
@State var pauseEmu = true
|
@State var pauseEmu = true
|
||||||
@AppStorage("location-enabled") var locationenabled: Bool = false
|
@AppStorage("location-enabled") var locationenabled: Bool = false
|
||||||
|
@FocusState private var isFocused: Bool
|
||||||
|
@ObservedObject var ryujijnx = Ryujinx.shared
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
|
if oldView {
|
||||||
|
Color.black
|
||||||
|
.ignoresSafeArea()
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
|
|
||||||
if isAirplaying {
|
if isAirplaying {
|
||||||
TouchView()
|
TouchView()
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
@ -37,66 +45,31 @@ struct EmulationView: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
Air.play(AnyView(MetalView().ignoresSafeArea().edgesIgnoringSafeArea(.all)))
|
Air.play(AnyView(MetalView().ignoresSafeArea().edgesIgnoringSafeArea(.all)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Color.black
|
|
||||||
.ignoresSafeArea()
|
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
.allowsHitTesting(false)
|
|
||||||
} else {
|
} else {
|
||||||
MetalView() // The Emulation View
|
MetalViewContainer() // The Emulation View
|
||||||
.ignoresSafeArea()
|
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Above Emulation View
|
// Above Emulation View
|
||||||
|
|
||||||
if isVCA {
|
if isVCA {
|
||||||
ControllerView() // Virtual Controller
|
ControllerView(isEditing: .constant(false), gameId: startgame?.titleId) // Virtual Controller
|
||||||
.opacity(controllerOpacity)
|
.opacity(controllerOpacity)
|
||||||
.allowsHitTesting(true)
|
.allowsHitTesting(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
Group {
|
|
||||||
VStack {
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
if performacehud, !showlogsgame {
|
if !performacehud, showlogsgame, ProcessInfo.processInfo.isLowPowerModeEnabled {
|
||||||
PerformanceOverlayView()
|
Circle()
|
||||||
}
|
.fill(Color.orange)
|
||||||
|
.frame(width: 10, height: 10)
|
||||||
Spacer()
|
.padding()
|
||||||
|
|
||||||
if performacehud, showlogsgame {
|
|
||||||
PerformanceOverlayView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
if showlogsgame, get_current_fps() != 0 {
|
|
||||||
LogFileView(isfps: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ssb {
|
if ssb {
|
||||||
HStack {
|
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
|
|
||||||
/*
|
|
||||||
Button {
|
|
||||||
showSettings.toggle()
|
|
||||||
|
|
||||||
} label: {
|
|
||||||
Label {
|
|
||||||
Text("Game Settings")
|
|
||||||
} icon: {
|
|
||||||
Image(systemName: "gearshape.circle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
pause_emulation(pauseEmu)
|
pause_emulation(pauseEmu)
|
||||||
pauseEmu.toggle()
|
pauseEmu.toggle()
|
||||||
@ -108,6 +81,17 @@ struct EmulationView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
// ryujijnx.config?.aspectRatio
|
||||||
|
ryujijnx.aspectRatio = nextAspectRatio(current: ryujijnx.aspectRatio)
|
||||||
|
} label: {
|
||||||
|
Label {
|
||||||
|
Text(ryujijnx.aspectRatio.displayName)
|
||||||
|
} icon: {
|
||||||
|
Image(systemName: "rectangle.expand.vertical")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
startgame = nil
|
startgame = nil
|
||||||
stop_emulation()
|
stop_emulation()
|
||||||
@ -122,19 +106,39 @@ struct EmulationView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
ExtButtonIconView(button: .guide, opacity: 0.4)
|
ExtButtonIconView(button: .guide, opacity: 0.4)
|
||||||
}
|
}
|
||||||
|
.menuStyle(.borderlessButton)
|
||||||
|
.menuIndicator(.hidden)
|
||||||
.padding()
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
|
||||||
|
if performacehud, getenv("MTL_HUD_ENABLED").flatMap({ String(cString: $0) }) != "1" {
|
||||||
|
PerformanceOverlayView()
|
||||||
|
.opacity(controllerOpacity)
|
||||||
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
|
if showlogsgame, get_current_fps() != 0 {
|
||||||
|
VStack {
|
||||||
|
LogFileView(isfps: false)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
isFocused = true
|
||||||
|
}
|
||||||
|
|
||||||
LocationManager.sharedInstance.startUpdatingLocation()
|
LocationManager.sharedInstance.startUpdatingLocation()
|
||||||
Air.shared.connectionCallbacks.append { cool in
|
Air.shared.connectionCallbacks.append { cool in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@ -148,10 +152,12 @@ struct EmulationView: View {
|
|||||||
print(cool)
|
print(cool)
|
||||||
startgame = nil
|
startgame = nil
|
||||||
stop_emulation()
|
stop_emulation()
|
||||||
try? Ryujinx.shared.stop()
|
try? ryujijnx.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onKeyPress()
|
||||||
|
.focused($isFocused)
|
||||||
.onChange(of: scenePhase) { newPhase in
|
.onChange(of: scenePhase) { newPhase in
|
||||||
// Detect when the app enters the background
|
// Detect when the app enters the background
|
||||||
if newPhase == .background {
|
if newPhase == .background {
|
||||||
@ -172,4 +178,32 @@ struct EmulationView: View {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nextAspectRatio(current: AspectRatio) -> AspectRatio {
|
||||||
|
let all = AspectRatio.allCases
|
||||||
|
if let index = all.firstIndex(of: current) {
|
||||||
|
let nextIndex = (index + 1) % all.count
|
||||||
|
return all[nextIndex]
|
||||||
|
} else {
|
||||||
|
return .fixed16x9 // Default fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is just to stop the sound on macOS when doing a keypress
|
||||||
|
extension View {
|
||||||
|
func onKeyPress() -> some View {
|
||||||
|
if #available(iOS 17.0, *), ProcessInfo.processInfo.isiOSAppOnMac {
|
||||||
|
return AnyView(self
|
||||||
|
.focusable()
|
||||||
|
.focusEffectDisabled()
|
||||||
|
.onKeyPress { _ in
|
||||||
|
return .handled
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return AnyView(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,14 @@ struct PerformanceOverlayView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
|
if ProcessInfo.processInfo.isLowPowerModeEnabled {
|
||||||
|
Text("\(fpsmonitor.formatFPS())")
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.stroke(color: .orange, width: 2)
|
||||||
|
Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage))
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.stroke(color: .orange, width: 2)
|
||||||
|
} else {
|
||||||
Text("\(fpsmonitor.formatFPS())")
|
Text("\(fpsmonitor.formatFPS())")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.stroke(color: .black, width: 2)
|
.stroke(color: .black, width: 2)
|
||||||
@ -22,6 +30,7 @@ struct PerformanceOverlayView: View {
|
|||||||
.stroke(color: .black, width: 2)
|
.stroke(color: .black, width: 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
//
|
||||||
|
// MetalViewContainer.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 06/07/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MetalViewContainer: View {
|
||||||
|
@ObservedObject var ryujinx = Ryujinx.shared
|
||||||
|
@AppStorage("OldView") var oldView = true
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@Environment(\.horizontalSizeClass) var horizontalSizeClass
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { geo in
|
||||||
|
ZStack {
|
||||||
|
if shouldStretchToFillScreen {
|
||||||
|
stretchedView
|
||||||
|
} else if oldView {
|
||||||
|
Color.black.edgesIgnoringSafeArea(.all)
|
||||||
|
|
||||||
|
|
||||||
|
oldStyleView(containerSize: geo.size)
|
||||||
|
} else {
|
||||||
|
modernView(containerSize: geo.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animation(.easeInOut(duration: 0.3), value: ryujinx.aspectRatio)
|
||||||
|
.animation(.easeInOut(duration: 0.3), value: oldView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - View Components
|
||||||
|
|
||||||
|
private var stretchedView: some View {
|
||||||
|
MetalView()
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func oldStyleView(containerSize: CGSize) -> some View {
|
||||||
|
let size = targetSize(for: containerSize)
|
||||||
|
let isPortrait = containerSize.width < containerSize.height
|
||||||
|
|
||||||
|
return ZStack {
|
||||||
|
MetalView()
|
||||||
|
.frame(width: size.width, height: size.height)
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.ignoresSafeArea(.container, edges: isPortrait ? .horizontal : .vertical)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func modernView(containerSize: CGSize) -> some View {
|
||||||
|
let size = targetSize(for: containerSize)
|
||||||
|
let isPortrait = containerSize.width < containerSize.height
|
||||||
|
let isPhone = UIDevice.current.userInterfaceIdiom == .phone
|
||||||
|
let scale = calculateScale(isPortrait: isPortrait, isPhone: isPhone)
|
||||||
|
|
||||||
|
return ZStack {
|
||||||
|
borderedMetalView(size: size)
|
||||||
|
.scaleEffect(scale)
|
||||||
|
.ignoresSafeArea(.container, edges: isPortrait ? .horizontal : .vertical)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func borderedMetalView(size: CGSize) -> some View {
|
||||||
|
let cornerRadius: CGFloat = 16
|
||||||
|
let borderWidth: CGFloat = 2
|
||||||
|
|
||||||
|
return ZStack {
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
||||||
|
.fill(backgroundColor)
|
||||||
|
.shadow(color: shadowColor, radius: 8, x: 0, y: 2)
|
||||||
|
|
||||||
|
MetalView()
|
||||||
|
.frame(width: size.width - borderWidth * 2, height: size.height - borderWidth * 2)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: cornerRadius - borderWidth, style: .continuous))
|
||||||
|
}
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
||||||
|
.strokeBorder(borderColor, lineWidth: borderWidth)
|
||||||
|
)
|
||||||
|
.frame(width: size.width, height: size.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Computed Properties
|
||||||
|
|
||||||
|
private var shouldStretchToFillScreen: Bool {
|
||||||
|
ryujinx.aspectRatio == .stretched ||
|
||||||
|
(ryujinx.aspectRatio == .fixed4x3 && isScreenAspectRatio(4, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var borderColor: Color {
|
||||||
|
colorScheme == .light ? Color.gray : Color(UIColor.darkGray)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var backgroundColor: Color {
|
||||||
|
colorScheme == .light ? .white : Color(.systemGray6)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var shadowColor: Color {
|
||||||
|
colorScheme == .light ? .black.opacity(0.1) : .black.opacity(0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helper Methods
|
||||||
|
|
||||||
|
private func calculateScale(isPortrait: Bool, isPhone: Bool) -> CGFloat {
|
||||||
|
let baseScale: CGFloat = isPhone ? 0.95 : 1.0
|
||||||
|
return isPortrait ? baseScale : baseScale * 0.92
|
||||||
|
}
|
||||||
|
|
||||||
|
private func targetSize(for containerSize: CGSize) -> CGSize {
|
||||||
|
let targetAspect: CGFloat = {
|
||||||
|
switch ryujinx.aspectRatio {
|
||||||
|
case .fixed4x3: return 4.0 / 3.0
|
||||||
|
case .fixed16x9: return 16.0 / 9.0
|
||||||
|
case .fixed16x10: return 16.0 / 10.0
|
||||||
|
case .fixed21x9: return 21.0 / 9.0
|
||||||
|
case .fixed32x9: return 32.0 / 10.0
|
||||||
|
case .stretched: return containerSize.width / containerSize.height
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
let safeArea = UIApplication.shared.windows.first?.safeAreaInsets ?? .zero
|
||||||
|
let adjustedContainer = CGSize(
|
||||||
|
width: containerSize.width - safeArea.left - safeArea.right,
|
||||||
|
height: containerSize.height - safeArea.top - safeArea.bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
if ryujinx.aspectRatio == .stretched {
|
||||||
|
return adjustedContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
let containerAspect = adjustedContainer.width / adjustedContainer.height
|
||||||
|
|
||||||
|
if containerAspect > targetAspect {
|
||||||
|
let height = adjustedContainer.height
|
||||||
|
let width = height * targetAspect
|
||||||
|
return CGSize(width: width, height: height)
|
||||||
|
} else {
|
||||||
|
let width = adjustedContainer.width
|
||||||
|
let height = width / targetAspect
|
||||||
|
return CGSize(width: width, height: height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func isScreenAspectRatio(_ targetWidth: CGFloat, _ targetHeight: CGFloat, tolerance: CGFloat = 0.05) -> Bool {
|
||||||
|
let screenSize = UIScreen.main.bounds.size
|
||||||
|
let actualRatio = screenSize.width / screenSize.height
|
||||||
|
let targetRatio = targetWidth / targetHeight
|
||||||
|
return abs(actualRatio - targetRatio) < tolerance
|
||||||
|
}
|
||||||
|
}
|
@ -99,7 +99,7 @@ class MeloMTKView: MTKView {
|
|||||||
let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
|
let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
|
||||||
guard !disabled else { return }
|
guard !disabled else { return }
|
||||||
|
|
||||||
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
|
setAspectRatio(Ryujinx.shared.aspectRatio)
|
||||||
|
|
||||||
for touch in touches {
|
for touch in touches {
|
||||||
let location = touch.location(in: self)
|
let location = touch.location(in: self)
|
||||||
@ -153,7 +153,7 @@ class MeloMTKView: MTKView {
|
|||||||
let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
|
let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
|
||||||
guard !disabled else { return }
|
guard !disabled else { return }
|
||||||
|
|
||||||
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
|
setAspectRatio(Ryujinx.shared.aspectRatio)
|
||||||
|
|
||||||
for touch in touches {
|
for touch in touches {
|
||||||
if ignoredTouches.contains(touch) {
|
if ignoredTouches.contains(touch) {
|
||||||
|
@ -21,14 +21,13 @@ struct MetalView: UIViewRepresentable {
|
|||||||
fatalError("[Swift] Error: MTKView's layer is not a CAMetalLayer")
|
fatalError("[Swift] Error: MTKView's layer is not a CAMetalLayer")
|
||||||
}
|
}
|
||||||
|
|
||||||
metalLayer.device = MTLCreateSystemDefaultDevice()
|
notnil(metalLayer.device) ? () : (metalLayer.device = MTLCreateSystemDefaultDevice())
|
||||||
|
|
||||||
let layerPtr = Unmanaged.passUnretained(metalLayer).toOpaque()
|
let layerPtr = Unmanaged.passUnretained(metalLayer).toOpaque()
|
||||||
set_native_window(layerPtr)
|
set_native_window(layerPtr)
|
||||||
|
|
||||||
Ryujinx.shared.emulationUIView = view
|
Ryujinx.shared.emulationUIView = view
|
||||||
|
|
||||||
|
|
||||||
Ryujinx.shared.metalLayer = metalLayer
|
Ryujinx.shared.metalLayer = metalLayer
|
||||||
|
|
||||||
return view
|
return view
|
||||||
@ -51,5 +50,7 @@ struct MetalView: UIViewRepresentable {
|
|||||||
|
|
||||||
func updateUIView(_ uiView: UIView, context: Context) {
|
func updateUIView(_ uiView: UIView, context: Context) {
|
||||||
// nothin
|
// nothin
|
||||||
|
print(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,7 @@ import MetalKit
|
|||||||
|
|
||||||
struct TouchView: UIViewRepresentable {
|
struct TouchView: UIViewRepresentable {
|
||||||
func makeUIView(context: Context) -> UIView {
|
func makeUIView(context: Context) -> UIView {
|
||||||
let view = MeloMTKView()
|
return MeloMTKView()
|
||||||
return view
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIView(_ uiView: UIView, context: Context) {}
|
func updateUIView(_ uiView: UIView, context: Context) {}
|
@ -52,6 +52,9 @@ struct ContentView: View {
|
|||||||
@AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: Bool = true
|
@AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: Bool = true
|
||||||
@AppStorage("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS") var syncqsubmits: Bool = false
|
@AppStorage("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS") var syncqsubmits: Bool = false
|
||||||
@AppStorage("ignoreJIT") var ignoreJIT: Bool = false
|
@AppStorage("ignoreJIT") var ignoreJIT: Bool = false
|
||||||
|
@AppStorage("DUAL_MAPPED_JIT") var dualMapped: Bool = false
|
||||||
|
@AppStorage("DUAL_MAPPED_JIT_edit") var dualMappededit: Bool = false
|
||||||
|
|
||||||
|
|
||||||
// Loading Animation
|
// Loading Animation
|
||||||
@AppStorage("showlogsloading") var showlogsloading: Bool = true
|
@AppStorage("showlogsloading") var showlogsloading: Bool = true
|
||||||
@ -79,6 +82,12 @@ struct ContentView: View {
|
|||||||
MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "512"),
|
MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "512"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if #available(iOS 19, *) {
|
||||||
|
setenv("HAS_TXM", ProcessInfo.processInfo.hasTXM ? "1" : "0", 1)
|
||||||
|
} else {
|
||||||
|
setenv("HAS_TXM", "0", 1)
|
||||||
|
}
|
||||||
|
|
||||||
_settings = State(initialValue: defaultSettings)
|
_settings = State(initialValue: defaultSettings)
|
||||||
|
|
||||||
initializeSDL()
|
initializeSDL()
|
||||||
@ -146,9 +155,7 @@ struct ContentView: View {
|
|||||||
let _ = loadSettings()
|
let _ = loadSettings()
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
||||||
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
|
|
||||||
refreshControllersList()
|
refreshControllersList()
|
||||||
}
|
|
||||||
|
|
||||||
UserDefaults.standard.set(false, forKey: "lockInApp")
|
UserDefaults.standard.set(false, forKey: "lockInApp")
|
||||||
|
|
||||||
@ -322,7 +329,9 @@ struct ContentView: View {
|
|||||||
controllersList.mutableForEach { $0.name = $0.name.replacingOccurrences(of: "GC - ", with: "") }
|
controllersList.mutableForEach { $0.name = $0.name.replacingOccurrences(of: "GC - ", with: "") }
|
||||||
|
|
||||||
if controllersList.count == 1 {
|
if controllersList.count == 1 {
|
||||||
|
if !ProcessInfo.processInfo.isiOSAppOnMac {
|
||||||
currentControllers.append(controllersList[0])
|
currentControllers.append(controllersList[0])
|
||||||
|
}
|
||||||
} else if (controllersList.count - 1) >= 1 {
|
} else if (controllersList.count - 1) >= 1 {
|
||||||
for controller in controllersList {
|
for controller in controllersList {
|
||||||
if controller.id != onscreencontroller.id && !currentControllers.contains(where: { $0.id == controller.id }) {
|
if controller.id != onscreencontroller.id && !currentControllers.contains(where: { $0.id == controller.id }) {
|
||||||
@ -392,6 +401,12 @@ struct ContentView: View {
|
|||||||
if syncqsubmits {
|
if syncqsubmits {
|
||||||
setenv("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", "1", 1)
|
setenv("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", "1", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dualMapped {
|
||||||
|
setenv("DUAL_MAPPED_JIT", "1", 1)
|
||||||
|
} else {
|
||||||
|
setenv("DUAL_MAPPED_JIT", "0", 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setMoltenVKSettings() {
|
private func setMoltenVKSettings() {
|
||||||
@ -405,7 +420,11 @@ struct ContentView: View {
|
|||||||
if jitStreamerEB {
|
if jitStreamerEB {
|
||||||
jitStreamerEB = false // byee jitstreamer eb
|
jitStreamerEB = false // byee jitstreamer eb
|
||||||
}
|
}
|
||||||
|
print("Has TXM? \(ProcessInfo.processInfo.hasTXM)")
|
||||||
|
if #available(iOS 19, *), !dualMappededit {
|
||||||
|
dualMapped = !ProcessInfo.processInfo.isiOSAppOnMac
|
||||||
|
dualMappededit = true
|
||||||
|
}
|
||||||
|
|
||||||
if !ryujinx.jitenabled {
|
if !ryujinx.jitenabled {
|
||||||
if useTrollStore {
|
if useTrollStore {
|
||||||
@ -415,12 +434,7 @@ struct ContentView: View {
|
|||||||
} else if jitStreamerEB {
|
} else if jitStreamerEB {
|
||||||
enableJITEB()
|
enableJITEB()
|
||||||
} else {
|
} else {
|
||||||
if !allocateTest(), checkDebugged() {
|
// nothing
|
||||||
loop_heartbeat()
|
|
||||||
sleep(5)
|
|
||||||
let cool = String(cString: attach(getpid())!)
|
|
||||||
print(cool)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,8 +443,6 @@ struct ContentView: View {
|
|||||||
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||||
components.host == "game" {
|
components.host == "game" {
|
||||||
|
|
||||||
refreshControllersList()
|
|
||||||
|
|
||||||
if let text = components.queryItems?.first(where: { $0.name == "id" })?.value {
|
if let text = components.queryItems?.first(where: { $0.name == "id" })?.value {
|
||||||
game = ryujinx.games.first(where: { $0.titleId == text })
|
game = ryujinx.games.first(where: { $0.titleId == text })
|
||||||
} else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value {
|
} else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value {
|
||||||
|
@ -27,13 +27,21 @@ struct GameLibraryView: View {
|
|||||||
@State var isSelectingGameFile = false
|
@State var isSelectingGameFile = false
|
||||||
@State var isViewingGameInfo: Bool = false
|
@State var isViewingGameInfo: Bool = false
|
||||||
@State var gamePerGameSettings: Game?
|
@State var gamePerGameSettings: Game?
|
||||||
|
@State var gameController: Game?
|
||||||
var isShowingPerGameSettings: Binding<Bool> {
|
var isShowingPerGameSettings: Binding<Bool> {
|
||||||
Binding<Bool> {
|
Binding<Bool> {
|
||||||
gamePerGameSettings != nil
|
gamePerGameSettings != nil
|
||||||
} set: { value in
|
} set: { value in
|
||||||
!value ? gamePerGameSettings = nil : ()
|
!value ? gamePerGameSettings = nil : ()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isShowingGameController: Binding<Bool> {
|
||||||
|
Binding<Bool> {
|
||||||
|
gameController != nil
|
||||||
|
} set: { value in
|
||||||
|
!value ? gameController = nil : ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@State var isSelectingGameUpdate: Bool = false
|
@State var isSelectingGameUpdate: Bool = false
|
||||||
@State var isSelectingGameDLC: Bool = false
|
@State var isSelectingGameDLC: Bool = false
|
||||||
@ -213,6 +221,9 @@ struct GameLibraryView: View {
|
|||||||
.sheet(isPresented: isShowingPerGameSettings) {
|
.sheet(isPresented: isShowingPerGameSettings) {
|
||||||
PerGameSettingsView(titleId: gamePerGameSettings!.titleId)
|
PerGameSettingsView(titleId: gamePerGameSettings!.titleId)
|
||||||
}
|
}
|
||||||
|
.fullScreenCover(isPresented: isShowingGameController) {
|
||||||
|
ControllerView(isEditing: isShowingGameController, gameId: gameController?.titleId)
|
||||||
|
}
|
||||||
.sheet(isPresented: Binding(
|
.sheet(isPresented: Binding(
|
||||||
get: { isViewingGameInfo && gameInfo != nil },
|
get: { isViewingGameInfo && gameInfo != nil },
|
||||||
set: { newValue in
|
set: { newValue in
|
||||||
@ -284,6 +295,7 @@ struct GameLibraryView: View {
|
|||||||
isSelectingGameDLC: $isSelectingGameDLC,
|
isSelectingGameDLC: $isSelectingGameDLC,
|
||||||
gameRequirements: $gameRequirements,
|
gameRequirements: $gameRequirements,
|
||||||
gameInfo: $gameInfo,
|
gameInfo: $gameInfo,
|
||||||
|
isShowingGameController: $gameController,
|
||||||
perGameSettings: $gamePerGameSettings
|
perGameSettings: $gamePerGameSettings
|
||||||
)
|
)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
@ -302,6 +314,7 @@ struct GameLibraryView: View {
|
|||||||
isSelectingGameDLC: $isSelectingGameDLC,
|
isSelectingGameDLC: $isSelectingGameDLC,
|
||||||
gameRequirements: $gameRequirements,
|
gameRequirements: $gameRequirements,
|
||||||
gameInfo: $gameInfo,
|
gameInfo: $gameInfo,
|
||||||
|
isShowingGameController: $gameController,
|
||||||
perGameSettings: $gamePerGameSettings
|
perGameSettings: $gamePerGameSettings
|
||||||
)
|
)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
@ -502,6 +515,12 @@ struct GameLibraryView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Label("\(game.titleName) Settings", systemImage: "gear")
|
Label("\(game.titleName) Settings", systemImage: "gear")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
gameController = game
|
||||||
|
} label: {
|
||||||
|
Label("Controller Layout", systemImage: "formfitting.gamecontroller")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
@ -527,6 +546,12 @@ struct GameLibraryView: View {
|
|||||||
Label("Remove from Recents", systemImage: "trash")
|
Label("Remove from Recents", systemImage: "trash")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button(role: .destructive) {
|
||||||
|
Ryujinx.clearShaderCache(game.titleId)
|
||||||
|
} label: {
|
||||||
|
Label("Clear Shader Cache", systemImage: "trash")
|
||||||
|
}
|
||||||
|
|
||||||
if #available(iOS 15, *) {
|
if #available(iOS 15, *) {
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
deleteGame(game: game)
|
deleteGame(game: game)
|
||||||
@ -797,6 +822,7 @@ struct GameListRow: View {
|
|||||||
@Binding var isSelectingGameDLC: Bool
|
@Binding var isSelectingGameDLC: Bool
|
||||||
@Binding var gameRequirements: [GameRequirements]
|
@Binding var gameRequirements: [GameRequirements]
|
||||||
@Binding var gameInfo: Game?
|
@Binding var gameInfo: Game?
|
||||||
|
@Binding var isShowingGameController: Game?
|
||||||
@StateObject private var settingsManager = PerGameSettingsManager.shared
|
@StateObject private var settingsManager = PerGameSettingsManager.shared
|
||||||
@Binding var perGameSettings: Game?
|
@Binding var perGameSettings: Game?
|
||||||
@State var gametoDelete: Game?
|
@State var gametoDelete: Game?
|
||||||
@ -939,6 +965,14 @@ struct GameListRow: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Label("\(game.titleName) Settings", systemImage: "gear")
|
Label("\(game.titleName) Settings", systemImage: "gear")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isShowingGameController
|
||||||
|
|
||||||
|
Button {
|
||||||
|
isShowingGameController = game
|
||||||
|
} label: {
|
||||||
|
Label("Controller Layout", systemImage: "formfitting.gamecontroller")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
@ -958,6 +992,12 @@ struct GameListRow: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
|
Button(role: .destructive) {
|
||||||
|
Ryujinx.clearShaderCache(game.titleId)
|
||||||
|
} label: {
|
||||||
|
Label("Clear Shader Cache", systemImage: "trash")
|
||||||
|
}
|
||||||
|
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
gametoDelete = game
|
gametoDelete = game
|
||||||
showGameDeleteConfirmation.toggle()
|
showGameDeleteConfirmation.toggle()
|
||||||
@ -1114,6 +1154,12 @@ struct GameListRow: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Label("Game Info", systemImage: "info.circle")
|
Label("Game Info", systemImage: "info.circle")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
isShowingGameController = game
|
||||||
|
} label: {
|
||||||
|
Label("Controller Layout", systemImage: "formfitting.gamecontroller")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
@ -1133,6 +1179,12 @@ struct GameListRow: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
|
Button(role: .destructive) {
|
||||||
|
Ryujinx.clearShaderCache(game.titleId)
|
||||||
|
} label: {
|
||||||
|
Label("Clear Shader Cache", systemImage: "trash")
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
gametoDelete = game
|
gametoDelete = game
|
||||||
showGameDeleteConfirmation.toggle()
|
showGameDeleteConfirmation.toggle()
|
||||||
@ -1238,30 +1290,10 @@ func pullGameCompatibility(completion: @escaping (Result<[GameRequirements], Err
|
|||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
func wow(_ colorScheme: ColorScheme) -> some View {
|
func wow(_ colorScheme: ColorScheme) -> some View {
|
||||||
if #available(iOS 26.0, *) {
|
self
|
||||||
return self
|
|
||||||
.glassEffect(Glass.regular, in:
|
|
||||||
RoundedRectangle(cornerRadius: 12)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return self
|
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 12)
|
RoundedRectangle(cornerRadius: 12)
|
||||||
.fill(colorScheme == .dark ? Color(.systemGray6) : Color(.systemGray6).opacity(0.5))
|
.fill(colorScheme == .dark ? Color(.systemGray6) : Color(.systemGray6).opacity(0.5))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension View {
|
|
||||||
@available(iOS, introduced: 14.0, deprecated: 19.0, message: "")
|
|
||||||
func glassEffect(_ style: Glass, in shape: some Shape) -> some View {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS, introduced: 14.0, deprecated: 19.0, message: "")
|
|
||||||
struct Glass: Hashable {
|
|
||||||
static var regular = Glass()
|
|
||||||
}
|
}
|
||||||
|
@ -232,6 +232,7 @@ struct SettingsViewNew: View {
|
|||||||
|
|
||||||
@AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: Bool = false
|
@AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: Bool = false
|
||||||
@AppStorage("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS") var syncqsubmits: Bool = false
|
@AppStorage("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS") var syncqsubmits: Bool = false
|
||||||
|
@AppStorage("DUAL_MAPPED_JIT") var dualMapped: Bool = false
|
||||||
|
|
||||||
@AppStorage("performacehud") var performacehud: Bool = false
|
@AppStorage("performacehud") var performacehud: Bool = false
|
||||||
|
|
||||||
@ -260,20 +261,18 @@ struct SettingsViewNew: View {
|
|||||||
|
|
||||||
@AppStorage("disableTouch") var disableTouch = false
|
@AppStorage("disableTouch") var disableTouch = false
|
||||||
|
|
||||||
@AppStorage("disableTouch") var blackScreen = false
|
|
||||||
|
|
||||||
@AppStorage("location-enabled") var locationenabled: Bool = false
|
@AppStorage("location-enabled") var locationenabled: Bool = false
|
||||||
|
|
||||||
@AppStorage("runOnMainThread") var runOnMainThread = false
|
@AppStorage("runOnMainThread") var runOnMainThread = false
|
||||||
|
|
||||||
@AppStorage("oldSettingsUI") var oldSettingsUI = false
|
@AppStorage("oldSettingsUI") var oldSettingsUI = false
|
||||||
|
|
||||||
@AppCodableStorage("toggleButtons") var toggleButtons = ToggleButtonsState()
|
|
||||||
|
|
||||||
let totalMemory = ProcessInfo.processInfo.physicalMemory
|
let totalMemory = ProcessInfo.processInfo.physicalMemory
|
||||||
|
|
||||||
@AppStorage("lockInApp") var restartApp = false
|
@AppStorage("lockInApp") var restartApp = false
|
||||||
|
|
||||||
|
@AppStorage("OldView") var oldView = true
|
||||||
|
|
||||||
@State private var showResolutionInfo = false
|
@State private var showResolutionInfo = false
|
||||||
@State private var showAnisotropicInfo = false
|
@State private var showAnisotropicInfo = false
|
||||||
@State private var showControllerInfo = false
|
@State private var showControllerInfo = false
|
||||||
@ -375,12 +374,26 @@ struct SettingsViewNew: View {
|
|||||||
color: .blue
|
color: .blue
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let versionPart = ProcessInfo.processInfo.operatingSystemVersionString.replacingOccurrences(of: "Version ", with: "")
|
||||||
|
|
||||||
|
let parts = versionPart.components(separatedBy: " (Build ")
|
||||||
|
if parts.count == 2 {
|
||||||
|
let version = parts[0]
|
||||||
|
let build = parts[1].replacingOccurrences(of: ")", with: "")
|
||||||
InfoCard(
|
InfoCard(
|
||||||
title: "System",
|
title: "System",
|
||||||
value: "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)",
|
value: "\(ProcessInfo.processInfo.isiOSAppOnMac ? "macOS" : UIDevice.current.systemName) \(version) (\(build))",
|
||||||
icon: "applelogo",
|
icon: "applelogo",
|
||||||
color: .gray
|
color: .gray
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
InfoCard(
|
||||||
|
title: "System",
|
||||||
|
value: "\(ProcessInfo.processInfo.isiOSAppOnMac ? "macOS" : UIDevice.current.systemName) \(UIDevice.current.systemVersion)",
|
||||||
|
icon: "applelogo",
|
||||||
|
color: .gray
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
InfoCard(
|
InfoCard(
|
||||||
title: "Increased Memory Limit",
|
title: "Increased Memory Limit",
|
||||||
@ -551,6 +564,16 @@ struct SettingsViewNew: View {
|
|||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||||
|
Text("macOS \(ProcessInfo.processInfo.operatingSystemVersionString)")
|
||||||
|
.font(.subheadline.weight(.medium))
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
Text("·")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
Text("Version \(appVersion)")
|
Text("Version \(appVersion)")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
@ -755,6 +778,11 @@ struct SettingsViewNew: View {
|
|||||||
// Aspect ratio card
|
// Aspect ratio card
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
|
||||||
|
SettingsToggle(isOn: $oldView, icon: "rectangle.on.rectangle.dashed", label: "Old Display UI")
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
labelWithIcon("Aspect Ratio", iconName: "rectangle.expand.vertical")
|
labelWithIcon("Aspect Ratio", iconName: "rectangle.expand.vertical")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
|
|
||||||
@ -825,16 +853,6 @@ struct SettingsViewNew: View {
|
|||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
SettingsToggle(isOn: $swapBandA, icon: "rectangle.2.swap", label: "Swap Face Buttons (Physical Controller)")
|
SettingsToggle(isOn: $swapBandA, icon: "rectangle.2.swap", label: "Swap Face Buttons (Physical Controller)")
|
||||||
|
|
||||||
Divider()
|
|
||||||
|
|
||||||
DisclosureGroup("Toggle Buttons") {
|
|
||||||
SettingsToggle(isOn: $toggleButtons.toggle1, icon: "circle.grid.cross.right.filled", label: "Toggle A")
|
|
||||||
SettingsToggle(isOn: $toggleButtons.toggle2, icon: "circle.grid.cross.down.filled", label: "Toggle B")
|
|
||||||
SettingsToggle(isOn: $toggleButtons.toggle3, icon: "circle.grid.cross.up.filled", label: "Toggle X")
|
|
||||||
SettingsToggle(isOn: $toggleButtons.toggle4, icon: "circle.grid.cross.left.filled", label: "Toggle Y")
|
|
||||||
}
|
|
||||||
.padding(.vertical, 6)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1242,6 +1260,7 @@ struct SettingsViewNew: View {
|
|||||||
SettingsSection(title: "Miscellaneous Options") {
|
SettingsSection(title: "Miscellaneous Options") {
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
VStack(spacing: 4) {
|
VStack(spacing: 4) {
|
||||||
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||||
SettingsToggle(isOn: $toggleGreen, icon: "arrow.clockwise", label: "Toggle Color Green when \"ON\"")
|
SettingsToggle(isOn: $toggleGreen, icon: "arrow.clockwise", label: "Toggle Color Green when \"ON\"")
|
||||||
|
|
||||||
@ -1254,11 +1273,6 @@ struct SettingsViewNew: View {
|
|||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
if colorScheme == .light {
|
|
||||||
SettingsToggle(isOn: $blackScreen, icon: "iphone.slash", label: "Black Screen when using AirPlay")
|
|
||||||
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
showAppIconSwitcher = true
|
showAppIconSwitcher = true
|
||||||
@ -1354,16 +1368,22 @@ struct SettingsViewNew: View {
|
|||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
|
SettingsToggle(isOn: $dualMapped, icon: "light.strip.2", label: "Dual Mapped JIT")
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
SettingsToggle(isOn: $checkForUpdate, icon: "square.and.arrow.down", label: "Check for Updates")
|
SettingsToggle(isOn: $checkForUpdate, icon: "square.and.arrow.down", label: "Check for Updates")
|
||||||
|
|
||||||
if ryujinx.firmwareversion != "0" {
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Ryujinx.shared.removeFirmware()
|
Ryujinx.clearShaderCache()
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Remove Firmware")
|
Image(systemName: "trash")
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
|
Text("Clear All Shader Cache")
|
||||||
|
.foregroundColor(.primary)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
@ -1372,7 +1392,6 @@ struct SettingsViewNew: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Helper Functions
|
// MARK: - Helper Functions
|
||||||
|
|
||||||
|
@ -41,7 +41,11 @@ struct MeloNXApp: App {
|
|||||||
@AppStorage("autoJIT") var autoJIT = false
|
@AppStorage("autoJIT") var autoJIT = false
|
||||||
|
|
||||||
@State var fourgbiPad = false
|
@State var fourgbiPad = false
|
||||||
|
@State var ios19 = false
|
||||||
@AppStorage("4GB iPad") var ignores = false
|
@AppStorage("4GB iPad") var ignores = false
|
||||||
|
@AppStorage("iOS19") var ignores19 = false
|
||||||
|
@AppStorage("DUAL_MAPPED_JIT") var dualMapped: Bool = false
|
||||||
|
@AppStorage("DUAL_MAPPED_JIT_edit") var dualMappededit: Bool = false
|
||||||
// String(format: "%.0f GB", Double(totalMemory) / 1_000_000_000)
|
// String(format: "%.0f GB", Double(totalMemory) / 1_000_000_000)
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
@ -77,10 +81,19 @@ struct MeloNXApp: App {
|
|||||||
withAnimation(.easeOut) {
|
withAnimation(.easeOut) {
|
||||||
finishedStorage = newValue
|
finishedStorage = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if #available(iOS 19, *), newValue {
|
||||||
|
dualMapped = !ProcessInfo.processInfo.isiOSAppOnMac
|
||||||
|
dualMappededit = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
|
if #available(iOS 19, *), ProcessInfo.processInfo.hasTXM, !ignores19 {
|
||||||
|
ios19 = true
|
||||||
|
}
|
||||||
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad && !ignores {
|
if UIDevice.current.userInterfaceIdiom == .pad && !ignores {
|
||||||
print((Double(ProcessInfo.processInfo.physicalMemory) / 1_000_000_000))
|
print((Double(ProcessInfo.processInfo.physicalMemory) / 1_000_000_000))
|
||||||
if round(Double(ProcessInfo.processInfo.physicalMemory) / 1_000_000_000) <= 4 {
|
if round(Double(ProcessInfo.processInfo.physicalMemory) / 1_000_000_000) <= 4 {
|
||||||
@ -148,3 +161,4 @@ func changeAppUI(_ string: String) -> String? {
|
|||||||
guard let data = Data(base64Encoded: string) else { return nil }
|
guard let data = Data(base64Encoded: string) else { return nil }
|
||||||
return String(data: data, encoding: .utf8)
|
return String(data: data, encoding: .utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
615
src/MeloNX/MeloNX/Assets/Localizable.xcstrings
Normal file
615
src/MeloNX/MeloNX/Assets/Localizable.xcstrings
Normal file
@ -0,0 +1,615 @@
|
|||||||
|
{
|
||||||
|
"sourceLanguage" : "en",
|
||||||
|
"strings" : {
|
||||||
|
"" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"-" : {
|
||||||
|
"comment" : "A button that decreases the size of a button.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"·" : {
|
||||||
|
"comment" : "A separator displayed between two lines of text.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"**%@** | %@" : {
|
||||||
|
"comment" : "A heading displaying the name of a game, followed by its title ID.",
|
||||||
|
"isCommentAutoGenerated" : true,
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "**%1$@** | %2$@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"**File Type**" : {
|
||||||
|
"comment" : "A label displayed above the file type of a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"**Game Size**" : {
|
||||||
|
"comment" : "A label displayed above the size of a game file.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"**Game URL**" : {
|
||||||
|
"comment" : "A label displayed above the URL of a game file.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"**Title ID**" : {
|
||||||
|
"comment" : "A button that copies the title ID to the clipboard.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"**Version**" : {
|
||||||
|
"comment" : "A label displayed before the version number of a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"%.1fx" : {
|
||||||
|
"comment" : "A placeholder text for the slider value.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"%.2fx" : {
|
||||||
|
"comment" : "The current resolution scale value is displayed in blue text.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"%@" : {
|
||||||
|
"comment" : "A label displaying the current FPS and memory usage in low power mode.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"%@ DLCs" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"%@ RAM" : {
|
||||||
|
"comment" : "A subheading displaying the amount of RAM available on a device.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"%@ Settings" : {
|
||||||
|
"comment" : "A button that allows the user to access the settings for a specific game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"%@ Updates" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"%lld icons" : {
|
||||||
|
"comment" : "A label displaying the number of icons created by a developer.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"%llu bytes" : {
|
||||||
|
"comment" : "A label displaying the size of a file.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"•" : {
|
||||||
|
"comment" : "A separator between the developer name and the version number.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"+" : {
|
||||||
|
"comment" : "A button that increases the scale of a button when pressed.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"0.1x" : {
|
||||||
|
"comment" : "The word \"low\" is used here to mean \"minimum\".",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"0GB" : {
|
||||||
|
"comment" : "A placeholder text displayed when the system requirements of a game cannot be determined.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"3.0x" : {
|
||||||
|
"comment" : "The \"1x\" text is a placeholder.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"16x" : {
|
||||||
|
"comment" : "The \"16x\" text in the slider.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"About" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Add Controller" : {
|
||||||
|
"comment" : "A button that allows adding a controller to the list.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Add DLC" : {
|
||||||
|
"comment" : "A button that adds game DLCs.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Add Game" : {
|
||||||
|
"comment" : "A button that adds a game to the library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Add ROM files to get started with your gaming experience" : {
|
||||||
|
"comment" : "A call-to-action displayed below the message that no games were found.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Add Update" : {
|
||||||
|
"comment" : "A button that adds a new game update.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Additional Arguments" : {
|
||||||
|
"comment" : "A heading displayed above a text field used to enter additional arguments for the virtual machine.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Adjust the internal Anisotropic filtering. Higher values improve texture quality at angles but may reduce performance. Default at 0 lets game decide." : {
|
||||||
|
"comment" : "A description displayed in an alert when the user taps the \"Max Anisotropic Filtering\" setting.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Adjust the internal rendering resolution. Higher values improve visuals but may reduce performance." : {
|
||||||
|
"comment" : "A description displayed in an alert when the user taps the \"Info\" icon next to \"Resolution Scale\".",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Adjust the On-Screen Controller size." : {
|
||||||
|
"comment" : "A description displayed when the user taps the \"Info\" icon next to the \"Scale\" label in the \"On-Screen Controller\" card.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Adjust the On-Screen Controller transparency." : {
|
||||||
|
"comment" : "A description displayed in an alert dialog box.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Always show Joystick Background" : {
|
||||||
|
"comment" : "A label displayed above a toggle button that controls whether the joystick background is always shown.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"App Icon Switcher" : {
|
||||||
|
"comment" : "A",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Applets" : {
|
||||||
|
"comment" : "A menu that allows users to launch specific apps on their Switch.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Are you sure you want to delete %@?" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Are you sure you want to delete this game?" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Available Layouts" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Button Scale: %@" : {
|
||||||
|
"comment" : "A label displaying the current scale of a button.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Cancel" : {
|
||||||
|
"comment" : "The title of the alert that confirms deleting a custom layout.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Changelog:" : {
|
||||||
|
"comment" : "A heading displayed above a list of changes in the latest update.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Choose App Icon" : {
|
||||||
|
"comment" : "The title displayed in the navigation bar above the list of app icons.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Clear All Shader Cache" : {
|
||||||
|
"comment" : "A",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Clear Shader Cache" : {
|
||||||
|
"comment" : "A button that deletes the shader cache for a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Close" : {
|
||||||
|
"comment" : "The label for the button to close the update sheet.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Continue" : {
|
||||||
|
"comment" : "A button that dismisses the alert.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Controller Configuration" : {
|
||||||
|
"comment" : "A heading displayed above the controller configuration options.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Controller Layout" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Controller Selection" : {
|
||||||
|
"comment" : "A heading displayed above a list of available controllers.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Copy Layout" : {
|
||||||
|
"comment" : "A title displayed in the navigation bar of the view.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Copy Layout From..." : {
|
||||||
|
"comment" : "A button label to copy a layout from another game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Copy Title ID" : {
|
||||||
|
"comment" : "A button label to copy a text to the clipboard.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"CPU Configuration" : {
|
||||||
|
"comment" : "A heading displayed above the CPU configuration options.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Current Game" : {
|
||||||
|
"comment" : "A heading displayed above the information about the current game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Custom Layout" : {
|
||||||
|
"comment" : "A caption displayed next to a checkmark icon.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Default Layout" : {
|
||||||
|
"comment" : "A button label for copying the default layout.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Delete" : {
|
||||||
|
"comment" : "The destructive button in an alert that deletes a custom layout.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Delete Custom Layout" : {
|
||||||
|
"comment" : "A button label to delete a custom layout.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Delete Game" : {
|
||||||
|
"comment" : "A destructive button that deletes a game from the library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Dismiss" : {
|
||||||
|
"comment" : "A button that dismisses the game info sheet.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"DLC Manager" : {
|
||||||
|
"comment" : "A button that displays a sheet for managing downloadable content for a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Done" : {
|
||||||
|
"comment" : "A button that toggles between editing mode and non-editing mode.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Download Now" : {
|
||||||
|
"comment" : "The action button to download the app update.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Edit" : {
|
||||||
|
"comment" : "A button that toggles between editing mode and non-editing mode.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Exit (Unstable)" : {
|
||||||
|
"comment" : "A destructive action displayed in a menu.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Finish Setup" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Game" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Game Info" : {
|
||||||
|
"comment" : "A button that displays information about a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Game Library" : {
|
||||||
|
"comment" : "The title displayed in the navigation bar for the game library view.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Game: %@" : {
|
||||||
|
"comment" : "A label displaying the identifier of the game being played.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Games" : {
|
||||||
|
"comment" : "A tab item that displays a list of games.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Hide" : {
|
||||||
|
"comment" : "A button that hides the edit controls when pressed.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Hide ABXY / Arrow Buttons" : {
|
||||||
|
"comment" : "A label displayed above a toggle button that controls whether the joystick buttons are hidden or not.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Hide Button" : {
|
||||||
|
"comment" : "A label displayed above a toggle switch that controls whether a button is hidden or not.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Hide Joystick" : {
|
||||||
|
"comment" : "A button to hide the joystick.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Home Menu (Broken)" : {
|
||||||
|
"comment" : "A button that launches the currently Broken Home Menu applet."
|
||||||
|
},
|
||||||
|
"Info" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Information" : {
|
||||||
|
"comment" : "A heading displayed above a list of information about a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Install Firmware" : {
|
||||||
|
"comment" : "A button that allows users to install the latest firmware for their Switch.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"JIT (Just-In-Time) compilation allows MeloNX to run code at as fast as possible by translating it dynamically. This is necessary for running this emulator." : {
|
||||||
|
"comment" : "A description of what JIT is.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"JIT Enabled" : {
|
||||||
|
"comment" : "A heading displayed above the status of JIT and the amount of RAM.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"JIT Not Acquired" : {
|
||||||
|
"comment" : "A heading displayed above the status of JIT and the amount of RAM.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Joystick" : {
|
||||||
|
"comment" : "A label displayed inside the circle of a joystick.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Joystick Scale: %@" : {
|
||||||
|
"comment" : "A label displaying the current scale of a joystick.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Larger" : {
|
||||||
|
"comment" : "A placeholder text that indicates the minimum value of the slider.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Launch ${gameName}" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Launch Game" : {
|
||||||
|
"comment" : "Title of the intent.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Launch Mii Maker" : {
|
||||||
|
"comment" : "A button that launches the Mii Maker applet.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Launches the Selected Game." : {
|
||||||
|
"comment" : "A short description of the intent.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Layout Actions" : {
|
||||||
|
"comment" : "A heading displayed above a list of actions related to layouts.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Layout Options" : {
|
||||||
|
"comment" : "A button that allows the user to customize the layout of the controller.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Less Transparent" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Library" : {
|
||||||
|
"comment" : "A label displayed above the list of games in the library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Loading %@" : {
|
||||||
|
"comment" : "A label displayed while the game is loading.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"macOS %@" : {
|
||||||
|
"comment" : "A subheading displaying the macOS version running on the user's device.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Make Button Toggle" : {
|
||||||
|
"comment" : "A checkbox displayed next to a text label.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Max Anisotropic Filtering" : {
|
||||||
|
"comment" : "A title displayed above a description of a setting.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Memory Manager Mode" : {
|
||||||
|
"comment" : "A label displayed above a picker view that allows the user to choose the memory manager mode.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"More Transparent" : {
|
||||||
|
"comment" : "A heading displayed above the opacity of a UI element.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"No controllers selected (Keyboard will be used)" : {
|
||||||
|
"comment" : "A text indicating that no controllers are selected.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"No DLCs Found" : {
|
||||||
|
"comment" : "A message displayed when no DLCs are found.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"No Games Found" : {
|
||||||
|
"comment" : "A message displayed when a user has no games in their library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"No Updates Found" : {
|
||||||
|
"comment" : "The title of a view that displays a list of items.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Off" : {
|
||||||
|
"comment" : "A caption displayed above the slider for max anisotropic filtering.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"OK" : {
|
||||||
|
"comment" : "The button label to dismiss the alert.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"ON" : {
|
||||||
|
"comment" : "The text displayed when the toggle is on or off.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"On-Screen Controller" : {
|
||||||
|
"comment" : "A heading displayed above the scale settings for the on-screen controller.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"On-Screen Controller Opacity" : {
|
||||||
|
"comment" : "A heading displayed above the description of the opacity setting for the on-screen controller.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"On-Screen Controller Scale" : {
|
||||||
|
"comment" : "A title displayed above a message about the scale of the on-screen controller.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Open Game" : {
|
||||||
|
"comment" : "A button that allows the user to open a game file.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Options" : {
|
||||||
|
"comment" : "A button that displays a menu with options for the game library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Pause" : {
|
||||||
|
"comment" : "A label displayed above a button that pauses or plays the emulation.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Play" : {
|
||||||
|
"comment" : "A label displayed above a button that pauses or plays the emulation.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Play Now" : {
|
||||||
|
"comment" : "A button that allows the user to launch a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Player %lld: %@" : {
|
||||||
|
"comment" : "A label displaying the name of a controller and its position in the list of controllers.",
|
||||||
|
"isCommentAutoGenerated" : true,
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Player %1$lld: %2$@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Recent Games" : {
|
||||||
|
"comment" : "A heading displayed above a list of recently played games.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Remove from Recents" : {
|
||||||
|
"comment" : "A button that deletes a game from the user's library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Reset" : {
|
||||||
|
"comment" : "A button that resets all settings to their default values.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Reset Current" : {
|
||||||
|
"comment" : "A button that resets the layout to its default state.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Reset Selected" : {
|
||||||
|
"comment" : "A button that resets the layout settings for a specific button when pressed.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Reset to Default Layout" : {
|
||||||
|
"comment" : "A button label to reset a layout to its default state.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Resolution Scale" : {
|
||||||
|
"comment" : "The title of an alert displayed when the user taps the \"i\" icon next to the \"Resolution Scale\" label.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Save to Photos" : {
|
||||||
|
"comment" : "A button that saves an image to the user's Photos library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Separate arguments with commas" : {
|
||||||
|
"comment" : "A text field that allows the user to specify additional arguments to pass to the game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Set up your Nintendo Switch emulation environment by importing keys and firmware." : {
|
||||||
|
"comment" : "A description displayed below the welcome message.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Settings" : {
|
||||||
|
"comment" : "The title displayed in the navigation bar of the settings view.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Setup" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Show MeloNX Folder" : {
|
||||||
|
"comment" : "A button that opens the folder containing the game files on macOS.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Show Setup Screen" : {
|
||||||
|
"comment" : "A button label that shows a setup screen.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Skip" : {
|
||||||
|
"comment" : "A button that allows the user to skip the setup process.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Skip Setup?" : {
|
||||||
|
"comment" : "A prompt displayed when the user wants to skip setup.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Smaller" : {
|
||||||
|
"comment" : "A label displayed above the slider that controls the size of the on-screen controller.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Tap the + button to add game DLCs." : {
|
||||||
|
"comment" : "A message displayed when no game DLCs are found.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Tap the + button to add game updates." : {
|
||||||
|
"comment" : "A description displayed when no game updates are found.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"The cake is a lie" : {
|
||||||
|
"comment" : "A placeholder text for a card in the settings menu.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"This will delete the custom layout for this game and revert to the default layout." : {
|
||||||
|
"comment" : "A message displayed when the user confirms to delete the custom layout.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Unsupported Device" : {
|
||||||
|
"comment" : "An alert that appears on iPad devices with less than 4 GB of memory.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Update Manager" : {
|
||||||
|
"comment" : "A button that displays a sheet for updating a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Using Default" : {
|
||||||
|
"comment" : "A caption displayed when the user has not customized a layout for a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"v%@" : {
|
||||||
|
"comment" : "A text indicating the version of a game.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Version %@" : {
|
||||||
|
"comment" : "A label displaying the version of the app.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Version %@ Available!" : {
|
||||||
|
"comment" : "The title of the sheet that appears when a new version of the app is available.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Version %@ is available. You are currently on Version %@." : {
|
||||||
|
"comment" : "A message that informs users a new version of the app is available, along with the current version and the version that is available.",
|
||||||
|
"isCommentAutoGenerated" : true,
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Version %1$@ is available. You are currently on Version %2$@."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Waiting for JIT" : {
|
||||||
|
"comment" : "A heading displayed when MeloNX is waiting for JIT compilation to complete.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Welcome to MeloNX" : {
|
||||||
|
"comment" : "A welcome message displayed on the initial setup screen.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"wow" : {
|
||||||
|
"comment" : "A placeholder text used for demonstration purposes.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Your Device is an iPad with %@ of memory, MeloNX has issues with those devices" : {
|
||||||
|
"comment" : "A message displayed when the user's iPad has less than 4 GB of memory.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version" : "1.1"
|
||||||
|
}
|
Binary file not shown.
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// BreakJIT.h
|
||||||
|
// BreakpointJIT
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 09/07/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BreakGetJITMapping_h
|
||||||
|
#define BreakGetJITMapping_h
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function with inline assembly for JIT mapping
|
||||||
|
* @param bytes Size parameter for mapping
|
||||||
|
* @return char* pointer result
|
||||||
|
*/
|
||||||
|
__attribute__((noinline, optnone, naked)) char* BreakGetJITMapping(size_t bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BreakGetJITMapping_h */
|
Binary file not shown.
@ -1,330 +0,0 @@
|
|||||||
#if 0
|
|
||||||
#elif defined(__arm64__) && __arm64__
|
|
||||||
// Generated by Apple Swift version 6.0.3 effective-5.10 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
|
|
||||||
#ifndef STOSJIT_SWIFT_H
|
|
||||||
#define STOSJIT_SWIFT_H
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wgcc-compat"
|
|
||||||
|
|
||||||
#if !defined(__has_include)
|
|
||||||
# define __has_include(x) 0
|
|
||||||
#endif
|
|
||||||
#if !defined(__has_attribute)
|
|
||||||
# define __has_attribute(x) 0
|
|
||||||
#endif
|
|
||||||
#if !defined(__has_feature)
|
|
||||||
# define __has_feature(x) 0
|
|
||||||
#endif
|
|
||||||
#if !defined(__has_warning)
|
|
||||||
# define __has_warning(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __has_include(<swift/objc-prologue.h>)
|
|
||||||
# include <swift/objc-prologue.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma clang diagnostic ignored "-Wauto-import"
|
|
||||||
#if defined(__OBJC__)
|
|
||||||
#include <Foundation/Foundation.h>
|
|
||||||
#endif
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdbool>
|
|
||||||
#include <cstring>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <new>
|
|
||||||
#include <type_traits>
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
|
|
||||||
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
|
|
||||||
# include <ptrauth.h>
|
|
||||||
#else
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
|
|
||||||
# ifndef __ptrauth_swift_value_witness_function_pointer
|
|
||||||
# define __ptrauth_swift_value_witness_function_pointer(x)
|
|
||||||
# endif
|
|
||||||
# ifndef __ptrauth_swift_class_method_pointer
|
|
||||||
# define __ptrauth_swift_class_method_pointer(x)
|
|
||||||
# endif
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(SWIFT_TYPEDEFS)
|
|
||||||
# define SWIFT_TYPEDEFS 1
|
|
||||||
# if __has_include(<uchar.h>)
|
|
||||||
# include <uchar.h>
|
|
||||||
# elif !defined(__cplusplus)
|
|
||||||
typedef uint_least16_t char16_t;
|
|
||||||
typedef uint_least32_t char32_t;
|
|
||||||
# endif
|
|
||||||
typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
|
|
||||||
typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
|
|
||||||
typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
|
|
||||||
typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
|
|
||||||
typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
|
|
||||||
typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
|
|
||||||
typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
|
|
||||||
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
|
|
||||||
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
|
|
||||||
typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
|
|
||||||
typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
|
|
||||||
typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(SWIFT_PASTE)
|
|
||||||
# define SWIFT_PASTE_HELPER(x, y) x##y
|
|
||||||
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_METATYPE)
|
|
||||||
# define SWIFT_METATYPE(X) Class
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_CLASS_PROPERTY)
|
|
||||||
# if __has_feature(objc_class_property)
|
|
||||||
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
|
|
||||||
# else
|
|
||||||
# define SWIFT_CLASS_PROPERTY(...)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_RUNTIME_NAME)
|
|
||||||
# if __has_attribute(objc_runtime_name)
|
|
||||||
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
|
|
||||||
# else
|
|
||||||
# define SWIFT_RUNTIME_NAME(X)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_COMPILE_NAME)
|
|
||||||
# if __has_attribute(swift_name)
|
|
||||||
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
|
|
||||||
# else
|
|
||||||
# define SWIFT_COMPILE_NAME(X)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_METHOD_FAMILY)
|
|
||||||
# if __has_attribute(objc_method_family)
|
|
||||||
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
|
|
||||||
# else
|
|
||||||
# define SWIFT_METHOD_FAMILY(X)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_NOESCAPE)
|
|
||||||
# if __has_attribute(noescape)
|
|
||||||
# define SWIFT_NOESCAPE __attribute__((noescape))
|
|
||||||
# else
|
|
||||||
# define SWIFT_NOESCAPE
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_RELEASES_ARGUMENT)
|
|
||||||
# if __has_attribute(ns_consumed)
|
|
||||||
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
|
|
||||||
# else
|
|
||||||
# define SWIFT_RELEASES_ARGUMENT
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_WARN_UNUSED_RESULT)
|
|
||||||
# if __has_attribute(warn_unused_result)
|
|
||||||
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
|
||||||
# else
|
|
||||||
# define SWIFT_WARN_UNUSED_RESULT
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_NORETURN)
|
|
||||||
# if __has_attribute(noreturn)
|
|
||||||
# define SWIFT_NORETURN __attribute__((noreturn))
|
|
||||||
# else
|
|
||||||
# define SWIFT_NORETURN
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_CLASS_EXTRA)
|
|
||||||
# define SWIFT_CLASS_EXTRA
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_PROTOCOL_EXTRA)
|
|
||||||
# define SWIFT_PROTOCOL_EXTRA
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_ENUM_EXTRA)
|
|
||||||
# define SWIFT_ENUM_EXTRA
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_CLASS)
|
|
||||||
# if __has_attribute(objc_subclassing_restricted)
|
|
||||||
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
|
|
||||||
# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
|
||||||
# else
|
|
||||||
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
|
||||||
# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_RESILIENT_CLASS)
|
|
||||||
# if __has_attribute(objc_class_stub)
|
|
||||||
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
|
|
||||||
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
|
|
||||||
# else
|
|
||||||
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
|
|
||||||
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_PROTOCOL)
|
|
||||||
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
|
|
||||||
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_EXTENSION)
|
|
||||||
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
|
|
||||||
#endif
|
|
||||||
#if !defined(OBJC_DESIGNATED_INITIALIZER)
|
|
||||||
# if __has_attribute(objc_designated_initializer)
|
|
||||||
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
|
|
||||||
# else
|
|
||||||
# define OBJC_DESIGNATED_INITIALIZER
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_ENUM_ATTR)
|
|
||||||
# if __has_attribute(enum_extensibility)
|
|
||||||
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
|
|
||||||
# else
|
|
||||||
# define SWIFT_ENUM_ATTR(_extensibility)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_ENUM)
|
|
||||||
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
|
|
||||||
# if __has_feature(generalized_swift_name)
|
|
||||||
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
|
|
||||||
# else
|
|
||||||
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_UNAVAILABLE)
|
|
||||||
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_UNAVAILABLE_MSG)
|
|
||||||
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_AVAILABILITY)
|
|
||||||
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_WEAK_IMPORT)
|
|
||||||
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_DEPRECATED)
|
|
||||||
# define SWIFT_DEPRECATED __attribute__((deprecated))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_DEPRECATED_MSG)
|
|
||||||
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_DEPRECATED_OBJC)
|
|
||||||
# if __has_feature(attribute_diagnose_if_objc)
|
|
||||||
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
|
|
||||||
# else
|
|
||||||
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if defined(__OBJC__)
|
|
||||||
#if !defined(IBSegueAction)
|
|
||||||
# define IBSegueAction
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_EXTERN)
|
|
||||||
# if defined(__cplusplus)
|
|
||||||
# define SWIFT_EXTERN extern "C"
|
|
||||||
# else
|
|
||||||
# define SWIFT_EXTERN extern
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_CALL)
|
|
||||||
# define SWIFT_CALL __attribute__((swiftcall))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_INDIRECT_RESULT)
|
|
||||||
# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_CONTEXT)
|
|
||||||
# define SWIFT_CONTEXT __attribute__((swift_context))
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_ERROR_RESULT)
|
|
||||||
# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
|
|
||||||
#endif
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
# define SWIFT_NOEXCEPT noexcept
|
|
||||||
#else
|
|
||||||
# define SWIFT_NOEXCEPT
|
|
||||||
#endif
|
|
||||||
#if !defined(SWIFT_C_INLINE_THUNK)
|
|
||||||
# if __has_attribute(always_inline)
|
|
||||||
# if __has_attribute(nodebug)
|
|
||||||
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug))
|
|
||||||
# else
|
|
||||||
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline))
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
# define SWIFT_C_INLINE_THUNK inline
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
|
|
||||||
# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
|
|
||||||
# define SWIFT_IMPORT_STDLIB_SYMBOL
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#if defined(__OBJC__)
|
|
||||||
#if __has_feature(objc_modules)
|
|
||||||
#if __has_warning("-Watimport-in-framework-header")
|
|
||||||
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
|
|
||||||
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
|
|
||||||
#if __has_warning("-Wpragma-clang-attribute")
|
|
||||||
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
|
|
||||||
#endif
|
|
||||||
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
|
||||||
#pragma clang diagnostic ignored "-Wnullability"
|
|
||||||
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
|
|
||||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
|
||||||
|
|
||||||
#if __has_attribute(external_source_symbol)
|
|
||||||
# pragma push_macro("any")
|
|
||||||
# undef any
|
|
||||||
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="StosJIT",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
|
|
||||||
# pragma pop_macro("any")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__OBJC__)
|
|
||||||
|
|
||||||
SWIFT_EXTERN char * _Nullable attach(int32_t pid) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
|
|
||||||
SWIFT_EXTERN char * _Nullable debugattachanddetachApp(char * _Nonnull bundleId) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
|
|
||||||
SWIFT_EXTERN void detach(void) SWIFT_NOEXCEPT;
|
|
||||||
|
|
||||||
|
|
||||||
SWIFT_EXTERN void loop_heartbeat(void) SWIFT_NOEXCEPT;
|
|
||||||
|
|
||||||
|
|
||||||
SWIFT_EXTERN BOOL writeZeroToMemory(uint64_t addr, int32_t length) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#if __has_attribute(external_source_symbol)
|
|
||||||
# pragma clang attribute pop
|
|
||||||
#endif
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#endif
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
#error unsupported Swift architecture
|
|
||||||
#endif
|
|
@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// StosJIT.h
|
|
||||||
// StosJIT
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 10/05/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <StosJIT/idevice.h>
|
|
||||||
|
|
||||||
//! Project version number for StosJIT.
|
|
||||||
FOUNDATION_EXPORT double StosJITVersionNumber;
|
|
||||||
|
|
||||||
//! Project version string for StosJIT.
|
|
||||||
FOUNDATION_EXPORT const unsigned char StosJITVersionString[];
|
|
||||||
|
|
||||||
// In this header, you should import all the public headers of your framework using statements like #import <StosJIT/PublicHeader.h>
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1,205 +0,0 @@
|
|||||||
<?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>
|
|
||||||
<key>files</key>
|
|
||||||
<dict>
|
|
||||||
<key>.DS_Store</key>
|
|
||||||
<data>
|
|
||||||
7Mfr8shT4pXWBr/plN+uNkIabdM=
|
|
||||||
</data>
|
|
||||||
<key>Headers/StosJIT-Swift.h</key>
|
|
||||||
<data>
|
|
||||||
h9vaTwhC6FlnyKmIkaxLQGlFd1g=
|
|
||||||
</data>
|
|
||||||
<key>Headers/StosJIT.h</key>
|
|
||||||
<data>
|
|
||||||
ggHr5wlLNIIPydwUL9Vxm6abxjo=
|
|
||||||
</data>
|
|
||||||
<key>Headers/idevice.h</key>
|
|
||||||
<data>
|
|
||||||
mHDz7368FsBID56/epJ2NgIkha4=
|
|
||||||
</data>
|
|
||||||
<key>Headers/plist.h</key>
|
|
||||||
<data>
|
|
||||||
bL/f0MQDpLfvIcI1zxPwMuJ/PfI=
|
|
||||||
</data>
|
|
||||||
<key>Info.plist</key>
|
|
||||||
<data>
|
|
||||||
ZTTwPKlta/gjXAr1HIHmyAxeU4E=
|
|
||||||
</data>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
|
|
||||||
<data>
|
|
||||||
nihJghwM5m7kxkQD7UvrWyHkLy8=
|
|
||||||
</data>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json</key>
|
|
||||||
<data>
|
|
||||||
gcwBsH4BgyFY4sVtNt+/xOKS3vY=
|
|
||||||
</data>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc</key>
|
|
||||||
<data>
|
|
||||||
YPtkDrAuBiPPEp4ZdRdBVlFXnRM=
|
|
||||||
</data>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule</key>
|
|
||||||
<data>
|
|
||||||
9cIInnjJzJFtY+CZm2iNo5qL3MQ=
|
|
||||||
</data>
|
|
||||||
<key>Modules/module.modulemap</key>
|
|
||||||
<data>
|
|
||||||
cnpvYzvLIwWcxkQodj5uLbHkyRk=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>files2</key>
|
|
||||||
<dict>
|
|
||||||
<key>Headers/StosJIT-Swift.h</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
1obIr4IjMvtcyNyYIV/Nh/5wahcA1cFjc4n4XVlNt2I=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Headers/StosJIT.h</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
yY9KyrRdOYRdlb7G6wVMU2hogasXMjwV5r8jUIk44ok=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Headers/idevice.h</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
zR9/TB9Dnv3uRC8qqGvaQ6c2yyOFUURmrHKLdEiUh/g=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Headers/plist.h</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
yFbGsiXBBp91tfsSFtS0Utt2Gpc3MEDFiMVXKG9q1rs=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
+Ehvco7cQbAaF7zufvBYTiGXFp37Hjym/Pav514sGPk=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
Qnesa0n4URGWAopawg9bGx36dUwkYV00BoCJ8LFzlyg=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
k7F2Xs2hh9iMbK8IE8TMtN6gjQ9kWs30NUKHeupq6VE=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
gMDYNHcBPCNwZw2A5mEUiCyYAS9VhtQG0z+/WqAUrOQ=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
<key>Modules/module.modulemap</key>
|
|
||||||
<dict>
|
|
||||||
<key>hash2</key>
|
|
||||||
<data>
|
|
||||||
FGwGKs5SNvpCyiIWiOP4eml9m2e3KISmtCJVtNnUnUc=
|
|
||||||
</data>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>rules</key>
|
|
||||||
<dict>
|
|
||||||
<key>^.*</key>
|
|
||||||
<true/>
|
|
||||||
<key>^.*\.lproj/</key>
|
|
||||||
<dict>
|
|
||||||
<key>optional</key>
|
|
||||||
<true/>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>1000</real>
|
|
||||||
</dict>
|
|
||||||
<key>^.*\.lproj/locversion.plist$</key>
|
|
||||||
<dict>
|
|
||||||
<key>omit</key>
|
|
||||||
<true/>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>1100</real>
|
|
||||||
</dict>
|
|
||||||
<key>^Base\.lproj/</key>
|
|
||||||
<dict>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>1010</real>
|
|
||||||
</dict>
|
|
||||||
<key>^version.plist$</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>rules2</key>
|
|
||||||
<dict>
|
|
||||||
<key>.*\.dSYM($|/)</key>
|
|
||||||
<dict>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>11</real>
|
|
||||||
</dict>
|
|
||||||
<key>^(.*/)?\.DS_Store$</key>
|
|
||||||
<dict>
|
|
||||||
<key>omit</key>
|
|
||||||
<true/>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>2000</real>
|
|
||||||
</dict>
|
|
||||||
<key>^.*</key>
|
|
||||||
<true/>
|
|
||||||
<key>^.*\.lproj/</key>
|
|
||||||
<dict>
|
|
||||||
<key>optional</key>
|
|
||||||
<true/>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>1000</real>
|
|
||||||
</dict>
|
|
||||||
<key>^.*\.lproj/locversion.plist$</key>
|
|
||||||
<dict>
|
|
||||||
<key>omit</key>
|
|
||||||
<true/>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>1100</real>
|
|
||||||
</dict>
|
|
||||||
<key>^Base\.lproj/</key>
|
|
||||||
<dict>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>1010</real>
|
|
||||||
</dict>
|
|
||||||
<key>^Info\.plist$</key>
|
|
||||||
<dict>
|
|
||||||
<key>omit</key>
|
|
||||||
<true/>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>20</real>
|
|
||||||
</dict>
|
|
||||||
<key>^PkgInfo$</key>
|
|
||||||
<dict>
|
|
||||||
<key>omit</key>
|
|
||||||
<true/>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>20</real>
|
|
||||||
</dict>
|
|
||||||
<key>^embedded\.provisionprofile$</key>
|
|
||||||
<dict>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>20</real>
|
|
||||||
</dict>
|
|
||||||
<key>^version\.plist$</key>
|
|
||||||
<dict>
|
|
||||||
<key>weight</key>
|
|
||||||
<real>20</real>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Binary file not shown.
@ -81,16 +81,16 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
||||||
{
|
{
|
||||||
if ((uint)index > (uint)coefficients.Length)
|
if ((uint)index < (uint)coefficients.Length)
|
||||||
{
|
{
|
||||||
|
return coefficients[index];
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return coefficients[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
|
public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
|
||||||
{
|
{
|
||||||
|
@ -2,23 +2,17 @@ using ARMeilleure.Memory;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||||
{
|
{
|
||||||
class WriteZeroCache : IDisposable
|
class DualMappedNoWxCache : IDisposable
|
||||||
{
|
{
|
||||||
private const int CodeAlignment = 4;
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int InitialCacheSize = 2 * 1024 * 1024;
|
private const int SharedCacheSize = 512 * 1024 * 1024;
|
||||||
private const int GrowthCacheSize = 2 * 1024 * 1024;
|
private const int LocalCacheSize = 128 * 1024 * 1024;
|
||||||
private const int MaxSharedCacheSize = 512 * 1024 * 1024;
|
|
||||||
private const int MaxLocalCacheSize = 128 * 1024 * 1024;
|
|
||||||
|
|
||||||
[DllImport("StosJIT.framework/StosJIT", EntryPoint = "writeZeroToMemory")]
|
|
||||||
public static extern bool WriteZeroToMemory(ulong addr, int length);
|
|
||||||
|
|
||||||
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
||||||
// and allow the guest to take the fast path.
|
// and allow the guest to take the fast path.
|
||||||
@ -26,105 +20,25 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
private class MemoryCache : IDisposable
|
private class MemoryCache : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ReservedRegion _region;
|
private readonly DualMappedJitAllocator _allocator;
|
||||||
private readonly CacheMemoryAllocator _cacheAllocator;
|
private readonly CacheMemoryAllocator _cacheAllocator;
|
||||||
public readonly IJitMemoryAllocator Allocator;
|
public DualMappedJitAllocator Allocator => _allocator;
|
||||||
private readonly ulong _maxSize;
|
public IntPtr RwPointer => _allocator.RwPtr;
|
||||||
private ulong _currentSize;
|
public IntPtr RxPointer => _allocator.RxPtr;
|
||||||
|
|
||||||
private readonly Dictionary<int, HashSet<int>> _reusePages;
|
|
||||||
private readonly object _reuselock = new object();
|
|
||||||
|
|
||||||
public CacheMemoryAllocator CacheAllocator => _cacheAllocator;
|
public CacheMemoryAllocator CacheAllocator => _cacheAllocator;
|
||||||
public IntPtr Pointer => _region.Block.Pointer;
|
public IntPtr Pointer => _allocator.RwPtr;
|
||||||
public ulong CurrentSize => _currentSize;
|
|
||||||
public ulong MaxSize => _maxSize;
|
|
||||||
|
|
||||||
public MemoryCache(IJitMemoryAllocator allocator, ulong maxSize)
|
public MemoryCache(ulong size)
|
||||||
{
|
{
|
||||||
Allocator = allocator;
|
_allocator = new DualMappedJitAllocator(size);
|
||||||
_maxSize = maxSize;
|
_cacheAllocator = new((int)size);
|
||||||
_currentSize = InitialCacheSize;
|
|
||||||
|
|
||||||
|
|
||||||
_region = new(allocator, maxSize);
|
|
||||||
_cacheAllocator = new((int)maxSize);
|
|
||||||
|
|
||||||
_reusePages = new Dictionary<int, HashSet<int>>();
|
|
||||||
|
|
||||||
_region.Block.MapAsRw(0, _currentSize);
|
|
||||||
_region.ExpandIfNeeded(_currentSize);
|
|
||||||
|
|
||||||
WriteZeroToMemory((ulong)_region.Block.Pointer.ToInt64(), (int)_currentSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetReusablePage(int size, out int offset)
|
|
||||||
{
|
|
||||||
lock (_reuselock)
|
|
||||||
{
|
|
||||||
if (_reusePages.TryGetValue(size, out var exactOffsets) && exactOffsets.Count > 0)
|
|
||||||
{
|
|
||||||
offset = exactOffsets.First();
|
|
||||||
exactOffsets.Remove(offset);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var largerSizes = _reusePages.Where(kvp => kvp.Key > size && kvp.Value.Count > 0)
|
|
||||||
.OrderBy(kvp => kvp.Key)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (largerSizes.Value != null && largerSizes.Value.Count > 0)
|
|
||||||
{
|
|
||||||
int largerSize = largerSizes.Key;
|
|
||||||
var largerOffsets = largerSizes.Value;
|
|
||||||
|
|
||||||
offset = largerOffsets.First();
|
|
||||||
largerOffsets.Remove(offset);
|
|
||||||
|
|
||||||
int remainingSize = largerSize - size;
|
|
||||||
if (remainingSize > 0)
|
|
||||||
{
|
|
||||||
AddReusablePage(offset + size, remainingSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = -1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddReusablePage(int offset, int size)
|
|
||||||
{
|
|
||||||
if (size < (int)MemoryBlock.GetPageSize())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_reuselock)
|
|
||||||
{
|
|
||||||
if (!_reusePages.TryGetValue(size, out var offsets))
|
|
||||||
{
|
|
||||||
offsets = new HashSet<int>();
|
|
||||||
_reusePages[size] = offsets;
|
|
||||||
}
|
|
||||||
offsets.Add(offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Allocate(int codeSize)
|
public int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
if (codeSize >= (int)MemoryBlock.GetPageSize() &&
|
|
||||||
(codeSize % (int)MemoryBlock.GetPageSize() == 0) &&
|
|
||||||
TryGetReusablePage(codeSize, out int reuseOffset))
|
|
||||||
{
|
|
||||||
ReprotectAsRw(reuseOffset, codeSize);
|
|
||||||
return reuseOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
if (allocOffset < 0)
|
if (allocOffset < 0)
|
||||||
@ -132,63 +46,19 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ulong requiredSize = (ulong)allocOffset + (ulong)codeSize;
|
|
||||||
if (requiredSize > _currentSize)
|
|
||||||
{
|
|
||||||
ulong neededGrowth = requiredSize - _currentSize;
|
|
||||||
ulong growthIncrements = (neededGrowth + GrowthCacheSize - 1) / GrowthCacheSize;
|
|
||||||
ulong newSize = _currentSize + (growthIncrements * GrowthCacheSize);
|
|
||||||
|
|
||||||
newSize = Math.Min(newSize, _maxSize);
|
|
||||||
|
|
||||||
if (newSize <= _currentSize || requiredSize > newSize)
|
|
||||||
{
|
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted, cannot grow further.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_region.Block.MapAsRw(_currentSize, newSize - _currentSize);
|
|
||||||
_region.ExpandIfNeeded(newSize);
|
|
||||||
|
|
||||||
WriteZeroToMemory((ulong)(_region.Block.Pointer.ToInt64() + (long)_currentSize), (int)(newSize - _currentSize));
|
|
||||||
|
|
||||||
_currentSize = newSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allocOffset;
|
return allocOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Free(int offset, int size)
|
public void Free(int offset, int size)
|
||||||
{
|
|
||||||
if (size >= (int)MemoryBlock.GetPageSize() && (size % (int)MemoryBlock.GetPageSize() == 0) &&
|
|
||||||
(offset % (int)MemoryBlock.GetPageSize() == 0))
|
|
||||||
{
|
|
||||||
AddReusablePage(offset, size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(offset, size);
|
_cacheAllocator.Free(offset, size);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void ReprotectAsRw(int offset, int size)
|
public void SysIcacheInvalidate(int offset, int size)
|
||||||
{
|
{
|
||||||
Debug.Assert(offset >= 0 && (offset & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
|
||||||
Debug.Assert(size > 0 && (size & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
|
||||||
|
|
||||||
_region.Block.MapAsRw((ulong)offset, (ulong)size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReprotectAsRx(int offset, int size)
|
|
||||||
{
|
|
||||||
Debug.Assert(offset >= 0 && (offset & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
|
||||||
Debug.Assert(size > 0 && (size & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
|
||||||
|
|
||||||
_region.Block.MapAsRx((ulong)offset, (ulong)size);
|
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||||
{
|
{
|
||||||
JitSupportDarwin.SysIcacheInvalidate(_region.Block.Pointer + offset, size);
|
JitSupportDarwin.SysIcacheInvalidate(_allocator.RxPtr + offset, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -196,14 +66,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearReusePool()
|
|
||||||
{
|
|
||||||
lock (_reuselock)
|
|
||||||
{
|
|
||||||
_reusePages.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
{
|
{
|
||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
@ -213,8 +75,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
ClearReusePool();
|
_allocator.Dispose();
|
||||||
_region.Dispose();
|
|
||||||
_cacheAllocator.Clear();
|
_cacheAllocator.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,12 +120,12 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static Dictionary<ulong, ThreadLocalCacheEntry> _threadLocalCache;
|
private static Dictionary<ulong, ThreadLocalCacheEntry> _threadLocalCache;
|
||||||
|
|
||||||
public WriteZeroCache(IJitMemoryAllocator allocator, IStackWalker stackWalker, Translator translator)
|
public DualMappedNoWxCache(IJitMemoryAllocator allocator, IStackWalker stackWalker, Translator translator)
|
||||||
{
|
{
|
||||||
_stackWalker = stackWalker;
|
_stackWalker = stackWalker;
|
||||||
_translator = translator;
|
_translator = translator;
|
||||||
_sharedCaches = new List<MemoryCache> { new(allocator, MaxSharedCacheSize) };
|
_sharedCaches = new List<MemoryCache> { new(SharedCacheSize) };
|
||||||
_localCaches = new List<MemoryCache> { new(allocator, MaxLocalCacheSize) };
|
_localCaches = new List<MemoryCache> { new(LocalCacheSize) };
|
||||||
_pendingMaps = new Dictionary<ulong, PageAlignedRangeList>();
|
_pendingMaps = new Dictionary<ulong, PageAlignedRangeList>();
|
||||||
_lock = new();
|
_lock = new();
|
||||||
}
|
}
|
||||||
@ -275,7 +136,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
if (!_pendingMaps.TryGetValue(cacheKey, out var pendingMap))
|
if (!_pendingMaps.TryGetValue(cacheKey, out var pendingMap))
|
||||||
{
|
{
|
||||||
pendingMap = new PageAlignedRangeList(
|
pendingMap = new PageAlignedRangeList(
|
||||||
(offset, size) => _sharedCaches[cacheIndex].ReprotectAsRx(offset, size),
|
(offset, size) => _sharedCaches[cacheIndex].SysIcacheInvalidate(offset, size),
|
||||||
(address, func) => RegisterFunction(address, func));
|
(address, func) => RegisterFunction(address, func));
|
||||||
_pendingMaps[cacheKey] = pendingMap;
|
_pendingMaps[cacheKey] = pendingMap;
|
||||||
}
|
}
|
||||||
@ -304,15 +165,13 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
catch (OutOfMemoryException)
|
catch (OutOfMemoryException)
|
||||||
{
|
{
|
||||||
// Try next cache
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All existing caches are full, create a new one
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
var allocator = _sharedCaches[0].Allocator;
|
var allocator = _sharedCaches[0].Allocator;
|
||||||
_sharedCaches.Add(new(allocator, MaxSharedCacheSize));
|
_sharedCaches.Add(new(SharedCacheSize));
|
||||||
return (_sharedCaches.Count - 1) << 28 | _sharedCaches[_sharedCaches.Count - 1].Allocate(codeLength);
|
return (_sharedCaches.Count - 1) << 28 | _sharedCaches[_sharedCaches.Count - 1].Allocate(codeLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,14 +186,14 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
catch (OutOfMemoryException)
|
catch (OutOfMemoryException)
|
||||||
{
|
{
|
||||||
// Try next cache
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
var allocator = _localCaches[0].Allocator;
|
var allocator = _localCaches[0].Allocator;
|
||||||
_localCaches.Add(new(allocator, MaxLocalCacheSize));
|
_localCaches.Add(new(LocalCacheSize));
|
||||||
return (_localCaches.Count - 1) << 28 | _localCaches[_localCaches.Count - 1].Allocate(codeLength);
|
return (_localCaches.Count - 1) << 28 | _localCaches[_localCaches.Count - 1].Allocate(codeLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,8 +219,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
MemoryCache cache = _sharedCaches[cacheIndex];
|
MemoryCache cache = _sharedCaches[cacheIndex];
|
||||||
funcPtr = cache.Pointer + funcOffset;
|
funcPtr = cache.Pointer + funcOffset;
|
||||||
|
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||||
|
funcPtr = cache.RxPointer + funcOffset;
|
||||||
|
|
||||||
TranslatedFunction function = new(funcPtr, guestSize);
|
TranslatedFunction function = new(funcPtr, guestSize);
|
||||||
|
|
||||||
@ -396,21 +255,22 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0);
|
Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0);
|
||||||
|
|
||||||
IntPtr funcPtr1 = _sharedCaches[cacheIndex].Pointer + funcOffset;
|
IntPtr funcPtr1 = _sharedCaches[cacheIndex].Pointer + funcOffset;
|
||||||
|
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr1, code.Length));
|
code.CopyTo(new Span<byte>((void*)funcPtr1, code.Length));
|
||||||
|
funcPtr1 = _sharedCaches[cacheIndex].RxPointer + funcOffset;
|
||||||
|
|
||||||
_sharedCaches[cacheIndex].ReprotectAsRx(funcOffset, sizeAligned);
|
_sharedCaches[cacheIndex].SysIcacheInvalidate(funcOffset, sizeAligned);
|
||||||
|
|
||||||
return funcPtr1;
|
return funcPtr1;
|
||||||
}
|
}
|
||||||
catch (OutOfMemoryException)
|
catch (OutOfMemoryException)
|
||||||
{
|
{
|
||||||
// Try next cache
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var allocator = _sharedCaches[0].Allocator;
|
var allocator = _sharedCaches[0].Allocator;
|
||||||
var newCache = new MemoryCache(allocator, MaxSharedCacheSize);
|
var newCache = new MemoryCache(SharedCacheSize);
|
||||||
_sharedCaches.Add(newCache);
|
_sharedCaches.Add(newCache);
|
||||||
cacheIndex = _sharedCaches.Count - 1;
|
cacheIndex = _sharedCaches.Count - 1;
|
||||||
|
|
||||||
@ -425,8 +285,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
IntPtr funcPtr = newCache.Pointer + funcOffset;
|
IntPtr funcPtr = newCache.Pointer + funcOffset;
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||||
|
funcPtr = newCache.RxPointer + funcOffset;
|
||||||
|
|
||||||
newCache.ReprotectAsRx(funcOffset, newSizeAligned);
|
newCache.SysIcacheInvalidate(funcOffset, newSizeAligned);
|
||||||
|
|
||||||
return funcPtr;
|
return funcPtr;
|
||||||
}
|
}
|
||||||
@ -481,8 +342,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
for (int i = 0; i < _localCaches.Count; i++)
|
for (int i = 0; i < _localCaches.Count; i++)
|
||||||
{
|
{
|
||||||
cachePointers[i] = _localCaches[i].Pointer;
|
cachePointers[i] = _localCaches[i].RxPointer;
|
||||||
cacheSizes[i] = (int)_localCaches[i].CurrentSize;
|
cacheSizes[i] = LocalCacheSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntPtr[] sharedPointers = new IntPtr[_sharedCaches.Count];
|
IntPtr[] sharedPointers = new IntPtr[_sharedCaches.Count];
|
||||||
@ -490,19 +351,20 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
for (int i = 0; i < _sharedCaches.Count; i++)
|
for (int i = 0; i < _sharedCaches.Count; i++)
|
||||||
{
|
{
|
||||||
sharedPointers[i] = _sharedCaches[i].Pointer;
|
sharedPointers[i] = _sharedCaches[i].RxPointer;
|
||||||
sharedSizes[i] = (int)_sharedCaches[i].CurrentSize;
|
sharedSizes[i] = SharedCacheSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterate over the arrays and pass each element to GetCallStack
|
||||||
IEnumerable<ulong> callStack = null;
|
IEnumerable<ulong> callStack = null;
|
||||||
for (int i = 0; i < _localCaches.Count; i++)
|
for (int i = 0; i < _localCaches.Count; i++)
|
||||||
{
|
{
|
||||||
callStack = _stackWalker.GetCallStack(
|
callStack = _stackWalker.GetCallStack(
|
||||||
framePointer,
|
framePointer,
|
||||||
cachePointers[i],
|
cachePointers[i], // Passing each individual cachePointer
|
||||||
cacheSizes[i],
|
cacheSizes[i], // Passing each individual cacheSize
|
||||||
sharedPointers[i],
|
sharedPointers[i], // Passing each individual sharedPointer
|
||||||
sharedSizes[i]
|
sharedSizes[i] // Passing each individual sharedSize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,12 +372,16 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
foreach ((ulong address, ThreadLocalCacheEntry entry) in _threadLocalCache)
|
foreach ((ulong address, ThreadLocalCacheEntry entry) in _threadLocalCache)
|
||||||
{
|
{
|
||||||
|
// We only want to delete if the function is already on the shared cache,
|
||||||
|
// otherwise we will keep translating the same function over and over again.
|
||||||
bool canDelete = !HasInAnyPendingMap(address);
|
bool canDelete = !HasInAnyPendingMap(address);
|
||||||
if (!canDelete)
|
if (!canDelete)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can only delete if the function is not part of the current thread call stack,
|
||||||
|
// otherwise we will crash the program when the thread returns to it.
|
||||||
foreach (ulong funcAddress in callStack)
|
foreach (ulong funcAddress in callStack)
|
||||||
{
|
{
|
||||||
if (funcAddress >= (ulong)entry.FuncPtr && funcAddress < (ulong)entry.FuncPtr + (ulong)entry.Size)
|
if (funcAddress >= (ulong)entry.FuncPtr && funcAddress < (ulong)entry.FuncPtr + (ulong)entry.Size)
|
||||||
@ -541,12 +407,14 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
var (cacheIndex, offset) = SplitCacheOffset(entry.Offset);
|
var (cacheIndex, offset) = SplitCacheOffset(entry.Offset);
|
||||||
|
|
||||||
_localCaches[cacheIndex].Free(offset, sizeAligned);
|
_localCaches[cacheIndex].Free(offset, sizeAligned);
|
||||||
_localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void ClearEntireThreadLocalCache()
|
public void ClearEntireThreadLocalCache()
|
||||||
{
|
{
|
||||||
|
// Thread is exiting, delete everything.
|
||||||
|
|
||||||
if (_threadLocalCache == null)
|
if (_threadLocalCache == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -560,7 +428,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
var (cacheIndex, offset) = SplitCacheOffset(entry.Offset);
|
var (cacheIndex, offset) = SplitCacheOffset(entry.Offset);
|
||||||
|
|
||||||
_localCaches[cacheIndex].Free(offset, sizeAligned);
|
_localCaches[cacheIndex].Free(offset, sizeAligned);
|
||||||
_localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_threadLocalCache.Clear();
|
_threadLocalCache.Clear();
|
||||||
@ -577,10 +444,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
IntPtr funcPtr = _localCaches[cacheIndex].Pointer + funcOffset;
|
IntPtr funcPtr = _localCaches[cacheIndex].Pointer + funcOffset;
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||||
|
funcPtr = _localCaches[cacheIndex].RxPointer + funcOffset;
|
||||||
|
|
||||||
(_threadLocalCache ??= new()).Add(guestAddress, new(funcOffset, code.Length, funcPtr, cacheIndex));
|
(_threadLocalCache ??= new()).Add(guestAddress, new(funcOffset, code.Length, funcPtr, cacheIndex));
|
||||||
|
|
||||||
_localCaches[cacheIndex].ReprotectAsRx(funcOffset, alignedSize);
|
_localCaches[cacheIndex].SysIcacheInvalidate(funcOffset, alignedSize);
|
||||||
|
|
||||||
return funcPtr;
|
return funcPtr;
|
||||||
}
|
}
|
@ -5,6 +5,7 @@ using System.Runtime.Versioning;
|
|||||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||||
{
|
{
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
|
[SupportedOSPlatform("ios")]
|
||||||
static partial class JitSupportDarwin
|
static partial class JitSupportDarwin
|
||||||
{
|
{
|
||||||
[LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")]
|
[LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")]
|
||||||
|
@ -41,7 +41,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
|
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
private readonly NoWxCache _noWxCache;
|
private readonly NoWxCache _noWxCache;
|
||||||
private readonly WriteZeroCache _writeZeroCache;
|
private readonly DualMappedNoWxCache _dualMappedCache;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||||
@ -57,27 +57,14 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
|
|
||||||
if (IsNoWxPlatform)
|
if (IsNoWxPlatform)
|
||||||
{
|
{
|
||||||
if (File.Exists("/System/Library/CoreServices/SystemVersion.plist"))
|
string dualMapped = Environment.GetEnvironmentVariable("DUAL_MAPPED_JIT");
|
||||||
|
if (dualMapped == "1") //(OperatingSystem.IsIOSVersionAtLeast(19) || OperatingSystem.IsIOSVersionAtLeast(26))
|
||||||
{
|
{
|
||||||
string content = File.ReadAllText("/System/Library/CoreServices/SystemVersion.plist");
|
Console.WriteLine($"Dual Mapped JIT enabled.");
|
||||||
if (content.Contains("22E5200s") && content.Contains("18.4") && content.Contains("Beta"))
|
_dualMappedCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
|
||||||
{
|
|
||||||
// iOS 18.4db1 (22E5200s) disables traditional JIT (R/X) and needs a debugger to fill to the page to make the executable region a debug map.
|
|
||||||
// Apple has confirmed that this change will be coming to later iOS releases.
|
|
||||||
// Credit to JJTech for figuring out a workaround: https://gist.github.com/JJTech0130/142aee0f7bda9c61a421140d17afbdeb
|
|
||||||
Console.WriteLine($"User is using iOS 18.4db1 (22E5200s), enabling Debugger Memory Writing");
|
|
||||||
_writeZeroCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
|
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||||
Stubs = new TranslatorStubs(FunctionTable, _writeZeroCache);
|
Stubs = new TranslatorStubs(FunctionTable, _dualMappedCache);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
|
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
|
||||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
|
||||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -125,7 +112,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
|
|
||||||
NativeInterface.UnregisterThread();
|
NativeInterface.UnregisterThread();
|
||||||
_noWxCache?.ClearEntireThreadLocalCache();
|
_noWxCache?.ClearEntireThreadLocalCache();
|
||||||
_writeZeroCache?.ClearEntireThreadLocalCache();
|
_dualMappedCache?.ClearEntireThreadLocalCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IntPtr GetOrTranslatePointer(IntPtr framePointer, ulong address, ExecutionMode mode)
|
internal IntPtr GetOrTranslatePointer(IntPtr framePointer, ulong address, ExecutionMode mode)
|
||||||
@ -135,10 +122,10 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
CompiledFunction func = Compile(address, mode);
|
CompiledFunction func = Compile(address, mode);
|
||||||
return _noWxCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength);
|
return _noWxCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength);
|
||||||
}
|
}
|
||||||
else if (_writeZeroCache != null)
|
else if (_dualMappedCache != null)
|
||||||
{
|
{
|
||||||
CompiledFunction func = Compile(address, mode);
|
CompiledFunction func = Compile(address, mode);
|
||||||
return _writeZeroCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength);
|
return _dualMappedCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetOrTranslate(address, mode).FuncPointer;
|
return GetOrTranslate(address, mode).FuncPointer;
|
||||||
@ -239,9 +226,9 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
{
|
{
|
||||||
_noWxCache.Dispose();
|
_noWxCache.Dispose();
|
||||||
}
|
}
|
||||||
else if (_writeZeroCache != null)
|
else if (_dualMappedCache != null)
|
||||||
{
|
{
|
||||||
_writeZeroCache.Dispose();
|
_dualMappedCache.Dispose();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
|
|
||||||
private readonly AddressTable<ulong> _functionTable;
|
private readonly AddressTable<ulong> _functionTable;
|
||||||
private readonly NoWxCache _noWxCache;
|
private readonly NoWxCache _noWxCache;
|
||||||
private readonly WriteZeroCache _writeZeroCache;
|
private readonly DualMappedNoWxCache _dualMappedCache;
|
||||||
|
|
||||||
private readonly GetFunctionAddressDelegate _getFunctionAddressRef;
|
private readonly GetFunctionAddressDelegate _getFunctionAddressRef;
|
||||||
private readonly IntPtr _getFunctionAddress;
|
private readonly IntPtr _getFunctionAddress;
|
||||||
@ -101,12 +101,12 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||||
/// <param name="writeZeroCache">Cache used on iOS versions that need a debugger to make a debug map</param>
|
/// <param name="writeZeroCache">Cache used on iOS versions that need a debugger to make a debug map</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(AddressTable<ulong> functionTable, WriteZeroCache writeZeroCache)
|
public TranslatorStubs(AddressTable<ulong> functionTable, DualMappedNoWxCache dualMappedCache)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(functionTable);
|
ArgumentNullException.ThrowIfNull(functionTable);
|
||||||
|
|
||||||
_functionTable = functionTable;
|
_functionTable = functionTable;
|
||||||
_writeZeroCache = writeZeroCache;
|
_dualMappedCache = dualMappedCache;
|
||||||
_getFunctionAddressRef = NativeInterface.GetFunctionAddress;
|
_getFunctionAddressRef = NativeInterface.GetFunctionAddress;
|
||||||
_getFunctionAddress = Marshal.GetFunctionPointerForDelegate(_getFunctionAddressRef);
|
_getFunctionAddress = Marshal.GetFunctionPointerForDelegate(_getFunctionAddressRef);
|
||||||
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
||||||
@ -131,7 +131,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
{
|
{
|
||||||
if (!_disposed)
|
if (!_disposed)
|
||||||
{
|
{
|
||||||
if (_noWxCache == null)
|
if (_noWxCache == null || _dualMappedCache == null)
|
||||||
{
|
{
|
||||||
if (_dispatchStub.IsValueCreated)
|
if (_dispatchStub.IsValueCreated)
|
||||||
{
|
{
|
||||||
@ -383,9 +383,9 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
{
|
{
|
||||||
return _noWxCache.MapPageAligned(code);
|
return _noWxCache.MapPageAligned(code);
|
||||||
}
|
}
|
||||||
else if (_writeZeroCache != null)
|
else if (_dualMappedCache != null)
|
||||||
{
|
{
|
||||||
return _writeZeroCache.MapPageAligned(code);
|
return _dualMappedCache.MapPageAligned(code);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -127,11 +127,12 @@ namespace Ryujinx.Cpu.Signal
|
|||||||
|
|
||||||
ulong codeSizeAligned = BitUtils.AlignUp((ulong)code.Length, MemoryBlock.GetPageSize());
|
ulong codeSizeAligned = BitUtils.AlignUp((ulong)code.Length, MemoryBlock.GetPageSize());
|
||||||
|
|
||||||
_codeBlock = new MemoryBlock(codeSizeAligned);
|
string dualMapped = Environment.GetEnvironmentVariable("DUAL_MAPPED_JIT");
|
||||||
|
_codeBlock = new MemoryBlock(codeSizeAligned, (dualMapped == "1") ? MemoryAllocationFlags.DualMapping : MemoryAllocationFlags.None);
|
||||||
_codeBlock.Write(0, code);
|
_codeBlock.Write(0, code);
|
||||||
_codeBlock.Reprotect(0, codeSizeAligned, MemoryPermission.ReadAndExecute);
|
_codeBlock.Reprotect(0, codeSizeAligned, MemoryPermission.ReadAndExecute);
|
||||||
|
|
||||||
return _codeBlock.Pointer;
|
return _codeBlock.RxPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe ref SignalHandlerConfig GetConfigRef()
|
private static unsafe ref SignalHandlerConfig GetConfigRef()
|
||||||
|
@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public const int MaxShaderStages = 5;
|
public const int MaxShaderStages = 5;
|
||||||
public const int MaxUniformBuffersPerStage = 18;
|
public const int MaxUniformBuffersPerStage = 18;
|
||||||
public const int MaxStorageBuffersPerStage = 16;
|
public const int MaxStorageBuffersPerStage = 16;
|
||||||
public const int MaxTexturesPerStage = 31;
|
public const int MaxTexturesPerStage = 32; // 31
|
||||||
public const int MaxImagesPerStage = 16;
|
public const int MaxImagesPerStage = 16;
|
||||||
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
|
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
|
||||||
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
||||||
|
@ -680,6 +680,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
ShaderCollection program = _program;
|
ShaderCollection program = _program;
|
||||||
|
|
||||||
|
|
||||||
|
// UpdateAndBindTexturesWithoutTemplate jas been renamed to UpdateAndBind and supports more then just textures.
|
||||||
if (_dirty.HasFlag(DirtyFlags.Uniform))
|
if (_dirty.HasFlag(DirtyFlags.Uniform))
|
||||||
{
|
{
|
||||||
if (program.UsePushDescriptors)
|
if (program.UsePushDescriptors)
|
||||||
@ -688,51 +690,77 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
UpdateAndBind(cbs, program, PipelineBase.UniformSetIndex, pbp);
|
UpdateAndBind(cbs, program, PipelineBase.UniformSetIndex, pbp);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// If binding fails, we can try to bind the uniform buffers without using the template.
|
||||||
|
// This is a workaround for some games that use invalid bindings.
|
||||||
|
|
||||||
|
UpdateAndBind(cbs, PipelineBase.UniformSetIndex, pbp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_dirty.HasFlag(DirtyFlags.Storage))
|
if (_dirty.HasFlag(DirtyFlags.Storage))
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp);
|
UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// If binding fails, we can try to bind the storage buffers without using the template.
|
||||||
|
// This is a workaround for some games that use invalid bindings.
|
||||||
|
|
||||||
|
UpdateAndBind(cbs, PipelineBase.StorageSetIndex, pbp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_dirty.HasFlag(DirtyFlags.Texture))
|
if (_dirty.HasFlag(DirtyFlags.Texture))
|
||||||
{
|
{
|
||||||
if (true)
|
if (program.UpdateTexturesWithoutTemplate)
|
||||||
{
|
{
|
||||||
try
|
UpdateAndBind(cbs, PipelineBase.TextureSetIndex, pbp);
|
||||||
{
|
|
||||||
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
|
// If binding fails, we can try to bind the textures without using the template.
|
||||||
|
// This is a workaround for some games that use invalid bindings.
|
||||||
|
|
||||||
|
UpdateAndBind(cbs, PipelineBase.TextureSetIndex, pbp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_dirty.HasFlag(DirtyFlags.Image))
|
if (_dirty.HasFlag(DirtyFlags.Image))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp);
|
UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// If binding fails, we can try to bind the images without using the template.
|
||||||
|
// This is a workaround for some games that use invalid bindings.
|
||||||
|
|
||||||
|
UpdateAndBind(cbs, PipelineBase.ImageSetIndex, pbp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (program.BindingSegments.Length > PipelineBase.DescriptorSetLayouts)
|
if (program.BindingSegments.Length > PipelineBase.DescriptorSetLayouts)
|
||||||
{
|
{
|
||||||
// Program is using extra sets, we need to bind those too.
|
// Program is using extra sets, we need to bind those too.
|
||||||
|
|
||||||
BindExtraSets(cbs, program, pbp);
|
// Ignore Extra Sets for now.
|
||||||
|
// BindExtraSets(cbs, program, pbp);
|
||||||
}
|
}
|
||||||
|
|
||||||
_dirty = DirtyFlags.None;
|
_dirty = DirtyFlags.None;
|
||||||
@ -764,7 +792,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (info.Buffer.Handle == 0)
|
if (info.Buffer.Handle == 0)
|
||||||
{
|
{
|
||||||
info.Buffer = dummyBuffer?.Get(cbs).Value ?? default;
|
info.Buffer = dummyBuffer?.Get(cbs).Value ?? default;
|
||||||
// info.Offset = 0;
|
info.Offset = 0;
|
||||||
info.Range = Vk.WholeSize;
|
info.Range = Vk.WholeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -962,9 +990,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
|
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp)
|
||||||
{
|
{
|
||||||
int setIndex = PipelineBase.TextureSetIndex;
|
var program = _program;
|
||||||
var bindingSegments = program.BindingSegments[setIndex];
|
var bindingSegments = program.BindingSegments[setIndex];
|
||||||
|
|
||||||
if (bindingSegments.Length == 0)
|
if (bindingSegments.Length == 0)
|
||||||
@ -972,6 +1000,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||||
|
|
||||||
|
if (_updateDescriptorCacheCbIndex)
|
||||||
|
{
|
||||||
|
_updateDescriptorCacheCbIndex = false;
|
||||||
|
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs);
|
||||||
|
|
||||||
|
if (!program.HasMinimalLayout)
|
||||||
|
{
|
||||||
|
if (isNew)
|
||||||
|
{
|
||||||
|
Initialize(cbs, setIndex, dsc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var dummyImageInfo = new DescriptorImageInfo
|
var dummyImageInfo = new DescriptorImageInfo
|
||||||
{
|
{
|
||||||
ImageView = _dummyTexture.GetImageView().Get(cbs).Value,
|
ImageView = _dummyTexture.GetImageView().Get(cbs).Value,
|
||||||
@ -979,13 +1025,57 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ImageLayout = ImageLayout.General
|
ImageLayout = ImageLayout.General
|
||||||
};
|
};
|
||||||
|
|
||||||
var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
|
|
||||||
|
|
||||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||||
{
|
{
|
||||||
int binding = segment.Binding;
|
int binding = segment.Binding;
|
||||||
int count = segment.Count;
|
int count = segment.Count;
|
||||||
|
|
||||||
|
if (setIndex == PipelineBase.UniformSetIndex)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int index = binding + i;
|
||||||
|
|
||||||
|
if (_uniformSet.Set(index))
|
||||||
|
{
|
||||||
|
ref BufferRef buffer = ref _uniformBufferRefs[index];
|
||||||
|
|
||||||
|
bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true);
|
||||||
|
|
||||||
|
_uniformMirrored.Set(index, mirrored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||||
|
dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||||
|
}
|
||||||
|
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int index = binding + i;
|
||||||
|
|
||||||
|
ref BufferRef buffer = ref _storageBufferRefs[index];
|
||||||
|
|
||||||
|
if (_storageSet.Set(index))
|
||||||
|
{
|
||||||
|
ref var info = ref _storageBuffers[index];
|
||||||
|
|
||||||
|
bool mirrored = UpdateBuffer(cbs,
|
||||||
|
ref info,
|
||||||
|
ref _storageBufferRefs[index],
|
||||||
|
dummyBuffer,
|
||||||
|
!buffer.Write && info.Range <= StorageBufferMaxMirrorable);
|
||||||
|
|
||||||
|
_storageMirrored.Set(index, mirrored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||||
|
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
||||||
|
}
|
||||||
|
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||||
|
{
|
||||||
if (!segment.IsArray)
|
if (!segment.IsArray)
|
||||||
{
|
{
|
||||||
if (segment.Type != ResourceType.BufferTexture)
|
if (segment.Type != ResourceType.BufferTexture)
|
||||||
@ -1013,7 +1103,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
int index = binding + i;
|
int index = binding + i;
|
||||||
var bufferView = _bufferTextureRefs[index]?.GetBufferView(cbs, false) ?? default;
|
var bufferView = _bufferTextureRefs[index]?.GetBufferView(cbs, false) ?? default(BufferView);
|
||||||
dsc.UpdateBufferImages(0, index, new[] { bufferView }, DescriptorType.UniformTexelBuffer);
|
dsc.UpdateBufferImages(0, index, new[] { bufferView }, DescriptorType.UniformTexelBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1060,8 +1150,35 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (setIndex == PipelineBase.ImageSetIndex)
|
||||||
|
{
|
||||||
|
if (segment.Type != ResourceType.BufferImage)
|
||||||
|
{
|
||||||
|
Span<DescriptorImageInfo> images = _images;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc.UpdateImages(0, binding, images[..count], DescriptorType.StorageImage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Span<BufferView> bufferImages = _bufferImages;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc.UpdateBufferImages(0, binding, bufferImages[..count], DescriptorType.StorageTexelBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var sets = dsc.GetSets();
|
var sets = dsc.GetSets();
|
||||||
|
|
||||||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,8 +186,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return sets;
|
return sets;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs);
|
|
||||||
|
|
||||||
DescriptorSetTemplate template = program.Templates[setIndex];
|
DescriptorSetTemplate template = program.Templates[setIndex];
|
||||||
|
|
||||||
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
|
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
|
||||||
@ -203,8 +201,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
templateUpdater.Commit(_gd, device, sets[0]);
|
templateUpdater.Commit(_gd, device, sets[0]);
|
||||||
|
|
||||||
sets = dsc.GetSets();
|
|
||||||
|
|
||||||
return sets;
|
return sets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
|
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
const int MaxAttachments = Constants.MaxRenderTargets + 1;
|
int MaxAttachments = Constants.MaxRenderTargets + 1;
|
||||||
|
|
||||||
AttachmentDescription[] attachmentDescs = null;
|
AttachmentDescription[] attachmentDescs = null;
|
||||||
|
|
||||||
@ -185,8 +185,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (gd.Capabilities.SupportsMultiView)
|
if (gd.Capabilities.SupportsMultiView)
|
||||||
{
|
{
|
||||||
pipeline.ScissorsCount = Constants.MaxViewports;
|
pipeline.ScissorsCount = (uint)Constants.MaxViewports;
|
||||||
pipeline.ViewportsCount = Constants.MaxViewports;
|
pipeline.ViewportsCount = (uint)Constants.MaxViewports;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -137,7 +137,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
|
(IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
|
||||||
|
|
||||||
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
|
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
|
||||||
UpdateTexturesWithoutTemplate = OperatingSystem.IsIOS(); // gd.IsQualcommProprietary && usesBufferTextures;
|
UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
|
||||||
|
|
||||||
_compileTask = Task.CompletedTask;
|
_compileTask = Task.CompletedTask;
|
||||||
_firstBackgroundUse = false;
|
_firstBackgroundUse = false;
|
||||||
|
@ -402,9 +402,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
properties.Limits.FramebufferDepthSampleCounts &
|
properties.Limits.FramebufferDepthSampleCounts &
|
||||||
properties.Limits.FramebufferStencilSampleCounts;
|
properties.Limits.FramebufferStencilSampleCounts;
|
||||||
|
|
||||||
bool isDynamicStateSupported = OperatingSystem.IsIOS()
|
bool isDynamicStateSupported = OperatingSystem.IsIOSVersionAtLeast(17) && _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName);
|
||||||
? OperatingSystem.IsIOSVersionAtLeast(17) && _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName)
|
|
||||||
: _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName);
|
|
||||||
|
|
||||||
Capabilities = new HardwareCapabilities(
|
Capabilities = new HardwareCapabilities(
|
||||||
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
|
||||||
@ -422,8 +420,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.Features.ShaderStorageImageMultisample,
|
features2.Features.ShaderStorageImageMultisample,
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
||||||
isDynamicStateSupported,
|
isDynamicStateSupported,
|
||||||
features2.Features.MultiViewport && !IsMoltenVk, // Workaround for AMD on MoltenVK issue
|
features2.Features.MultiViewport, // && !IsMoltenVk, // Workaround for AMD on MoltenVK issue
|
||||||
!IsMoltenVk ? featuresRobustness2.NullDescriptor : false,
|
featuresRobustness2.NullDescriptor && !IsMoltenVk,
|
||||||
supportsPushDescriptors && !IsMoltenVk,
|
supportsPushDescriptors && !IsMoltenVk,
|
||||||
propertiesPushDescriptor.MaxPushDescriptors,
|
propertiesPushDescriptor.MaxPushDescriptors,
|
||||||
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
||||||
@ -722,7 +720,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
SystemMemoryType memoryType;
|
SystemMemoryType memoryType;
|
||||||
|
|
||||||
if (IsSharedMemory && !IsMoltenVk)
|
if (IsSharedMemory)
|
||||||
{
|
{
|
||||||
memoryType = SystemMemoryType.UnifiedMemory;
|
memoryType = SystemMemoryType.UnifiedMemory;
|
||||||
}
|
}
|
||||||
@ -784,10 +782,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
imageSetIndex: PipelineBase.ImageSetIndex,
|
imageSetIndex: PipelineBase.ImageSetIndex,
|
||||||
extraSetBaseIndex: PipelineBase.DescriptorSetLayouts,
|
extraSetBaseIndex: PipelineBase.DescriptorSetLayouts,
|
||||||
maximumExtraSets: Math.Max(0, (int)limits.MaxBoundDescriptorSets - PipelineBase.DescriptorSetLayouts),
|
maximumExtraSets: Math.Max(0, (int)limits.MaxBoundDescriptorSets - PipelineBase.DescriptorSetLayouts),
|
||||||
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
maximumUniformBuffersPerStage: (uint)Constants.MaxUniformBuffersPerStage,
|
||||||
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
|
maximumStorageBuffersPerStage: (uint)Constants.MaxStorageBuffersPerStage,
|
||||||
maximumTexturesPerStage: Constants.MaxTexturesPerStage,
|
maximumTexturesPerStage: (uint)Constants.MaxTexturesPerStage,
|
||||||
maximumImagesPerStage: Constants.MaxImagesPerStage,
|
maximumImagesPerStage: (uint)Constants.MaxImagesPerStage,
|
||||||
maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize,
|
maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize,
|
||||||
maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy,
|
maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy,
|
||||||
shaderSubgroupSize: (int)Capabilities.SubgroupSize,
|
shaderSubgroupSize: (int)Capabilities.SubgroupSize,
|
||||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
_generalServiceDetail = new GeneralServiceDetail
|
_generalServiceDetail = new GeneralServiceDetail
|
||||||
{
|
{
|
||||||
ClientId = GeneralServiceManager.Count,
|
ClientId = GeneralServiceManager.Count,
|
||||||
IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request?
|
IsAnyInternetRequestAccepted = false, // NOTE: Why not accept any internet request?
|
||||||
};
|
};
|
||||||
|
|
||||||
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
||||||
|
@ -992,6 +992,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
bool isNintendoStyle = true; // gamepadName.Contains("Nintendo") || gamepadName.Contains("Joycons");
|
bool isNintendoStyle = true; // gamepadName.Contains("Nintendo") || gamepadName.Contains("Joycons");
|
||||||
|
|
||||||
ControllerType currentController;
|
ControllerType currentController;
|
||||||
|
|
||||||
if (index == PlayerIndex.Handheld)
|
if (index == PlayerIndex.Handheld)
|
||||||
{
|
{
|
||||||
currentController = ControllerType.Handheld;
|
currentController = ControllerType.Handheld;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<Message Importance="normal" Text="Found XCode at $(XcodeSelect)" Condition=" '$(FindXCode)' == 'true' "/>
|
<Message Importance="normal" Text="Found XCode at $(XcodeSelect)" Condition=" '$(FindXCode)' == 'true' "/>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- <LinkerArg Include="-Wl,-ld_classic" /> -->
|
<!-- <LinkerArg Include="-Wl,-ld_classic" /> -->
|
||||||
<LinkerArg Include="-flto -Ofast" />
|
<LinkerArg Include="-flto -Ofast" />
|
||||||
<LinkerArg Include="-isysroot %22$(XCodePath)Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk%22"
|
<LinkerArg Include="-isysroot %22$(XCodePath)Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk%22"
|
||||||
Condition=" $(RuntimeIdentifier.Contains('simulator')) "/>
|
Condition=" $(RuntimeIdentifier.Contains('simulator')) "/>
|
||||||
|
53
src/Ryujinx.Memory/DualMappedJitAllocator.cs
Normal file
53
src/Ryujinx.Memory/DualMappedJitAllocator.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
|
||||||
|
namespace Ryujinx.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Placeholder class for JIT memory allocation on iOS.
|
||||||
|
/// Intended to allocate memory with both r/x and r/w permissions,
|
||||||
|
/// as a workaround for stricter W^X (Write XOR Execute) enforcement introduced in iOS 26.
|
||||||
|
///
|
||||||
|
/// Specifically targets iOS 26, where the traditional method of reprotecting
|
||||||
|
/// memory from writable to executable (RX) no longer works for JIT code.
|
||||||
|
///
|
||||||
|
/// The actual allocation logic will be implemented after the release of iOS 26
|
||||||
|
/// to reduce the risk of this workaround being patched.
|
||||||
|
/// </summary>
|
||||||
|
public class DualMappedJitAllocator : IDisposable
|
||||||
|
{
|
||||||
|
|
||||||
|
public IntPtr RwPtr { get; private set; }
|
||||||
|
public IntPtr RxPtr { get; private set; }
|
||||||
|
public ulong Size { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
private IntPtr _mmapPtr;
|
||||||
|
|
||||||
|
public DualMappedJitAllocator(ulong size)
|
||||||
|
{
|
||||||
|
var stackTrace = new StackTrace(1, false);
|
||||||
|
var callingMethod = stackTrace.GetFrame(0)?.GetMethod();
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Cpu,
|
||||||
|
$"Allocating dual-mapped JIT memory of size {size} bytes, called by {callingMethod?.DeclaringType?.FullName}.{callingMethod?.Name}");
|
||||||
|
Size = size;
|
||||||
|
AllocateDualMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void AllocateDualMapping()
|
||||||
|
{
|
||||||
|
|
||||||
|
RwPtr = IntPtr.Zero;
|
||||||
|
RxPtr = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -48,5 +48,11 @@ namespace Ryujinx.Memory
|
|||||||
/// On some platforms, this requires special flags to be passed that will allow the memory to be executable.
|
/// On some platforms, this requires special flags to be passed that will allow the memory to be executable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Jit = 1 << 5,
|
Jit = 1 << 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the memory will be used to store JIT generated code in both read and execute modes.
|
||||||
|
/// On some platforms, this is required to allow the JIT to generate code that can be executed.
|
||||||
|
/// </summary>
|
||||||
|
DualMapping = 1 << 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,21 @@ namespace Ryujinx.Memory
|
|||||||
private readonly bool _isMirror;
|
private readonly bool _isMirror;
|
||||||
private readonly bool _viewCompatible;
|
private readonly bool _viewCompatible;
|
||||||
private readonly bool _forJit;
|
private readonly bool _forJit;
|
||||||
|
private DualMappedJitAllocator _dualMappedAllocator;
|
||||||
private IntPtr _sharedMemory;
|
private IntPtr _sharedMemory;
|
||||||
private IntPtr _pointer;
|
private IntPtr _pointer;
|
||||||
|
private IntPtr _rxPointer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pointer to the memory block data.
|
/// Pointer to the memory block data (RW).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IntPtr Pointer => _pointer;
|
public IntPtr Pointer => _pointer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pointer to the RX mapping (for execution), or IntPtr.Zero if not dual-mapped.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr RxPointer => _rxPointer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Size of the memory block.
|
/// Size of the memory block.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -35,7 +42,16 @@ namespace Ryujinx.Memory
|
|||||||
/// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception>
|
/// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception>
|
||||||
public MemoryBlock(ulong size, MemoryAllocationFlags flags = MemoryAllocationFlags.None)
|
public MemoryBlock(ulong size, MemoryAllocationFlags flags = MemoryAllocationFlags.None)
|
||||||
{
|
{
|
||||||
if (flags.HasFlag(MemoryAllocationFlags.Mirrorable))
|
Size = size;
|
||||||
|
if (flags.HasFlag(MemoryAllocationFlags.DualMapping))
|
||||||
|
{
|
||||||
|
_dualMappedAllocator = new DualMappedJitAllocator(size);
|
||||||
|
_pointer = _dualMappedAllocator.RwPtr;
|
||||||
|
_rxPointer = _dualMappedAllocator.RxPtr;
|
||||||
|
_forJit = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (flags.HasFlag(MemoryAllocationFlags.Mirrorable))
|
||||||
{
|
{
|
||||||
_sharedMemory = MemoryManagement.CreateSharedMemory(size, flags.HasFlag(MemoryAllocationFlags.Reserve));
|
_sharedMemory = MemoryManagement.CreateSharedMemory(size, flags.HasFlag(MemoryAllocationFlags.Reserve));
|
||||||
|
|
||||||
@ -58,7 +74,7 @@ namespace Ryujinx.Memory
|
|||||||
_pointer = MemoryManagement.Allocate(size, _forJit);
|
_pointer = MemoryManagement.Allocate(size, _forJit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Size = size;
|
_rxPointer = _pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -164,9 +180,12 @@ namespace Ryujinx.Memory
|
|||||||
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
||||||
/// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
|
/// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
|
||||||
public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true)
|
public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true)
|
||||||
|
{
|
||||||
|
if (_rxPointer == _pointer)
|
||||||
{
|
{
|
||||||
MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, throwOnFail);
|
MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, throwOnFail);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads bytes from the memory block.
|
/// Reads bytes from the memory block.
|
||||||
@ -388,8 +407,13 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
IntPtr ptr = Interlocked.Exchange(ref _pointer, IntPtr.Zero);
|
IntPtr ptr = Interlocked.Exchange(ref _pointer, IntPtr.Zero);
|
||||||
|
|
||||||
// If pointer is null, the memory was already freed or never allocated.
|
if (_dualMappedAllocator != null)
|
||||||
if (ptr != IntPtr.Zero)
|
{
|
||||||
|
_dualMappedAllocator.Dispose();
|
||||||
|
_dualMappedAllocator = null;
|
||||||
|
_rxPointer = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
else if (ptr != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
if (_usesSharedMemory)
|
if (_usesSharedMemory)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user