diff --git a/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index 4f6f1e87b..deb3b3447 100644 --- a/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -47,13 +47,13 @@ namespace ARMeilleure.CodeGen.X86 0xc3, // ret }; - using MemoryBlock memGetXcr0 = new((ulong)asmGetXcr0.Length); + using MemoryBlock memGetXcr0 = new((ulong)asmGetXcr0.Length, MemoryAllocationFlags.DualMapping); memGetXcr0.Write(0, asmGetXcr0); memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute); - var fGetXcr0 = Marshal.GetDelegateForFunctionPointer(memGetXcr0.Pointer); + var fGetXcr0 = Marshal.GetDelegateForFunctionPointer(memGetXcr0.RxPointer); return fGetXcr0(); } diff --git a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj index b4c4648c6..1209ebdb6 100644 --- a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj +++ b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj @@ -46,13 +46,6 @@ remoteGlobalIDString = 4E80A98C2CD6F54500029585; remoteInfo = MeloNX; }; - 4EFFCD182DFB766F00F78EA6 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4E80A9852CD6F54500029585 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BD43C6212D1B248D003BBC42; - remoteInfo = com.Stossy11.MeloNX.RyujinxAg; - }; BD43C6252D1B249E003BBC42 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 4E80A9852CD6F54500029585 /* Project object */; @@ -109,6 +102,10 @@ CA0AE31D2D3EECBC00F6D350 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; attributesByRelativePath = { + "Dependencies/Dynamic Libraries/BreakpointJIT.framework" = ( + CodeSignOnCopy, + RemoveHeadersOnCopy, + ); "Dependencies/Dynamic Libraries/Hypervisor.framework" = ( CodeSignOnCopy, RemoveHeadersOnCopy, @@ -120,10 +117,6 @@ CodeSignOnCopy, RemoveHeadersOnCopy, ); - "Dependencies/Dynamic Libraries/StosJIT.framework" = ( - CodeSignOnCopy, - RemoveHeadersOnCopy, - ); "Dependencies/Dynamic Libraries/libMoltenVK.dylib" = ( CodeSignOnCopy, ); @@ -172,13 +165,13 @@ }; buildPhase = 4E80AA092CD6FAA800029585 /* Embed Libraries */; membershipExceptions = ( + "Dependencies/Dynamic Libraries/BreakpointJIT.framework", "Dependencies/Dynamic Libraries/Hypervisor.framework", "Dependencies/Dynamic Libraries/libavcodec.dylib", "Dependencies/Dynamic Libraries/libavutil.dylib", "Dependencies/Dynamic Libraries/libMoltenVK.dylib", "Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib", "Dependencies/Dynamic Libraries/RyujinxHelper.framework", - "Dependencies/Dynamic Libraries/StosJIT.framework", Dependencies/XCFrameworks/libavcodec.xcframework, Dependencies/XCFrameworks/libavfilter.xcframework, Dependencies/XCFrameworks/libavformat.xcframework, @@ -294,7 +287,6 @@ buildRules = ( ); dependencies = ( - 4EFFCD192DFB766F00F78EA6 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 4E80A98F2CD6F54500029585 /* MeloNX */, @@ -492,11 +484,6 @@ target = 4E80A98C2CD6F54500029585 /* MeloNX */; targetProxy = 4E80A9A82CD6F54700029585 /* PBXContainerItemProxy */; }; - 4EFFCD192DFB766F00F78EA6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BD43C6212D1B248D003BBC42 /* com.Stossy11.MeloNX.RyujinxAg */; - targetProxy = 4EFFCD182DFB766F00F78EA6 /* PBXContainerItemProxy */; - }; BD43C6262D1B249E003BBC42 /* PBXTargetDependency */ = { isa = PBXTargetDependency; 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", ); GCC_OPTIMIZATION_LEVEL = z; 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", ); MARKETING_VERSION = "$(VERSION)"; 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", ); GCC_OPTIMIZATION_LEVEL = z; 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", ); MARKETING_VERSION = "$(VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; diff --git a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index b4c024fa9..407e351f1 100644 Binary files a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme b/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme index f79f4ed54..71ecb33d0 100644 --- a/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme +++ b/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme @@ -65,6 +65,7 @@ debugDocumentVersioning = "YES" debugXPCServices = "NO" debugServiceExtension = "internal" + enableGPUFrameCaptureMode = "3" enableGPUValidationMode = "1" allowLocationSimulation = "YES" queueDebuggingEnabled = "No" diff --git a/src/MeloNX/MeloNX/App/Core/Headers/MobileGestalt.h b/src/MeloNX/MeloNX/App/Core/Headers/MobileGestalt.h new file mode 100644 index 000000000..0cca6d041 --- /dev/null +++ b/src/MeloNX/MeloNX/App/Core/Headers/MobileGestalt.h @@ -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 +#include + +#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_ */ diff --git a/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h b/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h index 1a1b194f1..44dfe6c44 100644 --- a/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h +++ b/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h @@ -14,8 +14,7 @@ #include #include -#include - +#include "MobileGestalt.h" #ifdef __cplusplus extern "C" { diff --git a/src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift b/src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift index fd5b7c68e..b9eda10e6 100644 --- a/src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift +++ b/src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift @@ -17,7 +17,11 @@ func isJITEnabled() -> Bool { 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 { @@ -70,3 +74,26 @@ func allocateTest() -> Bool { 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 } }() + } +} + diff --git a/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift b/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift index 2cde8343d..cfff8b992 100644 --- a/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift +++ b/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift @@ -67,8 +67,15 @@ func checkifappinstalled(_ id: String) -> Bool { } func enableJITStik() { - let urlScheme = "stikjit://enable-jit?bundle-id=\(Bundle.main.bundleIdentifier ?? "wow")" - if let launchURL = URL(string: urlScheme), !isJITEnabled() { - UIApplication.shared.open(launchURL, options: [:], completionHandler: nil) + if #available(iOS 19, *), ProcessInfo.processInfo.hasTXM { + let urlScheme = "stikjit://script?data=ZnVuY3Rpb24gbGl0dGxlRW5kaWFuSGV4U3RyaW5nVG9OdW1iZXIoaGV4U3RyKSB7CiAgICBjb25zdCBieXRlcyA9IFtdOwogICAgZm9yIChsZXQgaSA9IDA7IGkgPCBoZXhTdHIubGVuZ3RoOyBpICs9IDIpIHsKICAgICAgICBieXRlcy5wdXNoKHBhcnNlSW50KGhleFN0ci5zdWJzdHIoaSwgMiksIDE2KSk7CiAgICB9CiAgICBsZXQgbnVtID0gMG47CiAgICBmb3IgKGxldCBpID0gNDsgaSA-PSAwOyBpLS0pIHsKICAgICAgICBudW0gPSAobnVtIDw8IDhuKSB8IEJpZ0ludChieXRlc1tpXSk7CiAgICB9CiAgICByZXR1cm4gbnVtOwp9CgpmdW5jdGlvbiBudW1iZXJUb0xpdHRsZUVuZGlhbkhleFN0cmluZyhudW0pIHsKICAgIGNvbnN0IGJ5dGVzID0gW107CiAgICBmb3IgKGxldCBpID0gMDsgaSA8IDU7IGkrKykgewogICAgICAgIGJ5dGVzLnB1c2goTnVtYmVyKG51bSAmIDB4RkZuKSk7CiAgICAgICAgbnVtID4-PSA4bjsKICAgIH0KICAgIHdoaWxlIChieXRlcy5sZW5ndGggPCA4KSB7CiAgICAgICAgYnl0ZXMucHVzaCgwKTsKICAgIH0KICAgIHJldHVybiBieXRlcy5tYXAoYiA9PiBiLnRvU3RyaW5nKDE2KS5wYWRTdGFydCgyLCAnMCcpKS5qb2luKCcnKTsKfQoKZnVuY3Rpb24gbGl0dGxlRW5kaWFuSGV4VG9VMzIoaGV4U3RyKSB7CiAgICByZXR1cm4gcGFyc2VJbnQoaGV4U3RyLm1hdGNoKC8uLi9nKS5yZXZlcnNlKCkuam9pbignJyksIDE2KTsKfQoKZnVuY3Rpb24gZXh0cmFjdEJya0ltbWVkaWF0ZSh1MzIpIHsKICAgIHJldHVybiAodTMyID4-IDUpICYgMHhGRkZGOwp9CgpmdW5jdGlvbiBhdHRhY2goYnJlYWtwb2ludGNvdW50KSB7CiAgICBsZXQgcGlkID0gZ2V0X3BpZCgpOwogICAgbG9nKGBwaWQgPSAke3BpZH1gKTsKICAgIGxldCBhdHRhY2hSZXNwb25zZSA9IHNlbmRfY29tbWFuZChgdkF0dGFjaDske3BpZC50b1N0cmluZygxNil9YCk7CiAgICBsb2coYGF0dGFjaF9yZXNwb25zZSA9ICR7YXR0YWNoUmVzcG9uc2V9YCk7CiAgICAKICAgIGxldCB2YWxpZEJyZWFrcG9pbnRzID0gMDsKICAgIGxldCB0b3RhbEJyZWFrcG9pbnRzID0gMDsKCiAgICB3aGlsZSAodmFsaWRCcmVha3BvaW50cyA8IGJyZWFrcG9pbnRjb3VudCkgewogICAgICAgIHRvdGFsQnJlYWtwb2ludHMrKzsKICAgICAgICBsb2coYEhhbmRsaW5nIGJyZWFrcG9pbnQgJHt0b3RhbEJyZWFrcG9pbnRzfSAobG9va2luZyBmb3IgdmFsaWQgYnJlYWtwb2ludCAke3ZhbGlkQnJlYWtwb2ludHMgKyAxfS8ke2JyZWFrcG9pbnRjb3VudH0pYCk7CiAgICAgICAgCiAgICAgICAgbGV0IGJya1Jlc3BvbnNlID0gc2VuZF9jb21tYW5kKGBjYCk7CiAgICAgICAgbG9nKGBicmtSZXNwb25zZSA9ICR7YnJrUmVzcG9uc2V9YCk7CiAgICAgICAgCiAgICAgICAgbGV0IHRpZE1hdGNoID0gL1RbMC05YS1mXSt0aHJlYWQ6KD88dGlkPlswLTlhLWZdKyk7Ly5leGVjKGJya1Jlc3BvbnNlKTsKICAgICAgICBsZXQgdGlkID0gdGlkTWF0Y2ggPyB0aWRNYXRjaC5ncm91cHNbJ3RpZCddIDogbnVsbDsKICAgICAgICBsZXQgcGNNYXRjaCA9IC8yMDooPzxyZWc-WzAtOWEtZl17MTZ9KTsvLmV4ZWMoYnJrUmVzcG9uc2UpOwogICAgICAgIGxldCBwYyA9IHBjTWF0Y2ggPyBwY01hdGNoLmdyb3Vwc1sncmVnJ10gOiBudWxsOwogICAgICAgIGxldCB4ME1hdGNoID0gLzAwOig_PHJlZz5bMC05YS1mXXsxNn0pOy8uZXhlYyhicmtSZXNwb25zZSk7CiAgICAgICAgbGV0IHgwID0geDBNYXRjaCA_IHgwTWF0Y2guZ3JvdXBzWydyZWcnXSA6IG51bGw7CiAgICAgICAgCiAgICAgICAgaWYgKCF0aWQgfHwgIXBjIHx8ICF4MCkgewogICAgICAgICAgICBsb2coYEZhaWxlZCB0byBleHRyYWN0IHJlZ2lzdGVyczogdGlkPSR7dGlkfSwgcGM9JHtwY30sIHgwPSR7eDB9YCk7CiAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KICAgICAgICAKICAgICAgICBjb25zdCBwY051bSA9IGxpdHRsZUVuZGlhbkhleFN0cmluZ1RvTnVtYmVyKHBjKTsKICAgICAgICBjb25zdCB4ME51bSA9IGxpdHRsZUVuZGlhbkhleFN0cmluZ1RvTnVtYmVyKHgwKTsKICAgICAgICBsb2coYHRpZCA9ICR7dGlkfSwgcGMgPSAke3BjTnVtLnRvU3RyaW5nKDE2KX0sIHgwID0gJHt4ME51bS50b1N0cmluZygxNil9YCk7CiAgICAgICAgCiAgICAgICAgbGV0IGluc3RydWN0aW9uUmVzcG9uc2UgPSBzZW5kX2NvbW1hbmQoYG0ke3BjTnVtLnRvU3RyaW5nKDE2KX0sNGApOwogICAgICAgIGxvZyhgaW5zdHJ1Y3Rpb24gYXQgcGM6ICR7aW5zdHJ1Y3Rpb25SZXNwb25zZX1gKTsKICAgICAgICBsZXQgaW5zdHJVMzIgPSBsaXR0bGVFbmRpYW5IZXhUb1UzMihpbnN0cnVjdGlvblJlc3BvbnNlKTsKICAgICAgICBsZXQgYnJrSW1tZWRpYXRlID0gZXh0cmFjdEJya0ltbWVkaWF0ZShpbnN0clUzMik7CiAgICAgICAgbG9nKGBCUksgaW1tZWRpYXRlOiAweCR7YnJrSW1tZWRpYXRlLnRvU3RyaW5nKDE2KX0gKCR7YnJrSW1tZWRpYXRlfSlgKTsKICAgICAgICAKICAgICAgICBpZiAoYnJrSW1tZWRpYXRlICE9PSAweDY5KSB7CiAgICAgICAgICAgIGxvZyhgU2tpcHBpbmcgYnJlYWtwb2ludDogYnJrIGltbWVkaWF0ZSB3YXMgbm90IDB4NjkgKHdhcyAweCR7YnJrSW1tZWRpYXRlLnRvU3RyaW5nKDE2KX0pYCk7CiAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KICAgICAgICAKICAgICAgICBsb2coYEJSSyBpbW1lZGlhdGUgbWF0Y2hlcyBleHBlY3RlZCB2YWx1ZSAweDY5IC0gcHJvY2Vzc2luZyB2YWxpZCBicmVha3BvaW50ICR7dmFsaWRCcmVha3BvaW50cyArIDF9LyR7YnJlYWtwb2ludGNvdW50fWApOwogICAgICAgIAogICAgICAgIGxldCByZXF1ZXN0UlhSZXNwb25zZSA9IHNlbmRfY29tbWFuZChgX00ke3gwTnVtLnRvU3RyaW5nKDE2KX0scnhgKTsKICAgICAgICBsb2coYHJlcXVlc3RSWFJlc3BvbnNlID0gJHtyZXF1ZXN0UlhSZXNwb25zZX1gKTsKICAgICAgICAKICAgICAgICBpZiAoIXJlcXVlc3RSWFJlc3BvbnNlIHx8IHJlcXVlc3RSWFJlc3BvbnNlLmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICBsb2coYEZhaWxlZCB0byBhbGxvY2F0ZSBSWCBtZW1vcnlgKTsKICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGxldCBqaXRQYWdlQWRkcmVzcyA9IEJpZ0ludChgMHgke3JlcXVlc3RSWFJlc3BvbnNlfWApOwogICAgICAgIGxvZyhgQWxsb2NhdGVkIEpJVCBwYWdlIGF0IGFkZHJlc3M6IDB4JHtqaXRQYWdlQWRkcmVzcy50b1N0cmluZygxNil9YCk7CiAgICAgICAgCiAgICAgICAgbGV0IHByZXBhcmVKSVRQYWdlUmVzcG9uc2UgPSBwcmVwYXJlX21lbW9yeV9yZWdpb24oaml0UGFnZUFkZHJlc3MsIHgwTnVtKTsKICAgICAgICBsb2coYHByZXBhcmVKSVRQYWdlUmVzcG9uc2UgPSAke3ByZXBhcmVKSVRQYWdlUmVzcG9uc2V9YCk7CiAgICAgICAgCiAgICAgICAgbGV0IHB1dFgwUmVzcG9uc2UgPSBzZW5kX2NvbW1hbmQoYFAwPSR7bnVtYmVyVG9MaXR0bGVFbmRpYW5IZXhTdHJpbmcoaml0UGFnZUFkZHJlc3MpfTt0aHJlYWQ6JHt0aWR9O2ApOwogICAgICAgIGxvZyhgcHV0WDBSZXNwb25zZSA9ICR7cHV0WDBSZXNwb25zZX1gKTsKICAgICAgICAKICAgICAgICBsZXQgcGNQbHVzNCA9IG51bWJlclRvTGl0dGxlRW5kaWFuSGV4U3RyaW5nKHBjTnVtICsgNG4pOwogICAgICAgIGxldCBwY1BsdXM0UmVzcG9uc2UgPSBzZW5kX2NvbW1hbmQoYFAyMD0ke3BjUGx1czR9O3RocmVhZDoke3RpZH07YCk7CiAgICAgICAgbG9nKGBwY1BsdXM0UmVzcG9uc2UgPSAke3BjUGx1czRSZXNwb25zZX1gKTsKICAgICAgICAKICAgICAgICB2YWxpZEJyZWFrcG9pbnRzKys7CiAgICAgICAgbG9nKGBDb21wbGV0ZWQgdmFsaWQgYnJlYWtwb2ludCAke3ZhbGlkQnJlYWtwb2ludHN9LyR7YnJlYWtwb2ludGNvdW50fSAtIHJldHVybmluZyBhZGRyZXNzIDB4JHtqaXRQYWdlQWRkcmVzcy50b1N0cmluZygxNil9YCk7CiAgICB9CiAgICAKICAgIGxldCBkZXRhY2hSZXNwb25zZSA9IHNlbmRfY29tbWFuZChgRGApOwogICAgbG9nKGBkZXRhY2hSZXNwb25zZSA9ICR7ZGV0YWNoUmVzcG9uc2V9YCk7Cn0KCmF0dGFjaCgzKTsgLy8gTWVsb05YIHVzZXMgMyBicmVha3BvaW50cywgYWRqdXN0IGFzIG5lZWRlZC4&bundle-id=\(Bundle.main.bundleIdentifier ?? "wow")&force-pip=1" + if let launchURL = URL(string: urlScheme), !isJITEnabled() { + UIApplication.shared.open(launchURL, options: [:], completionHandler: nil) + } + } else { + let urlScheme = "stikjit://enable-jit?bundle-id=\(Bundle.main.bundleIdentifier ?? "wow")" + if let launchURL = URL(string: urlScheme), !isJITEnabled() { + UIApplication.shared.open(launchURL, options: [:], completionHandler: nil) + } } } diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/ControllerInfo.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/ControllerInfo.swift new file mode 100644 index 000000000..5ed988004 --- /dev/null +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/ControllerInfo.swift @@ -0,0 +1,11 @@ +// +// ControllerInfo.swift +// MeloNX +// +// Created by Stossy11 on 28/06/2025. +// + +struct Controller: Identifiable, Hashable { + var id: String + var name: String +} diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/NativeController.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/NativeController.swift index d0cdc47e3..6e2d743d7 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/NativeController.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/NativeController.swift @@ -12,7 +12,8 @@ class NativeController: Hashable, BaseController { private var instanceID: SDL_JoystickID = -1 private var controller: OpaquePointer? private var nativeController: GCController - private var controllerMotionProvider: DSUMotionProvider? + private var controllerMotionProvider: ControllerMotionProvider? + private var deviceMotionProvider: DeviceMotionProvider? private let controllerHaptics: CHHapticEngine? private let rumbleController: RumbleController? @@ -24,14 +25,14 @@ class NativeController: Hashable, BaseController { var ncontrollerHaptics = nativeController.haptics?.createEngine(withLocality: .default) let vendorName = nativeController.vendorName ?? "Unknown" - var usesdeviceHaptics = (ncontrollerHaptics == nil || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one") - controllerHaptics = usesdeviceHaptics ? ncontrollerHaptics : try? CHHapticEngine() + var usesdeviceHaptics = (ncontrollerHaptics == nil && (vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")) + controllerHaptics = usesdeviceHaptics ? try? CHHapticEngine() : ncontrollerHaptics // Make sure the haptic engine exists before attempting to start it or initialize the controller. if let hapticsEngine = controllerHaptics { do { try hapticsEngine.start() - rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: 1.2) + rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: usesdeviceHaptics ? 2.0 : 2.5) } catch { rumbleController = nil } @@ -51,14 +52,22 @@ class NativeController: Hashable, BaseController { let vendorName = nativeController.vendorName ?? "Unknown" 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 { 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() { if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 { diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/VirtualController.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/VirtualController.swift index 9ffbcf85d..695f82c10 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/VirtualController.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/VirtualController.swift @@ -158,7 +158,7 @@ class VirtualController : BaseController { } } -enum VirtualControllerButton: Int { +enum VirtualControllerButton: Int, Codable { case A case B case X @@ -196,7 +196,7 @@ enum VirtualControllerButton: Int { } } -enum ThumbstickType: Int { +enum ThumbstickType: Int, Codable { case left case right } diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift index 3c09d1905..eda70b2ec 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift @@ -99,10 +99,7 @@ extension Notification.Name { static let newLogCaptured = Notification.Name("newLogCaptured") } -struct Controller: Identifiable, Hashable { - var id: String - var name: String -} + struct iOSNav: View { @ViewBuilder var content: () -> Content @@ -136,6 +133,7 @@ class Ryujinx : ObservableObject { @Published var emulationUIView: MeloMTKView? = nil @Published var config: Ryujinx.Arguments? = nil @Published var games: [Game] = [] + @Published var aspectRatio: AspectRatio = .fixed16x9 @Published var defMLContentSize: CGFloat? @@ -240,7 +238,7 @@ class Ryujinx : ObservableObject { disablevsync: Bool = false, language: SystemLanguage = .americanEnglish, regioncode: SystemRegionCode = .usa, - handHeldController: Bool = false, + handHeldController: Bool = false ) { self.gamepath = gamepath 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 { let exceptionType: String let message: String @@ -563,7 +597,11 @@ class Ryujinx : ObservableObject { 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]) @@ -835,116 +873,8 @@ public extension UIDevice { return identifier + String(UnicodeScalar(UInt8(value))) } - func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity - #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) + return CallMGCopyAnswer(kMGPhysicalHardwareNameString)?.takeUnretainedValue() as? String ?? identifier }() } + diff --git a/src/MeloNX/MeloNX/App/Models/ToggleButtonsState.swift b/src/MeloNX/MeloNX/App/Models/ToggleButtonsState.swift deleted file mode 100644 index 08e6d9310..000000000 --- a/src/MeloNX/MeloNX/App/Models/ToggleButtonsState.swift +++ /dev/null @@ -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) -} diff --git a/src/MeloNX/MeloNX/App/Views/Main/Elements/Alerts.swift b/src/MeloNX/MeloNX/App/Views/Main/Elements/Alerts.swift new file mode 100644 index 000000000..c505e919d --- /dev/null +++ b/src/MeloNX/MeloNX/App/Views/Main/Elements/Alerts.swift @@ -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) +} diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/ControllerView.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/ControllerView.swift index aaf5f758a..28b60472e 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/ControllerView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/ControllerView.swift @@ -9,76 +9,447 @@ import SwiftUI import GameController import CoreMotion +// MARK: - Model & Layout Manager + +struct ButtonLayout: Codable { + var offset: CGSize = .zero + var scale: CGFloat = 1.0 + var hidden: Bool = false + var toggle: Bool = false +} + +struct JoystickLayout: Codable { + var offset: CGSize = .zero + var scale: CGFloat = 1.0 + var hide: Bool = true + var background: Bool = false + var hidden: Bool = false +} + +struct LayoutConfig: Codable { + var buttons: [String: ButtonLayout] = [:] + var joysticks: [String: JoystickLayout] = [:] +} + +class LayoutManager { + static let shared = LayoutManager() + private init() {} + + private var baseURL: URL { + let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + return docDir.appendingPathComponent("controller_layouts") + } + + private func fileURL(for gameId: String?) -> URL { + let fileName = gameId?.isEmpty == false ? "\(gameId!).json" : "default.json" + return baseURL.appendingPathComponent(fileName) + } + + private func ensureDirectoryExists() { + try? FileManager.default.createDirectory(at: baseURL, withIntermediateDirectories: true) + } + + func save(_ layout: LayoutConfig, for gameId: String? = nil) { + ensureDirectoryExists() + try? JSONEncoder().encode(layout).write(to: fileURL(for: gameId)) + } + + func load(for gameId: String? = nil) -> LayoutConfig { + let url = fileURL(for: gameId) + guard let data = try? Data(contentsOf: url), + let config = try? JSONDecoder().decode(LayoutConfig.self, from: data) else { + // If no game-specific layout exists, try to load default + if gameId != nil { + return load(for: nil) + } + return LayoutConfig() + } + return config + } + + // Legacy support for old layout format + func loadLegacy(for gameId: String? = nil) -> [String: ButtonLayout] { + let url = fileURL(for: gameId) + guard let data = try? Data(contentsOf: url), + let legacyConfig = try? JSONDecoder().decode([String: ButtonLayout].self, from: data) else { + return [:] + } + return legacyConfig + } + + func reset(for gameId: String? = nil) { + let url = fileURL(for: gameId) + try? FileManager.default.removeItem(at: url) + } + + func resetAll() { + try? FileManager.default.removeItem(at: baseURL) + } + + func hasCustomLayout(for gameId: String) -> Bool { + let url = fileURL(for: gameId) + return FileManager.default.fileExists(atPath: url.path) + } + + func copyLayout(from sourceGameId: String?, to targetGameId: String?) { + let sourceLayout = load(for: sourceGameId) + save(sourceLayout, for: targetGameId) + } + + func getAllGameLayouts() -> [String] { + ensureDirectoryExists() + guard let files = try? FileManager.default.contentsOfDirectory(at: baseURL, includingPropertiesForKeys: nil) else { + return [] + } + return files.compactMap { url in + let filename = url.lastPathComponent + guard filename.hasSuffix(".json") && filename != "default.json" else { return nil } + return String(filename.dropLast(5)) // Remove .json extension + } + } +} + +extension VirtualControllerButton: Identifiable { + public var id: String { "\(self)" } +} + +// MARK: - Main Controller View + struct ControllerView: View { - // MARK: - Properties @AppStorage("On-ScreenControllerScale") private var controllerScale: Double = 1.0 @AppStorage("stick-button") private var stickButton = false @State private var isPortrait = true - @State var hideDpad = false - @State var hideABXY = false + @State private var hideDpad = false + @State private var hideABXY = false + @Binding var isEditing: Bool + @State private var selectedButton: String? + @State private var selectedJoystick: String? @Environment(\.verticalSizeClass) var verticalSizeClass - + @State private var showEditControls = true + + // Game-specific layout support + var gameId: String? + @State private var layout: LayoutConfig = LayoutConfig() + @State private var showingLayoutOptions = true - // MARK: - Body var body: some View { - Group { - let isPad = UIDevice.current.userInterfaceIdiom == .pad - - if isPortrait && !isPad { - portraitLayout - } else { - landscapeLayout + ZStack { + Group { + let isPad = UIDevice.current.userInterfaceIdiom == .pad + if isPortrait && !isPad { + portraitLayout + } else { + landscapeLayout + } + } + .padding() + .onChange(of: verticalSizeClass) { _ in updateOrientation() } + .onAppear { + updateOrientation() + loadLayout() + } + .onChange(of: gameId) { _ in + loadLayout() + } + + // Edit Controls + if isEditing { + + + if showEditControls { + editControls + .transition(.move(edge: .top).combined(with: .opacity)) + .zIndex(1) + } else { + VStack { + HStack { + Button(action: { + withAnimation(.easeInOut(duration: 0.3)) { + showEditControls = true + } + }) { + Image(systemName: showEditControls ? "eye.slash" : "eye") + .padding(12) + .background(.ultraThinMaterial) + .clipShape(Circle()) + } + + Spacer() + } + + Spacer() + } + } + } } - .padding() - .onChange(of: verticalSizeClass) { _ in - updateOrientation() - } - .onAppear(perform: updateOrientation) } - // MARK: - Layouts + private func loadLayout() { + layout = LayoutManager.shared.load(for: gameId) + + // Migration: Convert legacy layout format + let legacyLayout = LayoutManager.shared.loadLegacy(for: gameId) + if !legacyLayout.isEmpty && layout.buttons.isEmpty { + layout.buttons = legacyLayout + LayoutManager.shared.save(layout, for: gameId) + } + } + + private func saveLayout() { + LayoutManager.shared.save(layout, for: gameId) + } + + // MARK: - Edit Controls + + private var editControls: some View { + VStack { + HStack { + Button("Hide") { + withAnimation(.easeInOut(duration: 0.3)) { + showEditControls = false + } + } + .foregroundColor(.red) + + Button("Layout Options") { + showingLayoutOptions = true + } + .foregroundColor(.blue) + + Spacer() + + Button("Reset Current") { + layout = LayoutConfig() + LayoutManager.shared.reset(for: gameId) + selectedButton = nil + selectedJoystick = nil + } + .foregroundColor(.red) + + Spacer() + + if let selectedButton = selectedButton { + Button("Reset Selected") { + layout.buttons[selectedButton] = nil + self.selectedButton = nil + } + .foregroundColor(.orange) + .padding(.horizontal) + } else if let selectedJoystick = selectedJoystick { + Button("Reset Selected") { + layout.joysticks[selectedJoystick] = nil + self.selectedJoystick = nil + } + .foregroundColor(.orange) + .padding(.horizontal) + } + + Button(isEditing ? "Done" : "Edit") { + if isEditing { + saveLayout() + selectedButton = nil + selectedJoystick = nil + } + isEditing.toggle() + } + .padding(.horizontal) + } + .padding() + .background(.ultraThinMaterial) + .cornerRadius(12) + + // Game indicator + if let gameId = gameId { + HStack { + Image(systemName: "gamecontroller.fill") + .foregroundColor(.blue) + .padding(.vertical) + Text("Game: \(gameId)") + .font(.caption) + .foregroundColor(.secondary) + .padding(.vertical) + if LayoutManager.shared.hasCustomLayout(for: gameId) { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + .font(.caption) + } + Spacer() + } + .padding(.horizontal) + .padding(.top, 4) + .background(.ultraThinMaterial) + .cornerRadius(8) + } + + if let selectedButton = selectedButton { + buttonScaleControls(for: selectedButton) + } else if let selectedJoystick = selectedJoystick { + joystickScaleControls(for: selectedJoystick) + } + + Spacer() + } + .padding() + .sheet(isPresented: $showingLayoutOptions) { + LayoutOptionsView(gameId: gameId, layout: $layout) + } + } + + private func buttonScaleControls(for buttonId: String) -> some View { + VStack { + Text("Button Scale: \(String(format: "%.1f", layout.buttons[buttonId]?.scale ?? 1.0))") + .font(.headline) + + HStack { + Button("-") { + let currentScale = layout.buttons[buttonId]?.scale ?? 1.0 + layout.buttons[buttonId, default: ButtonLayout()].scale = max(0.5, currentScale - 0.1) + } + .font(.title2) + .frame(width: 40, height: 40) + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(8) + + Slider( + value: Binding( + get: { layout.buttons[buttonId]?.scale ?? 1.0 }, + set: { layout.buttons[buttonId, default: ButtonLayout()].scale = $0 } + ), + in: 0.5...2.0, + step: 0.1 + ) + + Button("+") { + let currentScale = layout.buttons[buttonId]?.scale ?? 1.0 + layout.buttons[buttonId, default: ButtonLayout()].scale = min(2.0, currentScale + 0.1) + } + .font(.title2) + .frame(width: 40, height: 40) + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(8) + } + + Toggle(isOn: Binding(get: { layout.buttons[buttonId]?.hidden ?? false }, set: { layout.buttons[buttonId, default: ButtonLayout()].hidden = $0 })) { + Text("Hide Button") + } + .accentColor(.blue) + + Toggle(isOn: Binding(get: { layout.buttons[buttonId]?.toggle ?? false }, set: { layout.buttons[buttonId, default: ButtonLayout()].toggle = $0 })) { + Text("Make Button Toggle") + } + .accentColor(.blue) + } + .padding() + .background(.ultraThinMaterial) + .cornerRadius(12) + } + + private func joystickScaleControls(for joystickId: String) -> some View { + VStack { + Text("Joystick Scale: \(String(format: "%.1f", layout.joysticks[joystickId]?.scale ?? 1.0))") + .font(.headline) + + HStack { + Button("-") { + let currentScale = layout.joysticks[joystickId]?.scale ?? 1.0 + layout.joysticks[joystickId, default: JoystickLayout()].scale = max(0.5, currentScale - 0.1) + } + .font(.title2) + .frame(width: 40, height: 40) + .background(Color.green) + .foregroundColor(.white) + .cornerRadius(8) + + Slider( + value: Binding( + get: { layout.joysticks[joystickId]?.scale ?? 1.0 }, + set: { layout.joysticks[joystickId, default: JoystickLayout()].scale = $0 } + ), + in: 0.5...2.0, + step: 0.1 + ) + + Button("+") { + let currentScale = layout.joysticks[joystickId]?.scale ?? 1.0 + layout.joysticks[joystickId, default: JoystickLayout()].scale = min(2.0, currentScale + 0.1) + } + .font(.title2) + .frame(width: 40, height: 40) + .background(Color.green) + .foregroundColor(.white) + .cornerRadius(8) + } + + Toggle(isOn: Binding(get: { layout.joysticks[joystickId]?.hidden ?? false }, set: { layout.joysticks[joystickId, default: JoystickLayout()].hide = $0 })) { + Text("Hide Joystick") + } + .accentColor(.green) + + Toggle(isOn: Binding(get: { layout.joysticks[joystickId]?.hide ?? true }, set: { layout.joysticks[joystickId, default: JoystickLayout()].hide = $0 })) { + Text("Hide ABXY / Arrow Buttons") + } + .accentColor(.green) + + Toggle(isOn: Binding(get: { layout.joysticks[joystickId]?.background ?? false }, set: { layout.joysticks[joystickId, default: JoystickLayout()].background = $0 })) { + Text("Always show Joystick Background") + } + .accentColor(.green) + } + .padding() + .background(.ultraThinMaterial) + .cornerRadius(12) + } + + // MARK: - Layout Views + private var portraitLayout: some View { VStack { Spacer() VStack(spacing: 20) { HStack(spacing: 30) { VStack(spacing: 15) { - ShoulderButtonsViewLeft() - .padding(.vertical) + shoulderButtonsLeft ZStack { - JoystickController(showBackground: $hideDpad) - DPadView() - .opacity(hideDpad ? 0 : 1) - .allowsHitTesting(!hideDpad) - .animation(.easeInOut(duration: 0.2), value: hideDpad) + editableJoystick(id: "leftJoystick", showBackground: $hideDpad) + + if layout.joysticks["leftJoystick"]?.hide ?? true { + dpadView + .opacity(hideDpad ? 0 : 1) + .allowsHitTesting(!hideDpad) + .animation(.easeInOut(duration: 0.2), value: hideDpad) + } else { + dpadView + } } } VStack(spacing: 15) { - ShoulderButtonsViewRight() - .padding(.vertical) + shoulderButtonsRight ZStack { - JoystickController(iscool: true, showBackground: $hideABXY) - ABXYView() - .opacity(hideABXY ? 0 : 1) - .allowsHitTesting(!hideABXY) - .animation(.easeInOut(duration: 0.2), value: hideABXY) + editableJoystick(id: "rightJoystick", iscool: true, showBackground: $hideABXY) + if layout.joysticks["rightJoystick"]?.hide ?? true { + abxyView + .opacity(hideABXY ? 0 : 1) + .allowsHitTesting(!hideABXY) + .animation(.easeInOut(duration: 0.2), value: hideABXY) + } else { + abxyView + } } } } HStack(spacing: 60) { HStack { - ButtonView(button: .leftStick) - .padding() - ButtonView(button: .back) + editableButton(.leftStick).padding() + editableButton(.back) } - HStack { - ButtonView(button: .start) - ButtonView(button: .rightStick) - .padding() + editableButton(.start) + editableButton(.rightStick).padding() } } } @@ -88,35 +459,39 @@ struct ControllerView: View { private var landscapeLayout: some View { VStack { Spacer() - HStack { VStack(spacing: 20) { - ShoulderButtonsViewLeft() - .padding(.vertical) + shoulderButtonsLeft ZStack { - JoystickController(showBackground: $hideDpad) - DPadView() - .opacity(hideDpad ? 0 : 1) - .allowsHitTesting(!hideDpad) - .animation(.easeInOut(duration: 0.2), value: hideDpad) + editableJoystick(id: "leftJoystick", showBackground: $hideDpad) + + if layout.joysticks["leftJoystick"]?.hide ?? true { + dpadView + .opacity(hideDpad ? 0 : 1) + .allowsHitTesting(!hideDpad) + .animation(.easeInOut(duration: 0.2), value: hideDpad) + } else { + dpadView + } } } Spacer() - centerButtons - Spacer() VStack(spacing: 20) { - ShoulderButtonsViewRight() - .padding(.vertical) + shoulderButtonsRight ZStack { - JoystickController(iscool: true, showBackground: $hideABXY) - ABXYView() - .opacity(hideABXY ? 0 : 1) - .allowsHitTesting(!hideABXY) - .animation(.easeInOut(duration: 0.2), value: hideABXY) + editableJoystick(id: "rightJoystick", iscool: true, showBackground: $hideABXY) + if layout.joysticks["rightJoystick"]?.hide ?? true { + abxyView + .opacity(hideABXY ? 0 : 1) + .allowsHitTesting(!hideABXY) + .animation(.easeInOut(duration: 0.2), value: hideABXY) + } else { + abxyView + } } } } @@ -128,34 +503,102 @@ struct ControllerView: View { if stickButton { VStack { HStack(spacing: 50) { - ButtonView(button: .leftStick) - .padding() + editableButton(.leftStick).padding() Spacer() - ButtonView(button: .rightStick) - .padding() + editableButton(.rightStick).padding() } .padding(.top, 30) HStack(spacing: 50) { - ButtonView(button: .back) + editableButton(.back) Spacer() - ButtonView(button: .start) + editableButton(.start) } } .padding(.bottom, 20) } else { HStack(spacing: 50) { - ButtonView(button: .back) + editableButton(.back) Spacer() - ButtonView(button: .start) + editableButton(.start) } .padding(.bottom, 20) } } } + + // MARK: - Button Groups - // MARK: - Methods + private var shoulderButtonsLeft: some View { + HStack(spacing: 20) { + editableButton(.leftTrigger) + editableButton(.leftShoulder) + } + .frame(width: 160 * CGFloat(controllerScale), height: 20 * CGFloat(controllerScale)) + } + private var shoulderButtonsRight: some View { + HStack(spacing: 20) { + editableButton(.rightShoulder) + editableButton(.rightTrigger) + } + .frame(width: 160 * CGFloat(controllerScale), height: 20 * CGFloat(controllerScale)) + } + + private var dpadView: some View { + VStack(spacing: 7) { + editableButton(.dPadUp) + HStack(spacing: 22) { + editableButton(.dPadLeft) + Spacer(minLength: 22) + editableButton(.dPadRight) + } + editableButton(.dPadDown) + } + .frame(width: 145 * CGFloat(controllerScale), height: 145 * CGFloat(controllerScale)) + } + + private var abxyView: some View { + VStack(spacing: 7) { + editableButton(.X) + HStack(spacing: 22) { + editableButton(.Y) + Spacer(minLength: 22) + editableButton(.A) + } + editableButton(.B) + } + .frame(width: 145 * CGFloat(controllerScale), height: 145 * CGFloat(controllerScale)) + } + + // MARK: - Helper Methods + + private func editableButton(_ button: VirtualControllerButton) -> some View { + EditableButtonView( + button: button, + layout: $layout, + isEditing: isEditing, + selectedButton: $selectedButton, + selectedJoystick: $selectedJoystick + ) + } + + private func editableJoystick( + id: String, + iscool: Bool = false, + showBackground: Binding, + ) -> some View { + EditableJoystickView( + id: id, + iscool: iscool, + showBackground: showBackground, + layout: $layout, + isEditing: isEditing, + selectedJoystick: $selectedJoystick, + selectedButton: $selectedButton, + ) + } + private func updateOrientation() { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first { @@ -164,117 +607,317 @@ struct ControllerView: View { } } +// MARK: - Editable Joystick View -struct ShoulderButtonsViewLeft: View { - @State private var width: CGFloat = 160 - @State private var height: CGFloat = 20 +struct EditableJoystickView: View { + let id: String + let iscool: Bool + @Binding var showBackground: Bool + @Binding var layout: LayoutConfig + var isEditing: Bool + @Binding var selectedJoystick: String? + @Binding var selectedButton: String? + @GestureState private var dragOffset = CGSize.zero @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 var body: some View { - HStack(spacing: 20) { - ButtonView(button: .leftTrigger) - ButtonView(button: .leftShoulder) - } - .frame(width: width, height: height) - .onAppear { - if UIDevice.current.systemName.contains("iPadOS") { - width *= 1.2 - height *= 1.2 + ZStack { + if isEditing { + Circle() + .fill(Color.gray.opacity(0.3)) + .frame(width: 160, height: 160) + .overlay( + Text("Joystick") + .font(.caption) + .foregroundColor(.white) + ) + .scaleEffect((layout.joysticks[id]?.scale ?? 1.0) * controllerScale) + .border(selectedJoystick == id ? Color.green : Color.clear, width: 3) + .offset( + x: (layout.joysticks[id]?.offset.width ?? 0) + dragOffset.width, + y: (layout.joysticks[id]?.offset.height ?? 0) + dragOffset.height + ) + .onTapGesture { + selectedJoystick = selectedJoystick == id ? nil : id + selectedButton = nil + } + .gesture( + DragGesture() + .updating($dragOffset) { value, state, _ in + state = value.translation + selectedJoystick = id + selectedButton = nil + } + .onEnded { value in + layout.joysticks[id, default: JoystickLayout()].offset.width += value.translation.width + layout.joysticks[id, default: JoystickLayout()].offset.height += value.translation.height + } + ) + } else { + if layout.joysticks[id]?.background ?? false { + JoystickController(iscool: iscool, showBackground: .constant(true)) + .scaleEffect(layout.joysticks[id]?.scale ?? 1.0) + .offset(layout.joysticks[id]?.offset ?? .zero) + } else { + JoystickController(iscool: iscool, showBackground: $showBackground) + .scaleEffect(layout.joysticks[id]?.scale ?? 1.0) + .offset(layout.joysticks[id]?.offset ?? .zero) + } } - - width *= CGFloat(controllerScale) - height *= CGFloat(controllerScale) } } } -struct ShoulderButtonsViewRight: View { - @State private var width: CGFloat = 160 - @State private var height: CGFloat = 20 - @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 +// MARK: - Layout Options View + +struct LayoutOptionsView: View { + let gameId: String? + @Binding var layout: LayoutConfig + @Environment(\.presentationMode) var presentationMode + @State private var showingResetAlert = false + @State private var showingCopySheet = false var body: some View { - HStack(spacing: 20) { - ButtonView(button: .rightShoulder) - ButtonView(button: .rightTrigger) - } - .frame(width: width, height: height) - .onAppear { - if UIDevice.current.systemName.contains("iPadOS") { - width *= 1.2 - height *= 1.2 + NavigationView { + VStack(spacing: 20) { + if let gameId = gameId { + VStack(alignment: .leading, spacing: 8) { + Text("Current Game") + .font(.headline) + HStack { + Image(systemName: "gamecontroller.fill") + .foregroundColor(.blue) + Text(gameId) + .font(.subheadline) + Spacer() + if LayoutManager.shared.hasCustomLayout(for: gameId) { + HStack { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + Text("Custom Layout") + .font(.caption) + .foregroundColor(.green) + } + } else { + Text("Using Default") + .font(.caption) + .foregroundColor(.secondary) + } + } + .padding() + .background(Color.gray.opacity(0.1)) + .cornerRadius(8) + } + } + + VStack(alignment: .leading, spacing: 12) { + Text("Layout Actions") + .font(.headline) + + Button(action: { + showingCopySheet = true + }) { + HStack { + Image(systemName: "doc.on.doc") + Text("Copy Layout From...") + Spacer() + Image(systemName: "chevron.right") + } + .padding() + .background(Color.blue.opacity(0.1)) + .foregroundColor(.blue) + .cornerRadius(8) + } + + Button(action: { + layout = LayoutManager.shared.load(for: nil) + }) { + HStack { + Image(systemName: "arrow.clockwise") + Text("Reset to Default Layout") + Spacer() + Image(systemName: "chevron.right") + } + .padding() + .background(Color.orange.opacity(0.1)) + .foregroundColor(.orange) + .cornerRadius(8) + } + + Button(action: { + showingResetAlert = true + }) { + HStack { + Image(systemName: "trash") + Text("Delete Custom Layout") + Spacer() + Image(systemName: "chevron.right") + } + .padding() + .background(Color.red.opacity(0.1)) + .foregroundColor(.red) + .cornerRadius(8) + } + } + + Spacer() } - - width *= CGFloat(controllerScale) - height *= CGFloat(controllerScale) + .padding() + .navigationTitle("Layout Options") + .navigationBarTitleDisplayMode(.inline) + .navigationBarItems(trailing: Button("Done") { + presentationMode.wrappedValue.dismiss() + }) + } + .alert("Delete Custom Layout", isPresented: $showingResetAlert) { + Button("Cancel", role: .cancel) { } + Button("Delete", role: .destructive) { + LayoutManager.shared.reset(for: gameId) + layout = LayoutManager.shared.load(for: gameId) + } + } message: { + Text("This will delete the custom layout for this game and revert to the default layout.") + } + .sheet(isPresented: $showingCopySheet) { + CopyLayoutView(targetGameId: gameId, layout: $layout) } } } -struct DPadView: View { - @State private var size: CGFloat = 145 - @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 +// MARK: - Copy Layout View + +struct CopyLayoutView: View { + let targetGameId: String? + @Binding var layout: LayoutConfig + @Environment(\.presentationMode) var presentationMode + @State private var availableLayouts: [String] = [] var body: some View { - VStack(spacing: 7) { - ButtonView(button: .dPadUp) - HStack(spacing: 22) { - ButtonView(button: .dPadLeft) - Spacer(minLength: 22) - ButtonView(button: .dPadRight) + NavigationView { + List { + Section(header: Text("Available Layouts")) { + Button(action: { + copyLayout(from: nil) + }) { + HStack { + Image(systemName: "gear") + .foregroundColor(.blue) + Text("Default Layout") + Spacer() + Image(systemName: "chevron.right") + .foregroundColor(.gray) + } + } + + ForEach(availableLayouts, id: \.self) { gameId in + if gameId != targetGameId { + Button(action: { + copyLayout(from: gameId) + }) { + HStack { + Image(systemName: "gamecontroller.fill") + .foregroundColor(.green) + Text(gameId) + Spacer() + Image(systemName: "chevron.right") + .foregroundColor(.gray) + } + } + } + } + } } - ButtonView(button: .dPadDown) + .navigationTitle("Copy Layout") + .navigationBarTitleDisplayMode(.inline) + .navigationBarItems( + leading: Button("Cancel") { + presentationMode.wrappedValue.dismiss() + } + ) } - .frame(width: size, height: size) .onAppear { - if UIDevice.current.systemName.contains("iPadOS") { - size *= 1.2 + loadAvailableLayouts() + } + } + + private func loadAvailableLayouts() { + availableLayouts = LayoutManager.shared.getAllGameLayouts() + } + + private func copyLayout(from sourceGameId: String?) { + let sourceLayout = LayoutManager.shared.load(for: sourceGameId) + layout = sourceLayout + LayoutManager.shared.save(layout, for: targetGameId) + presentationMode.wrappedValue.dismiss() + } +} + +// MARK: - Editable Button View + +struct EditableButtonView: View { + let button: VirtualControllerButton + @Binding var layout: LayoutConfig + var isEditing: Bool + @Binding var selectedButton: String? + @Binding var selectedJoystick: String? + @GestureState private var dragOffset = CGSize.zero + + var body: some View { + Group { + if isEditing { + ExtButtonIconView(button: button) + .scaleEffect(layout.buttons[button.id]?.scale ?? 1.0) + .border(selectedButton == button.id ? Color.blue : Color.clear, width: 3) + .offset( + x: (layout.buttons[button.id]?.offset.width ?? 0) + dragOffset.width, + y: (layout.buttons[button.id]?.offset.height ?? 0) + dragOffset.height + ) + .onTapGesture { + selectedButton = selectedButton == button.id ? nil : button.id + selectedJoystick = nil + } + .gesture( + DragGesture() + .updating($dragOffset) { value, state, _ in + state = value.translation + selectedButton = button.id + selectedJoystick = nil + } + .onEnded { value in + layout.buttons[button.id, default: ButtonLayout()].offset.width += value.translation.width + layout.buttons[button.id, default: ButtonLayout()].offset.height += value.translation.height + } + ) + } else { + if layout.buttons[button.id, default: ButtonLayout()].hidden { + ButtonView(button: button, layout: $layout) + .scaleEffect(layout.buttons[button.id]?.scale ?? 1.0) + .offset(layout.buttons[button.id]?.offset ?? .zero) + .opacity(0) + } else { + ButtonView(button: button, layout: $layout) + .scaleEffect(layout.buttons[button.id]?.scale ?? 1.0) + .offset(layout.buttons[button.id]?.offset ?? .zero) + } } - - size *= CGFloat(controllerScale) } } } -struct ABXYView: View { - @State private var size: CGFloat = 145 - @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 - - var body: some View { - VStack(spacing: 7) { - ButtonView(button: .X) - HStack(spacing: 22) { - ButtonView(button: .Y) - Spacer(minLength: 22) - ButtonView(button: .A) - } - ButtonView(button: .B) - } - .frame(width: size, height: size) - .onAppear { - if UIDevice.current.systemName.contains("iPadOS") { - size *= 1.2 - } - - size *= CGFloat(controllerScale) - } - } -} +// MARK: - Supporting Views (Simplified ButtonView and ExtButtonIconView) struct ButtonView: View { var button: VirtualControllerButton + @Binding var layout: LayoutConfig @AppStorage("onscreenhandheld") var onscreenjoy: Bool = false @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 @Environment(\.presentationMode) var presentationMode - @AppCodableStorage("toggleButtons") var toggleButtons = ToggleButtonsState() @State private var istoggle = false - @State private var isPressed = false @State private var toggleState = false - @State private var size: CGSize = .zero var body: some View { @@ -290,16 +933,14 @@ struct ButtonView: View { .allowsHitTesting(false) } .frame(width: size.width, height: size.height) - .background( - buttonBackground - ) + .background(buttonBackground) .gesture( DragGesture(minimumDistance: 0) .onChanged { _ in handleButtonPress() } .onEnded { _ in handleButtonRelease() } ) .onAppear { - istoggle = (toggleButtons.toggle1 && button == .A) || (toggleButtons.toggle2 && button == .B) || (toggleButtons.toggle3 && button == .X) || (toggleButtons.toggle4 && button == .Y) + istoggle = layout.buttons[button.id]?.toggle ?? false size = calculateButtonSize() } .onChange(of: controllerScale) { _ in @@ -351,17 +992,16 @@ struct ButtonView: View { isPressed = toggleState let value = toggleState ? 1 : 0 Ryujinx.shared.virtualController.setButtonState(Uint8(value), for: button) - Haptics.shared.play(.medium) + Haptics.shared.play(.soft) } else { isPressed = true Ryujinx.shared.virtualController.setButtonState(1, for: button) - Haptics.shared.play(.medium) + Haptics.shared.play(.soft) } } private func handleButtonRelease() { if istoggle { return } - guard isPressed else { return } isPressed = false @@ -394,56 +1034,14 @@ struct ButtonView: View { ) } - // Centralized button configuration private var buttonConfig: ButtonConfiguration { - switch button { - case .A: - return ButtonConfiguration(iconName: "a.circle.fill") - case .B: - return ButtonConfiguration(iconName: "b.circle.fill") - case .X: - return ButtonConfiguration(iconName: "x.circle.fill") - case .Y: - return ButtonConfiguration(iconName: "y.circle.fill") - case .leftStick: - return ButtonConfiguration(iconName: "l.joystick.press.down.fill") - case .rightStick: - return ButtonConfiguration(iconName: "r.joystick.press.down.fill") - case .dPadUp: - return ButtonConfiguration(iconName: "arrowtriangle.up.circle.fill") - case .dPadDown: - return ButtonConfiguration(iconName: "arrowtriangle.down.circle.fill") - case .dPadLeft: - return ButtonConfiguration(iconName: "arrowtriangle.left.circle.fill") - case .dPadRight: - return ButtonConfiguration(iconName: "arrowtriangle.right.circle.fill") - case .leftTrigger: - return ButtonConfiguration(iconName: "zl.rectangle.roundedtop.fill") - case .rightTrigger: - return ButtonConfiguration(iconName: "zr.rectangle.roundedtop.fill") - case .leftShoulder: - return ButtonConfiguration(iconName: "l.rectangle.roundedbottom.fill") - case .rightShoulder: - return ButtonConfiguration(iconName: "r.rectangle.roundedbottom.fill") - case .start: - return ButtonConfiguration(iconName: "plus.circle.fill") - case .back: - return ButtonConfiguration(iconName: "minus.circle.fill") - case .guide: - return ButtonConfiguration(iconName: "house.circle.fill") - } - } - - struct ButtonConfiguration { - let iconName: String + ButtonConfiguration.config(for: button) } } - struct ExtButtonIconView: View { var button: VirtualControllerButton var opacity = 0.8 - @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 @State private var size: CGSize = .zero @@ -460,9 +1058,7 @@ struct ExtButtonIconView: View { .allowsHitTesting(false) } .frame(width: size.width, height: size.height) - .background( - buttonBackground - ) + .background(buttonBackground) .onAppear { size = calculateButtonSize() } @@ -531,28 +1127,34 @@ struct ExtButtonIconView: View { } private var buttonConfig: ButtonConfiguration { - switch button { - case .A: return .init(iconName: "a.circle.fill") - case .B: return .init(iconName: "b.circle.fill") - case .X: return .init(iconName: "x.circle.fill") - case .Y: return .init(iconName: "y.circle.fill") - case .leftStick: return .init(iconName: "l.joystick.press.down.fill") - case .rightStick: return .init(iconName: "r.joystick.press.down.fill") - case .dPadUp: return .init(iconName: "arrowtriangle.up.circle.fill") - case .dPadDown: return .init(iconName: "arrowtriangle.down.circle.fill") - case .dPadLeft: return .init(iconName: "arrowtriangle.left.circle.fill") - case .dPadRight: return .init(iconName: "arrowtriangle.right.circle.fill") - case .leftTrigger: return .init(iconName: "zl.rectangle.roundedtop.fill") - case .rightTrigger: return .init(iconName: "zr.rectangle.roundedtop.fill") - case .leftShoulder: return .init(iconName: "l.rectangle.roundedbottom.fill") - case .rightShoulder: return .init(iconName: "r.rectangle.roundedbottom.fill") - case .start: return .init(iconName: "plus.circle.fill") - case .back: return .init(iconName: "minus.circle.fill") - case .guide: return .init(iconName: "gearshape.fill") - } - } - - struct ButtonConfiguration { - let iconName: String + ButtonConfiguration.config(for: button) + } +} + +// MARK: - Button Configuration + +struct ButtonConfiguration { + let iconName: String + + static func config(for button: VirtualControllerButton) -> ButtonConfiguration { + switch button { + case .A: return ButtonConfiguration(iconName: "a.circle.fill") + case .B: return ButtonConfiguration(iconName: "b.circle.fill") + case .X: return ButtonConfiguration(iconName: "x.circle.fill") + case .Y: return ButtonConfiguration(iconName: "y.circle.fill") + case .leftStick: return ButtonConfiguration(iconName: "l.joystick.press.down.fill") + case .rightStick: return ButtonConfiguration(iconName: "r.joystick.press.down.fill") + case .dPadUp: return ButtonConfiguration(iconName: "arrowtriangle.up.circle.fill") + case .dPadDown: return ButtonConfiguration(iconName: "arrowtriangle.down.circle.fill") + case .dPadLeft: return ButtonConfiguration(iconName: "arrowtriangle.left.circle.fill") + case .dPadRight: return ButtonConfiguration(iconName: "arrowtriangle.right.circle.fill") + case .leftTrigger: return ButtonConfiguration(iconName: "zl.rectangle.roundedtop.fill") + case .rightTrigger: return ButtonConfiguration(iconName: "zr.rectangle.roundedtop.fill") + case .leftShoulder: return ButtonConfiguration(iconName: "l.rectangle.roundedbottom.fill") + case .rightShoulder: return ButtonConfiguration(iconName: "r.rectangle.roundedbottom.fill") + case .start: return ButtonConfiguration(iconName: "plus.circle.fill") + case .back: return ButtonConfiguration(iconName: "minus.circle.fill") + case .guide: return ButtonConfiguration(iconName: "gearshape.fill") + } } } diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/Joystick.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/Joystick.swift index 67a7615f5..743597922 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/Joystick.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/Joystick.swift @@ -10,22 +10,23 @@ import SwiftUI struct Joystick: View { @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 - + @State var right = true @Binding var position: CGPoint @State var joystickSize: CGFloat var boundarySize: CGFloat @State private var offset: CGSize = .zero @Binding var showBackground: Bool + @State var joystickSmallSize = false let sensitivity: CGFloat = 1.2 - var dragGesture: some Gesture { DragGesture() .onChanged { value in withAnimation(.easeIn) { showBackground = true + joystickSmallSize = true } let translation = value.translation @@ -44,16 +45,28 @@ struct Joystick: View { x: max(-1, min(1, (offset.width / extendedRadius) * sensitivity)), y: max(-1, min(1, (offset.height / extendedRadius) * sensitivity)) ) + + setPos() } .onEnded { _ in offset = .zero position = .zero + setPos() withAnimation(.easeOut) { 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 { ZStack { Circle() @@ -82,7 +95,7 @@ struct Joystick: View { .scaleEffect(controllerScale) } .frame(width: boundarySize, height: boundarySize) - .onChange(of: showBackground) { newValue in + .onChange(of: joystickSmallSize) { newValue in if newValue { joystickSize *= 1.4 } else { diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/JoystickView.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/JoystickView.swift index 2459c6a7c..73362d70e 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/JoystickView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/Joystick/JoystickView.swift @@ -9,7 +9,7 @@ import SwiftUI struct JoystickController: View { - @State var iscool: Bool? = nil + @State var iscool: Bool @Environment(\.colorScheme) var colorScheme @Binding var showBackground: Bool @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 @@ -25,15 +25,8 @@ struct JoystickController: View { } public var body: some View { - VStack { - Joystick(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) - } - } + Group { + Joystick(right: iscool, position: $position, joystickSize: dragDiameter * 0.2, boundarySize: dragDiameter, showBackground: $showBackground) } } } diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/EmulationView.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/EmulationView.swift index 58a2b6d49..718af4e3d 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/EmulationView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/EmulationView.swift @@ -16,8 +16,7 @@ struct EmulationView: View { @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 isAirplaying = Air.shared.connected @Binding var startgame: Game? @@ -27,9 +26,18 @@ struct EmulationView: View { @State var showSettings = false @State var pauseEmu = true @AppStorage("location-enabled") var locationenabled: Bool = false + @FocusState private var isFocused: Bool + @ObservedObject var ryujijnx = Ryujinx.shared var body: some View { ZStack { + if oldView { + Color.black + .ignoresSafeArea() + .edgesIgnoringSafeArea(.all) + .allowsHitTesting(false) + } + if isAirplaying { TouchView() .ignoresSafeArea() @@ -37,104 +45,100 @@ struct EmulationView: View { .onAppear { Air.play(AnyView(MetalView().ignoresSafeArea().edgesIgnoringSafeArea(.all))) } - - Color.black - .ignoresSafeArea() - .edgesIgnoringSafeArea(.all) - .allowsHitTesting(false) } else { - MetalView() // The Emulation View - .ignoresSafeArea() - .edgesIgnoringSafeArea(.all) + MetalViewContainer() // The Emulation View } // Above Emulation View if isVCA { - ControllerView() // Virtual Controller + ControllerView(isEditing: .constant(false), gameId: startgame?.titleId) // Virtual Controller .opacity(controllerOpacity) .allowsHitTesting(true) } - Group { - VStack { - HStack { - if performacehud, !showlogsgame { - PerformanceOverlayView() - } - - Spacer() - - if performacehud, showlogsgame { - PerformanceOverlayView() - } - } - - HStack { - if showlogsgame, get_current_fps() != 0 { - LogFileView(isfps: false) - } - - Spacer() - } - - - if ssb { - HStack { - - Menu { - - /* - Button { - showSettings.toggle() - - } label: { - Label { - Text("Game Settings") - } icon: { - Image(systemName: "gearshape.circle") - } - } - */ - - Button { - pause_emulation(pauseEmu) - pauseEmu.toggle() - } label: { - Label { - Text(pauseEmu ? "Pause" : "Play") - } icon: { - Image(systemName: pauseEmu ? "pause.circle" : "play.circle") - } - } - - Button(role: .destructive) { - startgame = nil - stop_emulation() - try? Ryujinx.shared.stop() - } label: { - Label { - Text("Exit (Unstable)") - } icon: { - Image(systemName: "x.circle") - } - } - } label: { - ExtButtonIconView(button: .guide, opacity: 0.4) - } + + VStack { + HStack { + if !performacehud, showlogsgame, ProcessInfo.processInfo.isLowPowerModeEnabled { + Circle() + .fill(Color.orange) + .frame(width: 10, height: 10) .padding() - - Spacer() - - } } + + if ssb { + Menu { + Button { + pause_emulation(pauseEmu) + pauseEmu.toggle() + } label: { + Label { + Text(pauseEmu ? "Pause" : "Play") + } icon: { + Image(systemName: pauseEmu ? "pause.circle" : "play.circle") + } + } + + 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) { + startgame = nil + stop_emulation() + try? Ryujinx.shared.stop() + } label: { + Label { + Text("Exit (Unstable)") + } icon: { + Image(systemName: "x.circle") + } + } + } label: { + ExtButtonIconView(button: .guide, opacity: 0.4) + } + .menuStyle(.borderlessButton) + .menuIndicator(.hidden) + .padding() + } + + Spacer() + + if performacehud, getenv("MTL_HUD_ENABLED").flatMap({ String(cString: $0) }) != "1" { + PerformanceOverlayView() + .opacity(controllerOpacity) + .padding(.horizontal) + } + + } + + Spacer() + } + + if showlogsgame, get_current_fps() != 0 { + VStack { + LogFileView(isfps: false) + + Spacer() } } } .onAppear { + DispatchQueue.main.async { + isFocused = true + } + LocationManager.sharedInstance.startUpdatingLocation() Air.shared.connectionCallbacks.append { cool in DispatchQueue.main.async { @@ -148,10 +152,12 @@ struct EmulationView: View { print(cool) startgame = nil stop_emulation() - try? Ryujinx.shared.stop() + try? ryujijnx.stop() } } } + .onKeyPress() + .focused($isFocused) .onChange(of: scenePhase) { newPhase in // Detect when the app enters the 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) + } + } +} + diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/PerformanceDisplay/PerformanceOverlay.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/PerformanceDisplay/PerformanceOverlay.swift index 5573e1282..41f546314 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/PerformanceDisplay/PerformanceOverlay.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/PerformanceDisplay/PerformanceOverlay.swift @@ -14,12 +14,21 @@ struct PerformanceOverlayView: View { var body: some View { VStack { - Text("\(fpsmonitor.formatFPS())") - .foregroundStyle(.white) - .stroke(color: .black, width: 2) - Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage)) - .foregroundStyle(.white) - .stroke(color: .black, width: 2) + 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())") + .foregroundStyle(.white) + .stroke(color: .black, width: 2) + Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage)) + .foregroundStyle(.white) + .stroke(color: .black, width: 2) + } } } } diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/Container/MetalViewContainer.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/Container/MetalViewContainer.swift new file mode 100644 index 000000000..8cba7e6a3 --- /dev/null +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/Container/MetalViewContainer.swift @@ -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 + } +} diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MeloMTKView.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MeloMTKView.swift index 25d296012..15e26638c 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MeloMTKView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MeloMTKView.swift @@ -99,7 +99,7 @@ class MeloMTKView: MTKView { let disabled = UserDefaults.standard.bool(forKey: "disableTouch") guard !disabled else { return } - setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9) + setAspectRatio(Ryujinx.shared.aspectRatio) for touch in touches { let location = touch.location(in: self) @@ -153,7 +153,7 @@ class MeloMTKView: MTKView { let disabled = UserDefaults.standard.bool(forKey: "disableTouch") guard !disabled else { return } - setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9) + setAspectRatio(Ryujinx.shared.aspectRatio) for touch in touches { if ignoredTouches.contains(touch) { diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MetalView.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MetalView.swift index 9f9576bb0..7366a0dd0 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MetalView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/MetalView.swift @@ -21,14 +21,13 @@ struct MetalView: UIViewRepresentable { 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() set_native_window(layerPtr) Ryujinx.shared.emulationUIView = view - Ryujinx.shared.metalLayer = metalLayer return view @@ -51,5 +50,7 @@ struct MetalView: UIViewRepresentable { func updateUIView(_ uiView: UIView, context: Context) { // nothin + print(context) } } + diff --git a/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/TouchView.swift b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/Touch/TouchView.swift similarity index 83% rename from src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/TouchView.swift rename to src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/Touch/TouchView.swift index e4cbaa44e..4f2e68af7 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/TouchView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/MetalView/Touch/TouchView.swift @@ -10,8 +10,7 @@ import MetalKit struct TouchView: UIViewRepresentable { func makeUIView(context: Context) -> UIView { - let view = MeloMTKView() - return view + return MeloMTKView() } func updateUIView(_ uiView: UIView, context: Context) {} diff --git a/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift b/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift index 1eb05187d..36c81772a 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift @@ -52,6 +52,9 @@ struct ContentView: View { @AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: Bool = true @AppStorage("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS") var syncqsubmits: 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 @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"), ] + if #available(iOS 19, *) { + setenv("HAS_TXM", ProcessInfo.processInfo.hasTXM ? "1" : "0", 1) + } else { + setenv("HAS_TXM", "0", 1) + } + _settings = State(initialValue: defaultSettings) initializeSDL() @@ -146,9 +155,7 @@ struct ContentView: View { let _ = loadSettings() isLoading = true - Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in - refreshControllersList() - } + refreshControllersList() UserDefaults.standard.set(false, forKey: "lockInApp") @@ -322,7 +329,9 @@ struct ContentView: View { controllersList.mutableForEach { $0.name = $0.name.replacingOccurrences(of: "GC - ", with: "") } if controllersList.count == 1 { - currentControllers.append(controllersList[0]) + if !ProcessInfo.processInfo.isiOSAppOnMac { + currentControllers.append(controllersList[0]) + } } else if (controllersList.count - 1) >= 1 { for controller in controllersList { if controller.id != onscreencontroller.id && !currentControllers.contains(where: { $0.id == controller.id }) { @@ -392,6 +401,12 @@ struct ContentView: View { if syncqsubmits { 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() { @@ -405,7 +420,11 @@ struct ContentView: View { if jitStreamerEB { 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 useTrollStore { @@ -415,12 +434,7 @@ struct ContentView: View { } else if jitStreamerEB { enableJITEB() } else { - if !allocateTest(), checkDebugged() { - loop_heartbeat() - sleep(5) - let cool = String(cString: attach(getpid())!) - print(cool) - } + // nothing } } } @@ -429,8 +443,6 @@ struct ContentView: View { if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), components.host == "game" { - refreshControllersList() - if let text = components.queryItems?.first(where: { $0.name == "id" })?.value { game = ryujinx.games.first(where: { $0.titleId == text }) } else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value { diff --git a/src/MeloNX/MeloNX/App/Views/Main/UI/GamesList/GameListView.swift b/src/MeloNX/MeloNX/App/Views/Main/UI/GamesList/GameListView.swift index 47124f48e..f8911a32b 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/GamesList/GameListView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/GamesList/GameListView.swift @@ -27,13 +27,21 @@ struct GameLibraryView: View { @State var isSelectingGameFile = false @State var isViewingGameInfo: Bool = false @State var gamePerGameSettings: Game? + @State var gameController: Game? var isShowingPerGameSettings: Binding { Binding { gamePerGameSettings != nil } set: { value in !value ? gamePerGameSettings = nil : () } - + } + + var isShowingGameController: Binding { + Binding { + gameController != nil + } set: { value in + !value ? gameController = nil : () + } } @State var isSelectingGameUpdate: Bool = false @State var isSelectingGameDLC: Bool = false @@ -213,6 +221,9 @@ struct GameLibraryView: View { .sheet(isPresented: isShowingPerGameSettings) { PerGameSettingsView(titleId: gamePerGameSettings!.titleId) } + .fullScreenCover(isPresented: isShowingGameController) { + ControllerView(isEditing: isShowingGameController, gameId: gameController?.titleId) + } .sheet(isPresented: Binding( get: { isViewingGameInfo && gameInfo != nil }, set: { newValue in @@ -284,6 +295,7 @@ struct GameLibraryView: View { isSelectingGameDLC: $isSelectingGameDLC, gameRequirements: $gameRequirements, gameInfo: $gameInfo, + isShowingGameController: $gameController, perGameSettings: $gamePerGameSettings ) .padding(.horizontal) @@ -302,6 +314,7 @@ struct GameLibraryView: View { isSelectingGameDLC: $isSelectingGameDLC, gameRequirements: $gameRequirements, gameInfo: $gameInfo, + isShowingGameController: $gameController, perGameSettings: $gamePerGameSettings ) .padding(.horizontal) @@ -502,6 +515,12 @@ struct GameLibraryView: View { } label: { Label("\(game.titleName) Settings", systemImage: "gear") } + + Button { + gameController = game + } label: { + Label("Controller Layout", systemImage: "formfitting.gamecontroller") + } } Section { @@ -527,6 +546,12 @@ struct GameLibraryView: View { Label("Remove from Recents", systemImage: "trash") } + Button(role: .destructive) { + Ryujinx.clearShaderCache(game.titleId) + } label: { + Label("Clear Shader Cache", systemImage: "trash") + } + if #available(iOS 15, *) { Button(role: .destructive) { deleteGame(game: game) @@ -797,6 +822,7 @@ struct GameListRow: View { @Binding var isSelectingGameDLC: Bool @Binding var gameRequirements: [GameRequirements] @Binding var gameInfo: Game? + @Binding var isShowingGameController: Game? @StateObject private var settingsManager = PerGameSettingsManager.shared @Binding var perGameSettings: Game? @State var gametoDelete: Game? @@ -939,6 +965,14 @@ struct GameListRow: View { } label: { Label("\(game.titleName) Settings", systemImage: "gear") } + + // isShowingGameController + + Button { + isShowingGameController = game + } label: { + Label("Controller Layout", systemImage: "formfitting.gamecontroller") + } } Section { @@ -958,6 +992,12 @@ struct GameListRow: View { } Section { + Button(role: .destructive) { + Ryujinx.clearShaderCache(game.titleId) + } label: { + Label("Clear Shader Cache", systemImage: "trash") + } + Button(role: .destructive) { gametoDelete = game showGameDeleteConfirmation.toggle() @@ -1114,6 +1154,12 @@ struct GameListRow: View { } label: { Label("Game Info", systemImage: "info.circle") } + + Button { + isShowingGameController = game + } label: { + Label("Controller Layout", systemImage: "formfitting.gamecontroller") + } } Section { @@ -1133,6 +1179,12 @@ struct GameListRow: View { } Section { + Button(role: .destructive) { + Ryujinx.clearShaderCache(game.titleId) + } label: { + Label("Clear Shader Cache", systemImage: "trash") + } + Button { gametoDelete = game showGameDeleteConfirmation.toggle() @@ -1238,30 +1290,10 @@ func pullGameCompatibility(completion: @escaping (Result<[GameRequirements], Err extension View { func wow(_ colorScheme: ColorScheme) -> some View { - if #available(iOS 26.0, *) { - return self - .glassEffect(Glass.regular, in: - RoundedRectangle(cornerRadius: 12) - ) - } else { - return self - .background( - RoundedRectangle(cornerRadius: 12) - .fill(colorScheme == .dark ? Color(.systemGray6) : Color(.systemGray6).opacity(0.5)) - ) - } + self + .background( + RoundedRectangle(cornerRadius: 12) + .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() -} diff --git a/src/MeloNX/MeloNX/App/Views/Main/UI/SettingsView/SettingsView.swift b/src/MeloNX/MeloNX/App/Views/Main/UI/SettingsView/SettingsView.swift index 4205d284f..1abe0127f 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/SettingsView/SettingsView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/SettingsView/SettingsView.swift @@ -232,6 +232,7 @@ struct SettingsViewNew: View { @AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: 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 @@ -260,20 +261,18 @@ struct SettingsViewNew: View { @AppStorage("disableTouch") var disableTouch = false - @AppStorage("disableTouch") var blackScreen = false - @AppStorage("location-enabled") var locationenabled: Bool = false @AppStorage("runOnMainThread") var runOnMainThread = false @AppStorage("oldSettingsUI") var oldSettingsUI = false - @AppCodableStorage("toggleButtons") var toggleButtons = ToggleButtonsState() - let totalMemory = ProcessInfo.processInfo.physicalMemory @AppStorage("lockInApp") var restartApp = false + @AppStorage("OldView") var oldView = true + @State private var showResolutionInfo = false @State private var showAnisotropicInfo = false @State private var showControllerInfo = false @@ -375,12 +374,26 @@ struct SettingsViewNew: View { color: .blue ) - InfoCard( - title: "System", - value: "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)", - icon: "applelogo", - color: .gray - ) + 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( + title: "System", + value: "\(ProcessInfo.processInfo.isiOSAppOnMac ? "macOS" : UIDevice.current.systemName) \(version) (\(build))", + icon: "applelogo", + color: .gray + ) + } else { + InfoCard( + title: "System", + value: "\(ProcessInfo.processInfo.isiOSAppOnMac ? "macOS" : UIDevice.current.systemName) \(UIDevice.current.systemVersion)", + icon: "applelogo", + color: .gray + ) + } InfoCard( title: "Increased Memory Limit", @@ -551,6 +564,16 @@ struct SettingsViewNew: View { .font(.subheadline) .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)") .font(.subheadline) .foregroundColor(.secondary) @@ -755,6 +778,11 @@ struct SettingsViewNew: View { // Aspect ratio card SettingsCard { 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") .font(.headline) @@ -825,16 +853,6 @@ struct SettingsViewNew: View { Divider() 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") { SettingsCard { VStack(spacing: 4) { + if UIDevice.current.userInterfaceIdiom == .pad { SettingsToggle(isOn: $toggleGreen, icon: "arrow.clockwise", label: "Toggle Color Green when \"ON\"") @@ -1254,11 +1273,6 @@ struct SettingsViewNew: View { Divider() - if colorScheme == .light { - SettingsToggle(isOn: $blackScreen, icon: "iphone.slash", label: "Black Screen when using AirPlay") - - Divider() - } Button { showAppIconSwitcher = true @@ -1354,20 +1368,25 @@ struct SettingsViewNew: View { 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") - if ryujinx.firmwareversion != "0" { - Divider() - Button { - Ryujinx.shared.removeFirmware() - } label: { - HStack { - Text("Remove Firmware") - .foregroundColor(.blue) - Spacer() - } - .padding(.vertical, 8) + Divider() + + Button { + Ryujinx.clearShaderCache() + } label: { + HStack { + Image(systemName: "trash") + .foregroundColor(.blue) + Text("Clear All Shader Cache") + .foregroundColor(.primary) + Spacer() } + .padding(.vertical, 8) } } } diff --git a/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift b/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift index aeb7e5146..0f4e1042f 100644 --- a/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift +++ b/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift @@ -41,7 +41,11 @@ struct MeloNXApp: App { @AppStorage("autoJIT") var autoJIT = false @State var fourgbiPad = false + @State var ios19 = 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) var body: some Scene { WindowGroup { @@ -77,10 +81,19 @@ struct MeloNXApp: App { withAnimation(.easeOut) { finishedStorage = newValue } + + if #available(iOS 19, *), newValue { + dualMapped = !ProcessInfo.processInfo.isiOSAppOnMac + dualMappededit = true + } } } } .onAppear() { + if #available(iOS 19, *), ProcessInfo.processInfo.hasTXM, !ignores19 { + ios19 = true + } + if UIDevice.current.userInterfaceIdiom == .pad && !ignores { print((Double(ProcessInfo.processInfo.physicalMemory) / 1_000_000_000)) 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 } return String(data: data, encoding: .utf8) } + diff --git a/src/MeloNX/MeloNX/Assets/Localizable.xcstrings b/src/MeloNX/MeloNX/Assets/Localizable.xcstrings new file mode 100644 index 000000000..a0a76a717 --- /dev/null +++ b/src/MeloNX/MeloNX/Assets/Localizable.xcstrings @@ -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" +} \ No newline at end of file diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/BreakpointJIT b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/BreakpointJIT new file mode 100755 index 000000000..1d3ff6818 Binary files /dev/null and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/BreakpointJIT differ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/Headers/BreakJIT.h b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/Headers/BreakJIT.h new file mode 100644 index 000000000..a32eaa09d --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/Headers/BreakJIT.h @@ -0,0 +1,28 @@ +// +// BreakJIT.h +// BreakpointJIT +// +// Created by Stossy11 on 09/07/2025. +// + +#ifndef BreakGetJITMapping_h +#define BreakGetJITMapping_h + +#include + +#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 */ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Info.plist b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/Info.plist similarity index 52% rename from src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Info.plist rename to src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/Info.plist index 228311e3f..eb12665fb 100644 Binary files a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Info.plist and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/BreakpointJIT.framework/Info.plist differ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT-Swift.h b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT-Swift.h deleted file mode 100644 index e87058bf0..000000000 --- a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT-Swift.h +++ /dev/null @@ -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() -# include -#endif - -#pragma clang diagnostic ignored "-Wauto-import" -#if defined(__OBJC__) -#include -#endif -#if defined(__cplusplus) -#include -#include -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#endif -#if defined(__cplusplus) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module" -#if defined(__arm64e__) && __has_include() -# include -#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() -# include -# 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 diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT.h b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT.h deleted file mode 100644 index 572ca9033..000000000 --- a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// StosJIT.h -// StosJIT -// -// Created by Stossy11 on 10/05/2025. -// - -#import -#import - -//! 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 - - diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/idevice.h b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/idevice.h deleted file mode 100644 index 836b1e94d..000000000 --- a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/idevice.h +++ /dev/null @@ -1,2916 +0,0 @@ -// Jackson Coxson -// Bindings to idevice - https://github.com/jkcoxson/idevice - -#include -#include -#include -#include -#include -#include - -#define LOCKDOWN_PORT 62078 - -typedef enum AfcFopenMode { - AfcRdOnly = 1, - AfcRw = 2, - AfcWrOnly = 3, - AfcWr = 4, - AfcAppend = 5, - AfcRdAppend = 6, -} AfcFopenMode; - -/** - * Link type for creating hard or symbolic links - */ -typedef enum AfcLinkType { - Hard = 1, - Symbolic = 2, -} AfcLinkType; - -typedef enum IdeviceErrorCode { - IdeviceSuccess = 0, - Socket = -1, - Tls = -2, - TlsBuilderFailed = -3, - Plist = -4, - Utf8 = -5, - UnexpectedResponse = -6, - GetProhibited = -7, - SessionInactive = -8, - InvalidHostID = -9, - NoEstablishedConnection = -10, - HeartbeatSleepyTime = -11, - HeartbeatTimeout = -12, - NotFound = -13, - CdtunnelPacketTooShort = -14, - CdtunnelPacketInvalidMagic = -15, - PacketSizeMismatch = -16, - Json = -17, - DeviceNotFound = -18, - DeviceLocked = -19, - UsbConnectionRefused = -20, - UsbBadCommand = -21, - UsbBadDevice = -22, - UsbBadVersion = -23, - BadBuildManifest = -24, - ImageNotMounted = -25, - Reqwest = -26, - InternalError = -27, - Xpc = -28, - NsKeyedArchiveError = -29, - UnknownAuxValueType = -30, - UnknownChannel = -31, - AddrParseError = -32, - DisableMemoryLimitFailed = -33, - NotEnoughBytes = -34, - Utf8Error = -35, - InvalidArgument = -36, - UnknownErrorType = -37, - PemParseFailed = -38, - MisagentFailure = -39, - InstallationProxyOperationFailed = -40, - Afc = -41, - UnknownAfcOpcode = -42, - InvalidAfcMagic = -43, - AfcMissingAttribute = -44, - AdapterIOFailed = -996, - ServiceNotFound = -997, - BufferTooSmall = -998, - InvalidString = -999, - InvalidArg = -1000, -} IdeviceErrorCode; - -typedef enum IdeviceLogLevel { - Disabled = 0, - ErrorLevel = 1, - Warn = 2, - Info = 3, - Debug = 4, - Trace = 5, -} IdeviceLogLevel; - -typedef enum IdeviceLoggerError { - Success = 0, - FileError = -1, - AlreadyInitialized = -2, - InvalidPathString = -3, -} IdeviceLoggerError; - -typedef struct AdapterHandle AdapterHandle; - -typedef struct AfcClientHandle AfcClientHandle; - -/** - * Handle for an open file on the device - */ -typedef struct AfcFileHandle AfcFileHandle; - -typedef struct AmfiClientHandle AmfiClientHandle; - -typedef struct CoreDeviceProxyHandle CoreDeviceProxyHandle; - -/** - * Opaque handle to a DebugProxyClient - */ -typedef struct DebugProxyAdapterHandle DebugProxyAdapterHandle; - -typedef struct HeartbeatClientHandle HeartbeatClientHandle; - -/** - * Opaque C-compatible handle to an Idevice connection - */ -typedef struct IdeviceHandle IdeviceHandle; - -/** - * Opaque C-compatible handle to a PairingFile - */ -typedef struct IdevicePairingFile IdevicePairingFile; - -typedef struct IdeviceSocketHandle IdeviceSocketHandle; - -typedef struct ImageMounterHandle ImageMounterHandle; - -typedef struct InstallationProxyClientHandle InstallationProxyClientHandle; - -/** - * Opaque handle to a ProcessControlClient - */ -typedef struct LocationSimulationAdapterHandle LocationSimulationAdapterHandle; - -typedef struct LockdowndClientHandle LockdowndClientHandle; - -typedef struct MisagentClientHandle MisagentClientHandle; - -/** - * Opaque handle to a ProcessControlClient - */ -typedef struct ProcessControlAdapterHandle ProcessControlAdapterHandle; - -/** - * Opaque handle to a RemoteServerClient - */ -typedef struct RemoteServerAdapterHandle RemoteServerAdapterHandle; - -typedef struct SpringBoardServicesClientHandle SpringBoardServicesClientHandle; - -typedef struct TcpProviderHandle TcpProviderHandle; - -typedef struct UsbmuxdAddrHandle UsbmuxdAddrHandle; - -typedef struct UsbmuxdConnectionHandle UsbmuxdConnectionHandle; - -typedef struct UsbmuxdProviderHandle UsbmuxdProviderHandle; - -/** - * Opaque handle to an XPCDevice - */ -typedef struct XPCDeviceAdapterHandle XPCDeviceAdapterHandle; - -typedef struct sockaddr sockaddr; - -/** - * File information structure for C bindings - */ -typedef struct AfcFileInfo { - size_t size; - size_t blocks; - int64_t creation; - int64_t modified; - char *st_nlink; - char *st_ifmt; - char *st_link_target; -} AfcFileInfo; - -/** - * Device information structure for C bindings - */ -typedef struct AfcDeviceInfo { - char *model; - size_t total_bytes; - size_t free_bytes; - size_t block_size; -} AfcDeviceInfo; - -/** - * Represents a debugserver command - */ -typedef struct DebugserverCommandHandle { - char *name; - char **argv; - uintptr_t argv_count; -} DebugserverCommandHandle; - -/** - * Opaque handle to an XPCService - */ -typedef struct XPCServiceHandle { - char *entitlement; - uint16_t port; - bool uses_remote_xpc; - char **features; - uintptr_t features_count; - int64_t service_version; -} XPCServiceHandle; - -/** - * Creates a new Idevice connection - * - * # Arguments - * * [`socket`] - Socket for communication with the device - * * [`label`] - Label for the connection - * * [`idevice`] - On success, will be set to point to a newly allocated Idevice handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `label` must be a valid null-terminated C string - * `idevice` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_new(struct IdeviceSocketHandle *socket, - const char *label, - struct IdeviceHandle **idevice); - -/** - * Creates a new Idevice connection - * - * # Arguments - * * [`addr`] - The socket address to connect to - * * [`addr_len`] - Length of the socket - * * [`label`] - Label for the connection - * * [`idevice`] - On success, will be set to point to a newly allocated Idevice handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `addr` must be a valid sockaddr - * `label` must be a valid null-terminated C string - * `idevice` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_new_tcp_socket(const struct sockaddr *addr, - socklen_t addr_len, - const char *label, - struct IdeviceHandle **idevice); - -/** - * Gets the device type - * - * # Arguments - * * [`idevice`] - The Idevice handle - * * [`device_type`] - On success, will be set to point to a newly allocated string containing the device type - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `idevice` must be a valid, non-null pointer to an Idevice handle - * `device_type` must be a valid, non-null pointer to a location where the string pointer will be stored - */ -enum IdeviceErrorCode idevice_get_type(struct IdeviceHandle *idevice, - char **device_type); - -/** - * Performs RSD checkin - * - * # Arguments - * * [`idevice`] - The Idevice handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `idevice` must be a valid, non-null pointer to an Idevice handle - */ -enum IdeviceErrorCode idevice_rsd_checkin(struct IdeviceHandle *idevice); - -/** - * Starts a TLS session - * - * # Arguments - * * [`idevice`] - The Idevice handle - * * [`pairing_file`] - The pairing file to use for TLS - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `idevice` must be a valid, non-null pointer to an Idevice handle - * `pairing_file` must be a valid, non-null pointer to a pairing file handle - */ -enum IdeviceErrorCode idevice_start_session(struct IdeviceHandle *idevice, - const struct IdevicePairingFile *pairing_file); - -/** - * Frees an Idevice handle - * - * # Arguments - * * [`idevice`] - The Idevice handle to free - * - * # Safety - * `idevice` must be a valid pointer to an Idevice handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void idevice_free(struct IdeviceHandle *idevice); - -/** - * Frees a string allocated by this library - * - * # Arguments - * * [`string`] - The string to free - * - * # Safety - * `string` must be a valid pointer to a string that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void idevice_string_free(char *string); - -/** - * Connects the adapter to a specific port - * - * # Arguments - * * [`handle`] - The adapter handle - * * [`port`] - The port to connect to - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode adapter_connect(struct AdapterHandle *handle, uint16_t port); - -/** - * Enables PCAP logging for the adapter - * - * # Arguments - * * [`handle`] - The adapter handle - * * [`path`] - The path to save the PCAP file (null-terminated string) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `path` must be a valid null-terminated string - */ -enum IdeviceErrorCode adapter_pcap(struct AdapterHandle *handle, const char *path); - -/** - * Closes the adapter connection - * - * # Arguments - * * [`handle`] - The adapter handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode adapter_close(struct AdapterHandle *handle); - -/** - * Sends data through the adapter - * - * # Arguments - * * [`handle`] - The adapter handle - * * [`data`] - The data to send - * * [`length`] - The length of the data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `data` must be a valid pointer to at least `length` bytes - */ -enum IdeviceErrorCode adapter_send(struct AdapterHandle *handle, - const uint8_t *data, - uintptr_t length); - -/** - * Receives data from the adapter - * - * # Arguments - * * [`handle`] - The adapter handle - * * [`data`] - Pointer to a buffer where the received data will be stored - * * [`length`] - Pointer to store the actual length of received data - * * [`max_length`] - Maximum number of bytes that can be stored in `data` - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `data` must be a valid pointer to at least `max_length` bytes - * `length` must be a valid pointer to a usize - */ -enum IdeviceErrorCode adapter_recv(struct AdapterHandle *handle, - uint8_t *data, - uintptr_t *length, - uintptr_t max_length); - -/** - * Connects to the AFC service using a TCP provider - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated AfcClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode afc_client_connect_tcp(struct TcpProviderHandle *provider, - struct AfcClientHandle **client); - -/** - * Connects to the AFC service using a Usbmuxd provider - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated AfcClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode afc_client_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct AfcClientHandle **client); - -/** - * Creates a new AfcClient from an existing Idevice connection - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated AfcClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode afc_client_new(struct IdeviceHandle *socket, struct AfcClientHandle **client); - -/** - * Frees an AfcClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void afc_client_free(struct AfcClientHandle *handle); - -/** - * Lists the contents of a directory on the device - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`path`] - Path to the directory to list (UTF-8 null-terminated) - * * [`entries`] - Will be set to point to an array of directory entries - * * [`count`] - Will be set to the number of entries - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - * `path` must be a valid null-terminated C string - */ -enum IdeviceErrorCode afc_list_directory(struct AfcClientHandle *client, - const char *path, - char ***entries, - size_t *count); - -/** - * Creates a new directory on the device - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`path`] - Path of the directory to create (UTF-8 null-terminated) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `path` must be a valid null-terminated C string - */ -enum IdeviceErrorCode afc_make_directory(struct AfcClientHandle *client, const char *path); - -/** - * Retrieves information about a file or directory - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`path`] - Path to the file or directory (UTF-8 null-terminated) - * * [`info`] - Will be populated with file information - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` and `path` must be valid pointers - * `info` must be a valid pointer to an AfcFileInfo struct - */ -enum IdeviceErrorCode afc_get_file_info(struct AfcClientHandle *client, - const char *path, - struct AfcFileInfo *info); - -/** - * Frees memory allocated by afc_get_file_info - * - * # Arguments - * * [`info`] - Pointer to AfcFileInfo struct to free - * - * # Safety - * `info` must be a valid pointer to an AfcFileInfo struct previously returned by afc_get_file_info - */ -void afc_file_info_free(struct AfcFileInfo *info); - -/** - * Retrieves information about the device's filesystem - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`info`] - Will be populated with device information - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` and `info` must be valid pointers - */ -enum IdeviceErrorCode afc_get_device_info(struct AfcClientHandle *client, - struct AfcDeviceInfo *info); - -/** - * Frees memory allocated by afc_get_device_info - * - * # Arguments - * * [`info`] - Pointer to AfcDeviceInfo struct to free - * - * # Safety - * `info` must be a valid pointer to an AfcDeviceInfo struct previously returned by afc_get_device_info - */ -void afc_device_info_free(struct AfcDeviceInfo *info); - -/** - * Removes a file or directory - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`path`] - Path to the file or directory to remove (UTF-8 null-terminated) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `path` must be a valid null-terminated C string - */ -enum IdeviceErrorCode afc_remove_path(struct AfcClientHandle *client, const char *path); - -/** - * Recursively removes a directory and all its contents - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`path`] - Path to the directory to remove (UTF-8 null-terminated) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `path` must be a valid null-terminated C string - */ -enum IdeviceErrorCode afc_remove_path_and_contents(struct AfcClientHandle *client, - const char *path); - -/** - * Opens a file on the device - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`path`] - Path to the file to open (UTF-8 null-terminated) - * * [`mode`] - File open mode - * * [`handle`] - Will be set to a new file handle on success - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - * `path` must be a valid null-terminated C string - */ -enum IdeviceErrorCode afc_file_open(struct AfcClientHandle *client, - const char *path, - enum AfcFopenMode mode, - struct AfcFileHandle **handle); - -/** - * Closes a file handle - * - * # Arguments - * * [`handle`] - File handle to close - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode afc_file_close(struct AfcFileHandle *handle); - -/** - * Reads data from an open file - * - * # Arguments - * * [`handle`] - File handle to read from - * * [`data`] - Will be set to point to the read data - * * [`length`] - Will be set to the length of the read data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - */ -enum IdeviceErrorCode afc_file_read(struct AfcFileHandle *handle, uint8_t **data, size_t *length); - -/** - * Writes data to an open file - * - * # Arguments - * * [`handle`] - File handle to write to - * * [`data`] - Data to write - * * [`length`] - Length of data to write - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - * `data` must point to at least `length` bytes - */ -enum IdeviceErrorCode afc_file_write(struct AfcFileHandle *handle, - const uint8_t *data, - size_t length); - -/** - * Creates a hard or symbolic link - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`target`] - Target path of the link (UTF-8 null-terminated) - * * [`source`] - Path where the link should be created (UTF-8 null-terminated) - * * [`link_type`] - Type of link to create - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - * `target` and `source` must be valid null-terminated C strings - */ -enum IdeviceErrorCode afc_make_link(struct AfcClientHandle *client, - const char *target, - const char *source, - enum AfcLinkType link_type); - -/** - * Renames a file or directory - * - * # Arguments - * * [`client`] - A valid AfcClient handle - * * [`source`] - Current path of the file/directory (UTF-8 null-terminated) - * * [`target`] - New path for the file/directory (UTF-8 null-terminated) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - * `source` and `target` must be valid null-terminated C strings - */ -enum IdeviceErrorCode afc_rename_path(struct AfcClientHandle *client, - const char *source, - const char *target); - -/** - * Automatically creates and connects to AMFI service, returning a client handle - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode amfi_connect_tcp(struct TcpProviderHandle *provider, - struct AmfiClientHandle **client); - -/** - * Automatically creates and connects to AMFI service, returning a client handle - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode amfi_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct AmfiClientHandle **client); - -/** - * Automatically creates and connects to AMFI service, returning a client handle - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode amfi_new(struct IdeviceHandle *socket, struct AmfiClientHandle **client); - -/** - * Shows the option in the settings UI - * - * # Arguments - * * `client` - A valid AmfiClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode amfi_reveal_developer_mode_option_in_ui(struct AmfiClientHandle *client); - -/** - * Enables developer mode on the device - * - * # Arguments - * * `client` - A valid AmfiClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode amfi_enable_developer_mode(struct AmfiClientHandle *client); - -/** - * Accepts developer mode on the device - * - * # Arguments - * * `client` - A valid AmfiClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode amfi_accept_developer_mode(struct AmfiClientHandle *client); - -/** - * Frees a handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void amfi_client_free(struct AmfiClientHandle *handle); - -/** - * Automatically creates and connects to Core Device Proxy, returning a client handle - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode core_device_proxy_connect_tcp(struct TcpProviderHandle *provider, - struct CoreDeviceProxyHandle **client); - -/** - * Automatically creates and connects to Core Device Proxy, returning a client handle - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode core_device_proxy_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct CoreDeviceProxyHandle **client); - -/** - * Automatically creates and connects to Core Device Proxy, returning a client handle - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode core_device_proxy_new(struct IdeviceHandle *socket, - struct CoreDeviceProxyHandle **client); - -/** - * Sends data through the CoreDeviceProxy tunnel - * - * # Arguments - * * [`handle`] - The CoreDeviceProxy handle - * * [`data`] - The data to send - * * [`length`] - The length of the data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `data` must be a valid pointer to at least `length` bytes - */ -enum IdeviceErrorCode core_device_proxy_send(struct CoreDeviceProxyHandle *handle, - const uint8_t *data, - uintptr_t length); - -/** - * Receives data from the CoreDeviceProxy tunnel - * - * # Arguments - * * [`handle`] - The CoreDeviceProxy handle - * * [`data`] - Pointer to a buffer where the received data will be stored - * * [`length`] - Pointer to store the actual length of received data - * * [`max_length`] - Maximum number of bytes that can be stored in `data` - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `data` must be a valid pointer to at least `max_length` bytes - * `length` must be a valid pointer to a usize - */ -enum IdeviceErrorCode core_device_proxy_recv(struct CoreDeviceProxyHandle *handle, - uint8_t *data, - uintptr_t *length, - uintptr_t max_length); - -/** - * Gets the client parameters from the handshake - * - * # Arguments - * * [`handle`] - The CoreDeviceProxy handle - * * [`mtu`] - Pointer to store the MTU value - * * [`address`] - Pointer to store the IP address string (must be at least 16 bytes) - * * [`netmask`] - Pointer to store the netmask string (must be at least 16 bytes) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `mtu` must be a valid pointer to a u16 - * `address` and `netmask` must be valid pointers to buffers of at least 16 bytes - */ -enum IdeviceErrorCode core_device_proxy_get_client_parameters(struct CoreDeviceProxyHandle *handle, - uint16_t *mtu, - char **address, - char **netmask); - -/** - * Gets the server address from the handshake - * - * # Arguments - * * [`handle`] - The CoreDeviceProxy handle - * * [`address`] - Pointer to store the server address string (must be at least 16 bytes) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `address` must be a valid pointer to a buffer of at least 16 bytes - */ -enum IdeviceErrorCode core_device_proxy_get_server_address(struct CoreDeviceProxyHandle *handle, - char **address); - -/** - * Gets the server RSD port from the handshake - * - * # Arguments - * * [`handle`] - The CoreDeviceProxy handle - * * [`port`] - Pointer to store the port number - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `port` must be a valid pointer to a u16 - */ -enum IdeviceErrorCode core_device_proxy_get_server_rsd_port(struct CoreDeviceProxyHandle *handle, - uint16_t *port); - -/** - * Creates a software TCP tunnel adapter - * - * # Arguments - * * [`handle`] - The CoreDeviceProxy handle - * * [`adapter`] - Pointer to store the newly created adapter handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library, and never used again - * `adapter` must be a valid pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode core_device_proxy_create_tcp_adapter(struct CoreDeviceProxyHandle *handle, - struct AdapterHandle **adapter); - -/** - * Frees a handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void core_device_proxy_free(struct CoreDeviceProxyHandle *handle); - -/** - * Frees a handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void adapter_free(struct AdapterHandle *handle); - -/** - * Creates a new DebugserverCommand - * - * # Safety - * Caller must free with debugserver_command_free - */ -struct DebugserverCommandHandle *debugserver_command_new(const char *name, - const char *const *argv, - uintptr_t argv_count); - -/** - * Frees a DebugserverCommand - * - * # Safety - * `command` must be a valid pointer or NULL - */ -void debugserver_command_free(struct DebugserverCommandHandle *command); - -/** - * Creates a new DebugProxyClient - * - * # Arguments - * * [`socket`] - The socket to use for communication - * * [`handle`] - Pointer to store the newly created DebugProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `handle` must be a valid pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode debug_proxy_adapter_new(struct AdapterHandle *socket, - struct DebugProxyAdapterHandle **handle); - -/** - * Frees a DebugProxyClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void debug_proxy_free(struct DebugProxyAdapterHandle *handle); - -/** - * Sends a command to the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * * [`command`] - The command to send - * * [`response`] - Pointer to store the response (caller must free) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` and `command` must be valid pointers - * `response` must be a valid pointer to a location where the string will be stored - */ -enum IdeviceErrorCode debug_proxy_send_command(struct DebugProxyAdapterHandle *handle, - struct DebugserverCommandHandle *command, - char **response); - -/** - * Reads a response from the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * * [`response`] - Pointer to store the response (caller must free) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer - * `response` must be a valid pointer to a location where the string will be stored - */ -enum IdeviceErrorCode debug_proxy_read_response(struct DebugProxyAdapterHandle *handle, - char **response); - -/** - * Sends raw data to the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * * [`data`] - The data to send - * * [`len`] - Length of the data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer - * `data` must be a valid pointer to `len` bytes - */ -enum IdeviceErrorCode debug_proxy_send_raw(struct DebugProxyAdapterHandle *handle, - const uint8_t *data, - uintptr_t len); - -/** - * Reads data from the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * * [`len`] - Maximum number of bytes to read - * * [`response`] - Pointer to store the response (caller must free) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer - * `response` must be a valid pointer to a location where the string will be stored - */ -enum IdeviceErrorCode debug_proxy_read(struct DebugProxyAdapterHandle *handle, - uintptr_t len, - char **response); - -/** - * Sets the argv for the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * * [`argv`] - NULL-terminated array of arguments - * * [`argv_count`] - Number of arguments - * * [`response`] - Pointer to store the response (caller must free) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer - * `argv` must be a valid pointer to `argv_count` C strings or NULL - * `response` must be a valid pointer to a location where the string will be stored - */ -enum IdeviceErrorCode debug_proxy_set_argv(struct DebugProxyAdapterHandle *handle, - const char *const *argv, - uintptr_t argv_count, - char **response); - -/** - * Sends an ACK to the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer - */ -enum IdeviceErrorCode debug_proxy_send_ack(struct DebugProxyAdapterHandle *handle); - -/** - * Sends a NACK to the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer - */ -enum IdeviceErrorCode debug_proxy_send_nack(struct DebugProxyAdapterHandle *handle); - -/** - * Sets the ACK mode for the debug proxy - * - * # Arguments - * * [`handle`] - The DebugProxyClient handle - * * [`enabled`] - Whether ACK mode should be enabled - * - * # Safety - * `handle` must be a valid pointer - */ -void debug_proxy_set_ack_mode(struct DebugProxyAdapterHandle *handle, int enabled); - -/** - * Returns the underlying socket from a DebugProxyClient - * - * # Arguments - * * [`handle`] - The handle to get the socket from - * * [`adapter`] - The newly allocated ConnectionHandle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again - */ -enum IdeviceErrorCode debug_proxy_adapter_into_inner(struct DebugProxyAdapterHandle *handle, - struct AdapterHandle **adapter); - -/** - * Automatically creates and connects to Installation Proxy, returning a client handle - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode heartbeat_connect_tcp(struct TcpProviderHandle *provider, - struct HeartbeatClientHandle **client); - -/** - * Automatically creates and connects to Installation Proxy, returning a client handle - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode heartbeat_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct HeartbeatClientHandle **client); - -/** - * Automatically creates and connects to Installation Proxy, returning a client handle - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode heartbeat_new(struct IdeviceHandle *socket, - struct HeartbeatClientHandle **client); - -/** - * Sends a polo to the device - * - * # Arguments - * * `client` - A valid HeartbeatClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode heartbeat_send_polo(struct HeartbeatClientHandle *client); - -/** - * Sends a polo to the device - * - * # Arguments - * * `client` - A valid HeartbeatClient handle - * * `interval` - The time to wait for a marco - * * `new_interval` - A pointer to set the requested marco - * - * # Returns - * An error code indicating success or failure. - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode heartbeat_get_marco(struct HeartbeatClientHandle *client, - uint64_t interval, - uint64_t *new_interval); - -/** - * Frees a handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void heartbeat_client_free(struct HeartbeatClientHandle *handle); - -/** - * Automatically creates and connects to Installation Proxy, returning a client handle - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode installation_proxy_connect_tcp(struct TcpProviderHandle *provider, - struct InstallationProxyClientHandle **client); - -/** - * Automatically creates and connects to Installation Proxy, returning a client handle - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode installation_proxy_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct InstallationProxyClientHandle **client); - -/** - * Automatically creates and connects to Installation Proxy, returning a client handle - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode installation_proxy_new(struct IdeviceHandle *socket, - struct InstallationProxyClientHandle **client); - -/** - * Gets installed apps on the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`application_type`] - The application type to filter by (optional, NULL for "Any") - * * [`bundle_identifiers`] - The identifiers to filter by (optional, NULL for all apps) - * * [`out_result`] - On success, will be set to point to a newly allocated array of PlistRef - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `out_result` must be a valid, non-null pointer to a location where the result will be stored - */ -enum IdeviceErrorCode installation_proxy_get_apps(struct InstallationProxyClientHandle *client, - const char *application_type, - const char *const *bundle_identifiers, - size_t bundle_identifiers_len, - void **out_result, - size_t *out_result_len); - -/** - * Frees a handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void installation_proxy_client_free(struct InstallationProxyClientHandle *handle); - -/** - * Installs an application package on the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`package_path`] - Path to the .ipa package in the AFC jail - * * [`options`] - Optional installation options as a plist dictionary (can be NULL) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `package_path` must be a valid C string - * `options` must be a valid plist dictionary or NULL - */ -enum IdeviceErrorCode installation_proxy_install(struct InstallationProxyClientHandle *client, - const char *package_path, - void *options); - -/** - * Installs an application package on the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`package_path`] - Path to the .ipa package in the AFC jail - * * [`options`] - Optional installation options as a plist dictionary (can be NULL) - * * [`callback`] - Progress callback function - * * [`context`] - User context to pass to callback - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `package_path` must be a valid C string - * `options` must be a valid plist dictionary or NULL - */ -enum IdeviceErrorCode installation_proxy_install_with_callback(struct InstallationProxyClientHandle *client, - const char *package_path, - void *options, - void (*callback)(uint64_t progress, - void *context), - void *context); - -/** - * Upgrades an existing application on the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`package_path`] - Path to the .ipa package in the AFC jail - * * [`options`] - Optional upgrade options as a plist dictionary (can be NULL) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `package_path` must be a valid C string - * `options` must be a valid plist dictionary or NULL - */ -enum IdeviceErrorCode installation_proxy_upgrade(struct InstallationProxyClientHandle *client, - const char *package_path, - void *options); - -/** - * Upgrades an existing application on the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`package_path`] - Path to the .ipa package in the AFC jail - * * [`options`] - Optional upgrade options as a plist dictionary (can be NULL) - * * [`callback`] - Progress callback function - * * [`context`] - User context to pass to callback - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `package_path` must be a valid C string - * `options` must be a valid plist dictionary or NULL - */ -enum IdeviceErrorCode installation_proxy_upgrade_with_callback(struct InstallationProxyClientHandle *client, - const char *package_path, - void *options, - void (*callback)(uint64_t progress, - void *context), - void *context); - -/** - * Uninstalls an application from the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`bundle_id`] - Bundle identifier of the application to uninstall - * * [`options`] - Optional uninstall options as a plist dictionary (can be NULL) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `bundle_id` must be a valid C string - * `options` must be a valid plist dictionary or NULL - */ -enum IdeviceErrorCode installation_proxy_uninstall(struct InstallationProxyClientHandle *client, - const char *bundle_id, - void *options); - -/** - * Uninstalls an application from the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`bundle_id`] - Bundle identifier of the application to uninstall - * * [`options`] - Optional uninstall options as a plist dictionary (can be NULL) - * * [`callback`] - Progress callback function - * * [`context`] - User context to pass to callback - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `bundle_id` must be a valid C string - * `options` must be a valid plist dictionary or NULL - */ -enum IdeviceErrorCode installation_proxy_uninstall_with_callback(struct InstallationProxyClientHandle *client, - const char *bundle_id, - void *options, - void (*callback)(uint64_t progress, - void *context), - void *context); - -/** - * Checks if the device capabilities match the required capabilities - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`capabilities`] - Array of plist values representing required capabilities - * * [`capabilities_len`] - Length of the capabilities array - * * [`options`] - Optional check options as a plist dictionary (can be NULL) - * * [`out_result`] - Will be set to true if all capabilities are supported, false otherwise - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `capabilities` must be a valid array of plist values or NULL - * `options` must be a valid plist dictionary or NULL - * `out_result` must be a valid pointer to a bool - */ -enum IdeviceErrorCode installation_proxy_check_capabilities_match(struct InstallationProxyClientHandle *client, - void *const *capabilities, - size_t capabilities_len, - void *options, - bool *out_result); - -/** - * Browses installed applications on the device - * - * # Arguments - * * [`client`] - A valid InstallationProxyClient handle - * * [`options`] - Optional browse options as a plist dictionary (can be NULL) - * * [`out_result`] - On success, will be set to point to a newly allocated array of PlistRef - * * [`out_result_len`] - Will be set to the length of the result array - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `options` must be a valid plist dictionary or NULL - * `out_result` must be a valid, non-null pointer to a location where the result will be stored - * `out_result_len` must be a valid, non-null pointer to a location where the length will be stored - */ -enum IdeviceErrorCode installation_proxy_browse(struct InstallationProxyClientHandle *client, - void *options, - void **out_result, - size_t *out_result_len); - -/** - * Creates a new ProcessControlClient from a RemoteServerClient - * - * # Arguments - * * [`server`] - The RemoteServerClient to use - * * [`handle`] - Pointer to store the newly created ProcessControlClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `server` must be a valid pointer to a handle allocated by this library - * `handle` must be a valid pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode location_simulation_new(struct RemoteServerAdapterHandle *server, - struct LocationSimulationAdapterHandle **handle); - -/** - * Frees a ProcessControlClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void location_simulation_free(struct LocationSimulationAdapterHandle *handle); - -/** - * Clears the location set - * - * # Arguments - * * [`handle`] - The LocationSimulation handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid or NULL where appropriate - */ -enum IdeviceErrorCode location_simulation_clear(struct LocationSimulationAdapterHandle *handle); - -/** - * Sets the location - * - * # Arguments - * * [`handle`] - The LocationSimulation handle - * * [`latitude`] - The latitude to set - * * [`longitude`] - The longitude to set - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid or NULL where appropriate - */ -enum IdeviceErrorCode location_simulation_set(struct LocationSimulationAdapterHandle *handle, - double latitude, - double longitude); - -/** - * Connects to lockdownd service using TCP provider - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode lockdownd_connect_tcp(struct TcpProviderHandle *provider, - struct LockdowndClientHandle **client); - -/** - * Connects to lockdownd service using Usbmuxd provider - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode lockdownd_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct LockdowndClientHandle **client); - -/** - * Creates a new LockdowndClient from an existing Idevice connection - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode lockdownd_new(struct IdeviceHandle *socket, - struct LockdowndClientHandle **client); - -/** - * Starts a session with lockdownd - * - * # Arguments - * * `client` - A valid LockdowndClient handle - * * `pairing_file` - An IdevicePairingFile alocated by this library - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `pairing_file` must be a valid plist_t containing a pairing file - */ -enum IdeviceErrorCode lockdownd_start_session(struct LockdowndClientHandle *client, - struct IdevicePairingFile *pairing_file); - -/** - * Starts a service through lockdownd - * - * # Arguments - * * `client` - A valid LockdowndClient handle - * * `identifier` - The service identifier to start (null-terminated string) - * * `port` - Pointer to store the returned port number - * * `ssl` - Pointer to store whether SSL should be enabled - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `identifier` must be a valid null-terminated string - * `port` and `ssl` must be valid pointers - */ -enum IdeviceErrorCode lockdownd_start_service(struct LockdowndClientHandle *client, - const char *identifier, - uint16_t *port, - bool *ssl); - -/** - * Gets a value from lockdownd - * - * # Arguments - * * `client` - A valid LockdowndClient handle - * * `key` - The value to get (null-terminated string) - * * `domain` - The value to get (null-terminated string) - * * `out_plist` - Pointer to store the returned plist value - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `value` must be a valid null-terminated string - * `out_plist` must be a valid pointer to store the plist - */ -enum IdeviceErrorCode lockdownd_get_value(struct LockdowndClientHandle *client, - const char *key, - const char *domain, - void **out_plist); - -/** - * Gets all values from lockdownd - * - * # Arguments - * * `client` - A valid LockdowndClient handle - * * `out_plist` - Pointer to store the returned plist dictionary - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `out_plist` must be a valid pointer to store the plist - */ -enum IdeviceErrorCode lockdownd_get_all_values(struct LockdowndClientHandle *client, - void **out_plist); - -/** - * Frees a LockdowndClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void lockdownd_client_free(struct LockdowndClientHandle *handle); - -/** - * Initializes the logger - * - * # Arguments - * * [`console_level`] - The level to log to the file - * * [`file_level`] - The level to log to the file - * * [`file_path`] - If not null, the file to write logs to - * - * ## Log Level - * 0. Disabled - * 1. Error - * 2. Warn - * 3. Info - * 4. Debug - * 5. Trace - * - * # Returns - * 0 for success, -1 if the file couldn't be created, -2 if a logger has been initialized, -3 for invalid path string - * - * # Safety - * Pass a valid CString for file_path. Pass valid log levels according to the enum - */ -enum IdeviceLoggerError idevice_init_logger(enum IdeviceLogLevel console_level, - enum IdeviceLogLevel file_level, - char *file_path); - -/** - * Automatically creates and connects to Misagent, returning a client handle - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode misagent_connect_tcp(struct TcpProviderHandle *provider, - struct MisagentClientHandle **client); - -/** - * Automatically creates and connects to Misagent, returning a client handle - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode misagent_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct MisagentClientHandle **client); - -/** - * Installs a provisioning profile on the device - * - * # Arguments - * * [`client`] - A valid MisagentClient handle - * * [`profile_data`] - The provisioning profile data to install - * * [`profile_len`] - Length of the profile data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `profile_data` must be a valid pointer to profile data of length `profile_len` - */ -enum IdeviceErrorCode misagent_install(struct MisagentClientHandle *client, - const uint8_t *profile_data, - size_t profile_len); - -/** - * Removes a provisioning profile from the device - * - * # Arguments - * * [`client`] - A valid MisagentClient handle - * * [`profile_id`] - The UUID of the profile to remove (C string) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `profile_id` must be a valid C string - */ -enum IdeviceErrorCode misagent_remove(struct MisagentClientHandle *client, const char *profile_id); - -/** - * Retrieves all provisioning profiles from the device - * - * # Arguments - * * [`client`] - A valid MisagentClient handle - * * [`out_profiles`] - On success, will be set to point to an array of profile data - * * [`out_profiles_len`] - On success, will be set to the number of profiles - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `out_profiles` must be a valid pointer to store the resulting array - * `out_profiles_len` must be a valid pointer to store the array length - */ -enum IdeviceErrorCode misagent_copy_all(struct MisagentClientHandle *client, - uint8_t ***out_profiles, - size_t **out_profiles_len, - size_t *out_count); - -/** - * Frees profiles array returned by misagent_copy_all - * - * # Arguments - * * [`profiles`] - Array of profile data pointers - * * [`lens`] - Array of profile lengths - * * [`count`] - Number of profiles in the array - * - * # Safety - * Must only be called with values returned from misagent_copy_all - */ -void misagent_free_profiles(uint8_t **profiles, size_t *lens, size_t count); - -/** - * Frees a misagent client handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library, - * or NULL (in which case this function does nothing) - */ -void misagent_client_free(struct MisagentClientHandle *handle); - -/** - * Connects to the Image Mounter service using a TCP provider - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode image_mounter_connect_tcp(struct TcpProviderHandle *provider, - struct ImageMounterHandle **client); - -/** - * Connects to the Image Mounter service using a Usbmuxd provider - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode image_mounter_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct ImageMounterHandle **client); - -/** - * Creates a new ImageMounter client from an existing Idevice connection - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode image_mounter_new(struct IdeviceHandle *socket, - struct ImageMounterHandle **client); - -/** - * Frees an ImageMounter handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void image_mounter_free(struct ImageMounterHandle *handle); - -/** - * Gets a list of mounted devices - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`devices`] - Will be set to point to a slice of device plists on success - * * [`devices_len`] - Will be set to the number of devices copied - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `devices` must be a valid, non-null pointer to a location where the plist will be stored - */ -enum IdeviceErrorCode image_mounter_copy_devices(struct ImageMounterHandle *client, - void **devices, - size_t *devices_len); - -/** - * Looks up an image and returns its signature - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`image_type`] - The type of image to look up - * * [`signature`] - Will be set to point to the signature data on success - * * [`signature_len`] - Will be set to the length of the signature data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `image_type` must be a valid null-terminated C string - * `signature` and `signature_len` must be valid pointers - */ -enum IdeviceErrorCode image_mounter_lookup_image(struct ImageMounterHandle *client, - const char *image_type, - uint8_t **signature, - size_t *signature_len); - -/** - * Uploads an image to the device - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`image_type`] - The type of image being uploaded - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`signature`] - Pointer to the signature data - * * [`signature_len`] - Length of the signature data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - * `image_type` must be a valid null-terminated C string - */ -enum IdeviceErrorCode image_mounter_upload_image(struct ImageMounterHandle *client, - const char *image_type, - const uint8_t *image, - size_t image_len, - const uint8_t *signature, - size_t signature_len); - -/** - * Mounts an image on the device - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`image_type`] - The type of image being mounted - * * [`signature`] - Pointer to the signature data - * * [`signature_len`] - Length of the signature data - * * [`trust_cache`] - Pointer to trust cache data (optional) - * * [`trust_cache_len`] - Length of trust cache data (0 if none) - * * [`info_plist`] - Pointer to info plist (optional) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid (except optional ones which can be null) - * `image_type` must be a valid null-terminated C string - */ -enum IdeviceErrorCode image_mounter_mount_image(struct ImageMounterHandle *client, - const char *image_type, - const uint8_t *signature, - size_t signature_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const void *info_plist); - -/** - * Unmounts an image from the device - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`mount_path`] - The path where the image is mounted - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `mount_path` must be a valid null-terminated C string - */ -enum IdeviceErrorCode image_mounter_unmount_image(struct ImageMounterHandle *client, - const char *mount_path); - -/** - * Queries the developer mode status - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`status`] - Will be set to the developer mode status (1 = enabled, 0 = disabled) - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `status` must be a valid pointer - */ -enum IdeviceErrorCode image_mounter_query_developer_mode_status(struct ImageMounterHandle *client, - int *status); - -/** - * Mounts a developer image - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`signature`] - Pointer to the signature data - * * [`signature_len`] - Length of the signature data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - */ -enum IdeviceErrorCode image_mounter_mount_developer(struct ImageMounterHandle *client, - const uint8_t *image, - size_t image_len, - const uint8_t *signature, - size_t signature_len); - -/** - * Queries the personalization manifest from the device - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`image_type`] - The type of image to query - * * [`signature`] - Pointer to the signature data - * * [`signature_len`] - Length of the signature data - * * [`manifest`] - Will be set to point to the manifest data on success - * * [`manifest_len`] - Will be set to the length of the manifest data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid and non-null - * `image_type` must be a valid null-terminated C string - */ -enum IdeviceErrorCode image_mounter_query_personalization_manifest(struct ImageMounterHandle *client, - const char *image_type, - const uint8_t *signature, - size_t signature_len, - uint8_t **manifest, - size_t *manifest_len); - -/** - * Queries the nonce from the device - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`personalized_image_type`] - The type of image to query (optional) - * * [`nonce`] - Will be set to point to the nonce data on success - * * [`nonce_len`] - Will be set to the length of the nonce data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client`, `nonce`, and `nonce_len` must be valid pointers - * `personalized_image_type` can be NULL - */ -enum IdeviceErrorCode image_mounter_query_nonce(struct ImageMounterHandle *client, - const char *personalized_image_type, - uint8_t **nonce, - size_t *nonce_len); - -/** - * Queries personalization identifiers from the device - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`image_type`] - The type of image to query (optional) - * * [`identifiers`] - Will be set to point to the identifiers plist on success - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` and `identifiers` must be valid pointers - * `image_type` can be NULL - */ -enum IdeviceErrorCode image_mounter_query_personalization_identifiers(struct ImageMounterHandle *client, - const char *image_type, - void **identifiers); - -/** - * Rolls the personalization nonce - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode image_mounter_roll_personalization_nonce(struct ImageMounterHandle *client); - -/** - * Rolls the cryptex nonce - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode image_mounter_roll_cryptex_nonce(struct ImageMounterHandle *client); - -/** - * Mounts a personalized developer image - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`provider`] - A valid provider handle - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`trust_cache`] - Pointer to the trust cache data - * * [`trust_cache_len`] - Length of the trust cache data - * * [`build_manifest`] - Pointer to the build manifest data - * * [`build_manifest_len`] - Length of the build manifest data - * * [`info_plist`] - Pointer to info plist (optional) - * * [`unique_chip_id`] - The device's unique chip ID - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid (except optional ones which can be null) - */ -enum IdeviceErrorCode image_mounter_mount_personalized_usbmuxd(struct ImageMounterHandle *client, - struct UsbmuxdProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id); - -/** - * Mounts a personalized developer image - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`provider`] - A valid provider handle - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`trust_cache`] - Pointer to the trust cache data - * * [`trust_cache_len`] - Length of the trust cache data - * * [`build_manifest`] - Pointer to the build manifest data - * * [`build_manifest_len`] - Length of the build manifest data - * * [`info_plist`] - Pointer to info plist (optional) - * * [`unique_chip_id`] - The device's unique chip ID - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid (except optional ones which can be null) - */ -enum IdeviceErrorCode image_mounter_mount_personalized_tcp(struct ImageMounterHandle *client, - struct TcpProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id); - -/** - * Mounts a personalized developer image with progress callback - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`provider`] - A valid provider handle - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`trust_cache`] - Pointer to the trust cache data - * * [`trust_cache_len`] - Length of the trust cache data - * * [`build_manifest`] - Pointer to the build manifest data - * * [`build_manifest_len`] - Length of the build manifest data - * * [`info_plist`] - Pointer to info plist (optional) - * * [`unique_chip_id`] - The device's unique chip ID - * * [`callback`] - Progress callback function - * * [`context`] - User context to pass to callback - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid (except optional ones which can be null) - */ -enum IdeviceErrorCode image_mounter_mount_personalized_usbmuxd_with_callback(struct ImageMounterHandle *client, - struct UsbmuxdProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id, - void (*callback)(size_t progress, - size_t total, - void *context), - void *context); - -/** - * Mounts a personalized developer image with progress callback - * - * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`provider`] - A valid provider handle - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`trust_cache`] - Pointer to the trust cache data - * * [`trust_cache_len`] - Length of the trust cache data - * * [`build_manifest`] - Pointer to the build manifest data - * * [`build_manifest_len`] - Length of the build manifest data - * * [`info_plist`] - Pointer to info plist (optional) - * * [`unique_chip_id`] - The device's unique chip ID - * * [`callback`] - Progress callback function - * * [`context`] - User context to pass to callback - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid (except optional ones which can be null) - */ -enum IdeviceErrorCode image_mounter_mount_personalized_tcp_with_callback(struct ImageMounterHandle *client, - struct TcpProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id, - void (*callback)(size_t progress, - size_t total, - void *context), - void *context); - -/** - * Reads a pairing file from the specified path - * - * # Arguments - * * [`path`] - Path to the pairing file - * * [`pairing_file`] - On success, will be set to point to a newly allocated pairing file instance - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `path` must be a valid null-terminated C string - * `pairing_file` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_pairing_file_read(const char *path, - struct IdevicePairingFile **pairing_file); - -/** - * Parses a pairing file from a byte buffer - * - * # Arguments - * * [`data`] - Pointer to the buffer containing pairing file data - * * [`size`] - Size of the buffer in bytes - * * [`pairing_file`] - On success, will be set to point to a newly allocated pairing file instance - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `data` must be a valid pointer to a buffer of at least `size` bytes - * `pairing_file` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_pairing_file_from_bytes(const uint8_t *data, - uintptr_t size, - struct IdevicePairingFile **pairing_file); - -/** - * Serializes a pairing file to XML format - * - * # Arguments - * * [`pairing_file`] - The pairing file to serialize - * * [`data`] - On success, will be set to point to a newly allocated buffer containing the serialized data - * * [`size`] - On success, will be set to the size of the allocated buffer - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `pairing_file` must be a valid, non-null pointer to a pairing file instance - * `data` must be a valid, non-null pointer to a location where the buffer pointer will be stored - * `size` must be a valid, non-null pointer to a location where the buffer size will be stored - */ -enum IdeviceErrorCode idevice_pairing_file_serialize(const struct IdevicePairingFile *pairing_file, - uint8_t **data, - uintptr_t *size); - -/** - * Frees a pairing file instance - * - * # Arguments - * * [`pairing_file`] - The pairing file to free - * - * # Safety - * `pairing_file` must be a valid pointer to a pairing file instance that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void idevice_pairing_file_free(struct IdevicePairingFile *pairing_file); - -/** - * Creates a new ProcessControlClient from a RemoteServerClient - * - * # Arguments - * * [`server`] - The RemoteServerClient to use - * * [`handle`] - Pointer to store the newly created ProcessControlClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `server` must be a valid pointer to a handle allocated by this library - * `handle` must be a valid pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode process_control_new(struct RemoteServerAdapterHandle *server, - struct ProcessControlAdapterHandle **handle); - -/** - * Frees a ProcessControlClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void process_control_free(struct ProcessControlAdapterHandle *handle); - -/** - * Launches an application on the device - * - * # Arguments - * * [`handle`] - The ProcessControlClient handle - * * [`bundle_id`] - The bundle identifier of the app to launch - * * [`env_vars`] - NULL-terminated array of environment variables (format "KEY=VALUE") - * * [`arguments`] - NULL-terminated array of arguments - * * [`start_suspended`] - Whether to start the app suspended - * * [`kill_existing`] - Whether to kill existing instances of the app - * * [`pid`] - Pointer to store the process ID of the launched app - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * All pointers must be valid or NULL where appropriate - */ -enum IdeviceErrorCode process_control_launch_app(struct ProcessControlAdapterHandle *handle, - const char *bundle_id, - const char *const *env_vars, - uintptr_t env_vars_count, - const char *const *arguments, - uintptr_t arguments_count, - bool start_suspended, - bool kill_existing, - uint64_t *pid); - -/** - * Kills a running process - * - * # Arguments - * * [`handle`] - The ProcessControlClient handle - * * [`pid`] - The process ID to kill - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode process_control_kill_app(struct ProcessControlAdapterHandle *handle, - uint64_t pid); - -/** - * Disables memory limits for a process - * - * # Arguments - * * [`handle`] - The ProcessControlClient handle - * * [`pid`] - The process ID to modify - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - */ -enum IdeviceErrorCode process_control_disable_memory_limit(struct ProcessControlAdapterHandle *handle, - uint64_t pid); - -/** - * Creates a TCP provider for idevice - * - * # Arguments - * * [`ip`] - The sockaddr IP to connect to - * * [`pairing_file`] - The pairing file handle to use - * * [`label`] - The label to use with the connection - * * [`provider`] - A pointer to a newly allocated provider - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `ip` must be a valid sockaddr - * `pairing_file` must never be used again - * `label` must be a valid Cstr - * `provider` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_tcp_provider_new(const struct sockaddr *ip, - struct IdevicePairingFile *pairing_file, - const char *label, - struct TcpProviderHandle **provider); - -/** - * Frees a TcpProvider handle - * - * # Arguments - * * [`provider`] - The provider handle to free - * - * # Safety - * `provider` must be a valid pointer to a TcpProvider handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void tcp_provider_free(struct TcpProviderHandle *provider); - -/** - * Creates a usbmuxd provider for idevice - * - * # Arguments - * * [`addr`] - The UsbmuxdAddr handle to connect to - * * [`tag`] - The tag returned in usbmuxd responses - * * [`udid`] - The UDID of the device to connect to - * * [`device_id`] - The muxer ID of the device to connect to - * * [`pairing_file`] - The pairing file handle to use - * * [`label`] - The label to use with the connection - * * [`provider`] - A pointer to a newly allocated provider - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `addr` must be a valid pointer to UsbmuxdAddrHandle created by this library, and never used again - * `udid` must be a valid CStr - * `pairing_file` must never be used again - * `label` must be a valid Cstr - * `provider` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode usbmuxd_provider_new(struct UsbmuxdAddrHandle *addr, - uint32_t tag, - const char *udid, - uint32_t device_id, - const char *label, - struct UsbmuxdProviderHandle **provider); - -/** - * Frees a UsbmuxdProvider handle - * - * # Arguments - * * [`provider`] - The provider handle to free - * - * # Safety - * `provider` must be a valid pointer to a UsbmuxdProvider handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void usbmuxd_provider_free(struct UsbmuxdProviderHandle *provider); - -/** - * Creates a new RemoteServerClient from a ReadWrite connection - * - * # Arguments - * * [`connection`] - The connection to use for communication - * * [`handle`] - Pointer to store the newly created RemoteServerClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `connection` must be a valid pointer to a handle allocated by this library - * `handle` must be a valid pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode remote_server_adapter_new(struct AdapterHandle *adapter, - struct RemoteServerAdapterHandle **handle); - -/** - * Frees a RemoteServerClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void remote_server_free(struct RemoteServerAdapterHandle *handle); - -/** - * Returns the underlying connection from a RemoteServerClient - * - * # Arguments - * * [`handle`] - The handle to get the connection from - * * [`connection`] - The newly allocated ConnectionHandle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again - */ -enum IdeviceErrorCode remote_server_adapter_into_inner(struct RemoteServerAdapterHandle *handle, - struct AdapterHandle **connection); - -/** - * Creates a new XPCDevice from an adapter - * - * # Arguments - * * [`adapter`] - The adapter to use for communication - * * [`device`] - Pointer to store the newly created XPCDevice handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `adapter` must be a valid pointer to a handle allocated by this library - * `device` must be a valid pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode xpc_device_new(struct AdapterHandle *adapter, - struct XPCDeviceAdapterHandle **device); - -/** - * Frees an XPCDevice handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void xpc_device_free(struct XPCDeviceAdapterHandle *handle); - -/** - * Gets a service by name from the XPCDevice - * - * # Arguments - * * [`handle`] - The XPCDevice handle - * * [`service_name`] - The name of the service to get - * * [`service`] - Pointer to store the newly created XPCService handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `service_name` must be a valid null-terminated C string - * `service` must be a valid pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode xpc_device_get_service(struct XPCDeviceAdapterHandle *handle, - const char *service_name, - struct XPCServiceHandle **service); - -/** - * Returns the adapter in the RemoteXPC Device - * - * # Arguments - * * [`handle`] - The handle to get the adapter from - * * [`adapter`] - The newly allocated AdapterHandle - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again - */ -enum IdeviceErrorCode xpc_device_adapter_into_inner(struct XPCDeviceAdapterHandle *handle, - struct AdapterHandle **adapter); - -/** - * Frees an XPCService handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void xpc_service_free(struct XPCServiceHandle *handle); - -/** - * Gets the list of available service names - * - * # Arguments - * * [`handle`] - The XPCDevice handle - * * [`names`] - Pointer to store the array of service names - * * [`count`] - Pointer to store the number of services - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `names` must be a valid pointer to a location where the array will be stored - * `count` must be a valid pointer to a location where the count will be stored - */ -enum IdeviceErrorCode xpc_device_get_service_names(struct XPCDeviceAdapterHandle *handle, - char ***names, - uintptr_t *count); - -/** - * Frees a list of service names - * - * # Arguments - * * [`names`] - The array of service names to free - * * [`count`] - The number of services in the array - * - * # Safety - * `names` must be a valid pointer to an array of `count` C strings - */ -void xpc_device_free_service_names(char **names, uintptr_t count); - -/** - * Connects to the Springboard service using a TCP provider - * - * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode springboard_services_connect_tcp(struct TcpProviderHandle *provider, - struct SpringBoardServicesClientHandle **client); - -/** - * Connects to the Springboard service using a usbmuxd provider - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode springboard_services_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct SpringBoardServicesClientHandle **client); - -/** - * Creates a new SpringBoardServices client from an existing Idevice connection - * - * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `socket` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode springboard_services_new(struct IdeviceHandle *socket, - struct SpringBoardServicesClientHandle **client); - -/** - * Gets the icon of the specified app by bundle identifier - * - * # Arguments - * * `client` - A valid SpringBoardServicesClient handle - * * `bundle_identifier` - The identifiers of the app to get icon - * * `out_result` - On success, will be set to point to a newly allocated png data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `out_result` must be a valid, non-null pointer to a location where the result will be stored - */ -enum IdeviceErrorCode springboard_services_get_icon(struct SpringBoardServicesClientHandle *client, - const char *bundle_identifier, - void **out_result, - size_t *out_result_len); - -/** - * Frees an SpringBoardServicesClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void springboard_services_free(struct SpringBoardServicesClientHandle *handle); - -/** - * Connects to a usbmuxd instance over TCP - * - * # Arguments - * * [`addr`] - The socket address to connect to - * * [`addr_len`] - Length of the socket - * * [`tag`] - A tag that will be returned by usbmuxd responses - * * [`usbmuxd_connection`] - On success, will be set to point to a newly allocated UsbmuxdConnection handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `addr` must be a valid sockaddr - * `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_usbmuxd_new_tcp_connection(const struct sockaddr *addr, - socklen_t addr_len, - uint32_t tag, - struct UsbmuxdConnectionHandle **usbmuxd_connection); - -/** - * Connects to a usbmuxd instance over unix socket - * - * # Arguments - * * [`addr`] - The socket path to connect to - * * [`tag`] - A tag that will be returned by usbmuxd responses - * * [`usbmuxd_connection`] - On success, will be set to point to a newly allocated UsbmuxdConnection handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `addr` must be a valid CStr - * `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_usbmuxd_new_unix_socket_connection(const char *addr, - uint32_t tag, - struct UsbmuxdConnectionHandle **usbmuxd_connection); - -/** - * Frees a UsbmuxdConnection handle - * - * # Arguments - * * [`usbmuxd_connection`] - The UsbmuxdConnection handle to free - * - * # Safety - * `usbmuxd_connection` must be a valid pointer to a UsbmuxdConnection handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void idevice_usbmuxd_connection_free(struct UsbmuxdConnectionHandle *usbmuxd_connection); - -/** - * Creates a usbmuxd TCP address struct - * - * # Arguments - * * [`addr`] - The socket address to connect to - * * [`addr_len`] - Length of the socket - * * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `addr` must be a valid sockaddr - * `usbmuxd_Addr` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_usbmuxd_tcp_addr_new(const struct sockaddr *addr, - socklen_t addr_len, - struct UsbmuxdAddrHandle **usbmuxd_addr); - -/** - * Creates a new UsbmuxdAddr struct with a unix socket - * - * # Arguments - * * [`addr`] - The socket path to connect to - * * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `addr` must be a valid CStr - * `usbmuxd_addr` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode idevice_usbmuxd_unix_addr_new(const char *addr, - struct UsbmuxdAddrHandle **usbmuxd_addr); - -/** - * Frees a UsbmuxdAddr handle - * - * # Arguments - * * [`usbmuxd_addr`] - The UsbmuxdAddr handle to free - * - * # Safety - * `usbmuxd_addr` must be a valid pointer to a UsbmuxdAddr handle that was allocated by this library, - * or NULL (in which case this function does nothing) - */ -void idevice_usbmuxd_addr_free(struct UsbmuxdAddrHandle *usbmuxd_addr); diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/plist.h b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/plist.h deleted file mode 100644 index 0e211278e..000000000 --- a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/plist.h +++ /dev/null @@ -1,1096 +0,0 @@ -/** - * @file plist/plist.h - * @brief Main include of libplist - * \internal - * - * Copyright (c) 2012-2023 Nikias Bassen, All Rights Reserved. - * Copyright (c) 2008-2009 Jonathan Beck, All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBPLIST_H -#define LIBPLIST_H - -#if _MSC_VER && _MSC_VER < 1700 - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - -#else -#include -#endif - - -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - -#ifdef __llvm__ - #if defined(__has_extension) - #if (__has_extension(attribute_deprecated_with_message)) - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x))) - #endif - #else - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated)) - #endif - #endif - #else - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated)) - #endif - #endif -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 5))) - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x))) - #endif -#elif defined(_MSC_VER) - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __declspec(deprecated(x)) - #endif -#else - #define PLIST_WARN_DEPRECATED(x) - #pragma message("WARNING: You need to implement DEPRECATED for this compiler") -#endif - - /** - * \mainpage libplist : A library to handle Apple Property Lists - * \defgroup PublicAPI Public libplist API - */ - /*@{*/ - - - /** - * The basic plist abstract data type. - */ - typedef void *plist_t; - - /** - * The plist dictionary iterator. - */ - typedef void* plist_dict_iter; - - /** - * The plist array iterator. - */ - typedef void* plist_array_iter; - - /** - * The enumeration of plist node types. - */ - typedef enum - { - PLIST_BOOLEAN, /**< Boolean, scalar type */ - PLIST_INT, /**< Integer, scalar type */ - PLIST_REAL, /**< Real, scalar type */ - PLIST_STRING, /**< ASCII string, scalar type */ - PLIST_ARRAY, /**< Ordered array, structured type */ - PLIST_DICT, /**< Unordered dictionary (key/value pair), structured type */ - PLIST_DATE, /**< Date, scalar type */ - PLIST_DATA, /**< Binary data, scalar type */ - PLIST_KEY, /**< Key in dictionaries (ASCII String), scalar type */ - PLIST_UID, /**< Special type used for 'keyed encoding' */ - PLIST_NULL, /**< NULL type */ - PLIST_NONE /**< No type */ - } plist_type; - - /* for backwards compatibility */ - #define PLIST_UINT PLIST_INT - - /** - * libplist error values - */ - typedef enum - { - PLIST_ERR_SUCCESS = 0, /**< operation successful */ - PLIST_ERR_INVALID_ARG = -1, /**< one or more of the parameters are invalid */ - PLIST_ERR_FORMAT = -2, /**< the plist contains nodes not compatible with the output format */ - PLIST_ERR_PARSE = -3, /**< parsing of the input format failed */ - PLIST_ERR_NO_MEM = -4, /**< not enough memory to handle the operation */ - PLIST_ERR_UNKNOWN = -255 /**< an unspecified error occurred */ - } plist_err_t; - - /******************************************** - * * - * Creation & Destruction * - * * - ********************************************/ - - /** - * Create a new root plist_t type #PLIST_DICT - * - * @return the created plist - * @sa #plist_type - */ - plist_t plist_new_dict(void); - - /** - * Create a new root plist_t type #PLIST_ARRAY - * - * @return the created plist - * @sa #plist_type - */ - plist_t plist_new_array(void); - - /** - * Create a new plist_t type #PLIST_STRING - * - * @param val the sting value, encoded in UTF8. - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_string(const char *val); - - /** - * Create a new plist_t type #PLIST_BOOLEAN - * - * @param val the boolean value, 0 is false, other values are true. - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_bool(uint8_t val); - - /** - * Create a new plist_t type #PLIST_INT with an unsigned integer value - * - * @param val the unsigned integer value - * @return the created item - * @sa #plist_type - * @note The value is always stored as uint64_t internally. - * Use #plist_get_uint_val or #plist_get_int_val to get the unsigned or signed value. - */ - plist_t plist_new_uint(uint64_t val); - - /** - * Create a new plist_t type #PLIST_INT with a signed integer value - * - * @param val the signed integer value - * @return the created item - * @sa #plist_type - * @note The value is always stored as uint64_t internally. - * Use #plist_get_uint_val or #plist_get_int_val to get the unsigned or signed value. - */ - plist_t plist_new_int(int64_t val); - - /** - * Create a new plist_t type #PLIST_REAL - * - * @param val the real value - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_real(double val); - - /** - * Create a new plist_t type #PLIST_DATA - * - * @param val the binary buffer - * @param length the length of the buffer - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_data(const char *val, uint64_t length); - - /** - * Create a new plist_t type #PLIST_DATE - * - * @param sec the number of seconds since 01/01/2001 - * @param usec the number of microseconds - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_date(int32_t sec, int32_t usec); - - /** - * Create a new plist_t type #PLIST_UID - * - * @param val the unsigned integer value - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_uid(uint64_t val); - - /** - * Create a new plist_t type #PLIST_NULL - * @return the created item - * @sa #plist_type - * @note This type is not valid for all formats, e.g. the XML format - * does not support it. - */ - plist_t plist_new_null(void); - - /** - * Destruct a plist_t node and all its children recursively - * - * @param plist the plist to free - */ - void plist_free(plist_t plist); - - /** - * Return a copy of passed node and it's children - * - * @param node the plist to copy - * @return copied plist - */ - plist_t plist_copy(plist_t node); - - - /******************************************** - * * - * Array functions * - * * - ********************************************/ - - /** - * Get size of a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @return size of the #PLIST_ARRAY node - */ - uint32_t plist_array_get_size(plist_t node); - - /** - * Get the nth item in a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @param n the index of the item to get. Range is [0, array_size[ - * @return the nth item or NULL if node is not of type #PLIST_ARRAY - */ - plist_t plist_array_get_item(plist_t node, uint32_t n); - - /** - * Get the index of an item. item must be a member of a #PLIST_ARRAY node. - * - * @param node the node - * @return the node index or UINT_MAX if node index can't be determined - */ - uint32_t plist_array_get_item_index(plist_t node); - - /** - * Set the nth item in a #PLIST_ARRAY node. - * The previous item at index n will be freed using #plist_free - * - * @param node the node of type #PLIST_ARRAY - * @param item the new item at index n. The array is responsible for freeing item when it is no longer needed. - * @param n the index of the item to get. Range is [0, array_size[. Assert if n is not in range. - */ - void plist_array_set_item(plist_t node, plist_t item, uint32_t n); - - /** - * Append a new item at the end of a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @param item the new item. The array is responsible for freeing item when it is no longer needed. - */ - void plist_array_append_item(plist_t node, plist_t item); - - /** - * Insert a new item at position n in a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @param item the new item to insert. The array is responsible for freeing item when it is no longer needed. - * @param n The position at which the node will be stored. Range is [0, array_size[. Assert if n is not in range. - */ - void plist_array_insert_item(plist_t node, plist_t item, uint32_t n); - - /** - * Remove an existing position in a #PLIST_ARRAY node. - * Removed position will be freed using #plist_free. - * - * @param node the node of type #PLIST_ARRAY - * @param n The position to remove. Range is [0, array_size[. Assert if n is not in range. - */ - void plist_array_remove_item(plist_t node, uint32_t n); - - /** - * Remove a node that is a child node of a #PLIST_ARRAY node. - * node will be freed using #plist_free. - * - * @param node The node to be removed from its #PLIST_ARRAY parent. - */ - void plist_array_item_remove(plist_t node); - - /** - * Create an iterator of a #PLIST_ARRAY node. - * The allocated iterator should be freed with the standard free function. - * - * @param node The node of type #PLIST_ARRAY - * @param iter Location to store the iterator for the array. - */ - void plist_array_new_iter(plist_t node, plist_array_iter *iter); - - /** - * Increment iterator of a #PLIST_ARRAY node. - * - * @param node The node of type #PLIST_ARRAY. - * @param iter Iterator of the array - * @param item Location to store the item. The caller must *not* free the - * returned item. Will be set to NULL when no more items are left - * to iterate. - */ - void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item); - - - /******************************************** - * * - * Dictionary functions * - * * - ********************************************/ - - /** - * Get size of a #PLIST_DICT node. - * - * @param node the node of type #PLIST_DICT - * @return size of the #PLIST_DICT node - */ - uint32_t plist_dict_get_size(plist_t node); - - /** - * Create an iterator of a #PLIST_DICT node. - * The allocated iterator should be freed with the standard free function. - * - * @param node The node of type #PLIST_DICT. - * @param iter Location to store the iterator for the dictionary. - */ - void plist_dict_new_iter(plist_t node, plist_dict_iter *iter); - - /** - * Increment iterator of a #PLIST_DICT node. - * - * @param node The node of type #PLIST_DICT - * @param iter Iterator of the dictionary - * @param key Location to store the key, or NULL. The caller is responsible - * for freeing the the returned string. - * @param val Location to store the value, or NULL. The caller must *not* - * free the returned value. Will be set to NULL when no more - * key/value pairs are left to iterate. - */ - void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val); - - /** - * Get key associated key to an item. Item must be member of a dictionary. - * - * @param node the item - * @param key a location to store the key. The caller is responsible for freeing the returned string. - */ - void plist_dict_get_item_key(plist_t node, char **key); - - /** - * Get the nth item in a #PLIST_DICT node. - * - * @param node the node of type #PLIST_DICT - * @param key the identifier of the item to get. - * @return the item or NULL if node is not of type #PLIST_DICT. The caller should not free - * the returned node. - */ - plist_t plist_dict_get_item(plist_t node, const char* key); - - /** - * Get key node associated to an item. Item must be member of a dictionary. - * - * @param node the item - * @return the key node of the given item, or NULL. - */ - plist_t plist_dict_item_get_key(plist_t node); - - /** - * Set item identified by key in a #PLIST_DICT node. - * The previous item identified by key will be freed using #plist_free. - * If there is no item for the given key a new item will be inserted. - * - * @param node the node of type #PLIST_DICT - * @param item the new item associated to key - * @param key the identifier of the item to set. - */ - void plist_dict_set_item(plist_t node, const char* key, plist_t item); - - /** - * Insert a new item into a #PLIST_DICT node. - * - * @deprecated Deprecated. Use plist_dict_set_item instead. - * - * @param node the node of type #PLIST_DICT - * @param item the new item to insert - * @param key The identifier of the item to insert. - */ - PLIST_WARN_DEPRECATED("use plist_dict_set_item instead") - void plist_dict_insert_item(plist_t node, const char* key, plist_t item); - - /** - * Remove an existing position in a #PLIST_DICT node. - * Removed position will be freed using #plist_free - * - * @param node the node of type #PLIST_DICT - * @param key The identifier of the item to remove. Assert if identifier is not present. - */ - void plist_dict_remove_item(plist_t node, const char* key); - - /** - * Merge a dictionary into another. This will add all key/value pairs - * from the source dictionary to the target dictionary, overwriting - * any existing key/value pairs that are already present in target. - * - * @param target pointer to an existing node of type #PLIST_DICT - * @param source node of type #PLIST_DICT that should be merged into target - */ - void plist_dict_merge(plist_t *target, plist_t source); - - - /******************************************** - * * - * Getters * - * * - ********************************************/ - - /** - * Get the parent of a node - * - * @param node the parent (NULL if node is root) - */ - plist_t plist_get_parent(plist_t node); - - /** - * Get the #plist_type of a node. - * - * @param node the node - * @return the type of the node - */ - plist_type plist_get_node_type(plist_t node); - - /** - * Get the value of a #PLIST_KEY node. - * This function does nothing if node is not of type #PLIST_KEY - * - * @param node the node - * @param val a pointer to a C-string. This function allocates the memory, - * caller is responsible for freeing it. - * @note Use plist_mem_free() to free the allocated memory. - */ - void plist_get_key_val(plist_t node, char **val); - - /** - * Get the value of a #PLIST_STRING node. - * This function does nothing if node is not of type #PLIST_STRING - * - * @param node the node - * @param val a pointer to a C-string. This function allocates the memory, - * caller is responsible for freeing it. Data is UTF-8 encoded. - * @note Use plist_mem_free() to free the allocated memory. - */ - void plist_get_string_val(plist_t node, char **val); - - /** - * Get a pointer to the buffer of a #PLIST_STRING node. - * - * @note DO NOT MODIFY the buffer. Mind that the buffer is only available - * until the plist node gets freed. Make a copy if needed. - * - * @param node The node - * @param length If non-NULL, will be set to the length of the string - * - * @return Pointer to the NULL-terminated buffer. - */ - const char* plist_get_string_ptr(plist_t node, uint64_t* length); - - /** - * Get the value of a #PLIST_BOOLEAN node. - * This function does nothing if node is not of type #PLIST_BOOLEAN - * - * @param node the node - * @param val a pointer to a uint8_t variable. - */ - void plist_get_bool_val(plist_t node, uint8_t * val); - - /** - * Get the unsigned integer value of a #PLIST_INT node. - * This function does nothing if node is not of type #PLIST_INT - * - * @param node the node - * @param val a pointer to a uint64_t variable. - */ - void plist_get_uint_val(plist_t node, uint64_t * val); - - /** - * Get the signed integer value of a #PLIST_INT node. - * This function does nothing if node is not of type #PLIST_INT - * - * @param node the node - * @param val a pointer to a int64_t variable. - */ - void plist_get_int_val(plist_t node, int64_t * val); - - /** - * Get the value of a #PLIST_REAL node. - * This function does nothing if node is not of type #PLIST_REAL - * - * @param node the node - * @param val a pointer to a double variable. - */ - void plist_get_real_val(plist_t node, double *val); - - /** - * Get the value of a #PLIST_DATA node. - * This function does nothing if node is not of type #PLIST_DATA - * - * @param node the node - * @param val a pointer to an unallocated char buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length the length of the buffer - * @note Use plist_mem_free() to free the allocated memory. - */ - void plist_get_data_val(plist_t node, char **val, uint64_t * length); - - /** - * Get a pointer to the data buffer of a #PLIST_DATA node. - * - * @note DO NOT MODIFY the buffer. Mind that the buffer is only available - * until the plist node gets freed. Make a copy if needed. - * - * @param node The node - * @param length Pointer to a uint64_t that will be set to the length of the buffer - * - * @return Pointer to the buffer - */ - const char* plist_get_data_ptr(plist_t node, uint64_t* length); - - /** - * Get the value of a #PLIST_DATE node. - * This function does nothing if node is not of type #PLIST_DATE - * - * @param node the node - * @param sec a pointer to an int32_t variable. Represents the number of seconds since 01/01/2001. - * @param usec a pointer to an int32_t variable. Represents the number of microseconds - */ - void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec); - - /** - * Get the value of a #PLIST_UID node. - * This function does nothing if node is not of type #PLIST_UID - * - * @param node the node - * @param val a pointer to a uint64_t variable. - */ - void plist_get_uid_val(plist_t node, uint64_t * val); - - - /******************************************** - * * - * Setters * - * * - ********************************************/ - - /** - * Set the value of a node. - * Forces type of node to #PLIST_KEY - * - * @param node the node - * @param val the key value - */ - void plist_set_key_val(plist_t node, const char *val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_STRING - * - * @param node the node - * @param val the string value. The string is copied when set and will be - * freed by the node. - */ - void plist_set_string_val(plist_t node, const char *val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_BOOLEAN - * - * @param node the node - * @param val the boolean value - */ - void plist_set_bool_val(plist_t node, uint8_t val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_INT - * - * @param node the node - * @param val the unsigned integer value - */ - void plist_set_uint_val(plist_t node, uint64_t val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_INT - * - * @param node the node - * @param val the signed integer value - */ - void plist_set_int_val(plist_t node, int64_t val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_REAL - * - * @param node the node - * @param val the real value - */ - void plist_set_real_val(plist_t node, double val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_DATA - * - * @param node the node - * @param val the binary buffer. The buffer is copied when set and will - * be freed by the node. - * @param length the length of the buffer - */ - void plist_set_data_val(plist_t node, const char *val, uint64_t length); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_DATE - * - * @param node the node - * @param sec the number of seconds since 01/01/2001 - * @param usec the number of microseconds - */ - void plist_set_date_val(plist_t node, int32_t sec, int32_t usec); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_UID - * - * @param node the node - * @param val the unsigned integer value - */ - void plist_set_uid_val(plist_t node, uint64_t val); - - - /******************************************** - * * - * Import & Export * - * * - ********************************************/ - - /** - * Export the #plist_t structure to XML format. - * - * @param plist the root node to export - * @param plist_xml a pointer to a C-string. This function allocates the memory, - * caller is responsible for freeing it. Data is UTF-8 encoded. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length); - - /** - * Export the #plist_t structure to binary format. - * - * @param plist the root node to export - * @param plist_bin a pointer to a char* buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length); - - /** - * Export the #plist_t structure to JSON format. - * - * @param plist the root node to export - * @param plist_json a pointer to a char* buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @param prettify pretty print the output if != 0 - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify); - - /** - * Export the #plist_t structure to OpenStep format. - * - * @param plist the root node to export - * @param plist_openstep a pointer to a char* buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @param prettify pretty print the output if != 0 - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_openstep(plist_t plist, char **plist_openstep, uint32_t* length, int prettify); - - - /** - * Import the #plist_t structure from XML format. - * - * @param plist_xml a pointer to the xml buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from binary format. - * - * @param plist_bin a pointer to the xml buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from JSON format. - * - * @param json a pointer to the JSON buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from OpenStep plist format. - * - * @param openstep a pointer to the OpenStep plist buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_openstep(const char *openstep, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from memory data. - * This method will look at the first bytes of plist_data - * to determine if plist_data contains a binary, JSON, or XML plist - * and tries to parse the data in the appropriate format. - * @note This is just a convenience function and the format detection is - * very basic. It checks with plist_is_binary() if the data supposedly - * contains binary plist data, if not it checks if the first byte is - * either '{' or '[' and assumes JSON format, otherwise it will try - * to parse the data as XML. - * - * @param plist_data a pointer to the memory buffer containing plist data. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist); - - /** - * Test if in-memory plist data is in binary format. - * This function will look at the first bytes of plist_data to determine - * if it supposedly contains a binary plist. - * @note The function is not validating the whole memory buffer to check - * if the content is truly a plist, it is only using some heuristic on - * the first few bytes of plist_data. - * - * @param plist_data a pointer to the memory buffer containing plist data. - * @param length length of the buffer to read. - * @return 1 if the buffer is a binary plist, 0 otherwise. - */ - int plist_is_binary(const char *plist_data, uint32_t length); - - /******************************************** - * * - * Utils * - * * - ********************************************/ - - /** - * Get a node from its path. Each path element depends on the associated father node type. - * For Dictionaries, var args are casted to const char*, for arrays, var args are caster to uint32_t - * Search is breath first order. - * - * @param plist the node to access result from. - * @param length length of the path to access - * @return the value to access. - */ - plist_t plist_access_path(plist_t plist, uint32_t length, ...); - - /** - * Variadic version of #plist_access_path. - * - * @param plist the node to access result from. - * @param length length of the path to access - * @param v list of array's index and dic'st key - * @return the value to access. - */ - plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v); - - /** - * Compare two node values - * - * @param node_l left node to compare - * @param node_r rigth node to compare - * @return TRUE is type and value match, FALSE otherwise. - */ - char plist_compare_node_value(plist_t node_l, plist_t node_r); - - #define _PLIST_IS_TYPE(__plist, __plist_type) (__plist && (plist_get_node_type(__plist) == PLIST_##__plist_type)) - - /* Helper macros for the different plist types */ - #define PLIST_IS_BOOLEAN(__plist) _PLIST_IS_TYPE(__plist, BOOLEAN) - #define PLIST_IS_INT(__plist) _PLIST_IS_TYPE(__plist, INT) - #define PLIST_IS_REAL(__plist) _PLIST_IS_TYPE(__plist, REAL) - #define PLIST_IS_STRING(__plist) _PLIST_IS_TYPE(__plist, STRING) - #define PLIST_IS_ARRAY(__plist) _PLIST_IS_TYPE(__plist, ARRAY) - #define PLIST_IS_DICT(__plist) _PLIST_IS_TYPE(__plist, DICT) - #define PLIST_IS_DATE(__plist) _PLIST_IS_TYPE(__plist, DATE) - #define PLIST_IS_DATA(__plist) _PLIST_IS_TYPE(__plist, DATA) - #define PLIST_IS_KEY(__plist) _PLIST_IS_TYPE(__plist, KEY) - #define PLIST_IS_UID(__plist) _PLIST_IS_TYPE(__plist, UID) - /* for backwards compatibility */ - #define PLIST_IS_UINT PLIST_IS_INT - - /** - * Helper function to check the value of a PLIST_BOOL node. - * - * @param boolnode node of type PLIST_BOOL - * @return 1 if the boolean node has a value of TRUE or 0 if FALSE. - */ - int plist_bool_val_is_true(plist_t boolnode); - - /** - * Helper function to test if a given #PLIST_INT node's value is negative - * - * @param intnode node of type PLIST_INT - * @return 1 if the node's value is negative, or 0 if positive. - */ - int plist_int_val_is_negative(plist_t intnode); - - /** - * Helper function to compare the value of a PLIST_INT node against - * a given signed integer value. - * - * @param uintnode node of type PLIST_INT - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_int_val_compare(plist_t uintnode, int64_t cmpval); - - /** - * Helper function to compare the value of a PLIST_INT node against - * a given unsigned integer value. - * - * @param uintnode node of type PLIST_INT - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval); - - /** - * Helper function to compare the value of a PLIST_UID node against - * a given value. - * - * @param uidnode node of type PLIST_UID - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval); - - /** - * Helper function to compare the value of a PLIST_REAL node against - * a given value. - * - * @note WARNING: Comparing floating point values can give inaccurate - * results because of the nature of floating point values on computer - * systems. While this function is designed to be as accurate as - * possible, please don't rely on it too much. - * - * @param realnode node of type PLIST_REAL - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are (almost) equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_real_val_compare(plist_t realnode, double cmpval); - - /** - * Helper function to compare the value of a PLIST_DATE node against - * a given set of seconds and fraction of a second since epoch. - * - * @param datenode node of type PLIST_DATE - * @param cmpsec number of seconds since epoch to compare against - * @param cmpusec fraction of a second in microseconds to compare against - * @return 0 if the node's date is equal to the supplied values, - * 1 if the node's date is greater than the supplied values, - * or -1 if the node's date is less than the supplied values. - */ - int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec); - - /** - * Helper function to compare the value of a PLIST_STRING node against - * a given value. - * This function basically behaves like strcmp. - * - * @param strnode node of type PLIST_STRING - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_string_val_compare(plist_t strnode, const char* cmpval); - - /** - * Helper function to compare the value of a PLIST_STRING node against - * a given value, while not comparing more than n characters. - * This function basically behaves like strncmp. - * - * @param strnode node of type PLIST_STRING - * @param cmpval value to compare against - * @param n maximum number of characters to compare - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n); - - /** - * Helper function to match a given substring in the value of a - * PLIST_STRING node. - * - * @param strnode node of type PLIST_STRING - * @param substr value to match - * @return 1 if the node's value contains the given substring, - * or 0 if not. - */ - int plist_string_val_contains(plist_t strnode, const char* substr); - - /** - * Helper function to compare the value of a PLIST_KEY node against - * a given value. - * This function basically behaves like strcmp. - * - * @param keynode node of type PLIST_KEY - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_key_val_compare(plist_t keynode, const char* cmpval); - - /** - * Helper function to compare the value of a PLIST_KEY node against - * a given value, while not comparing more than n characters. - * This function basically behaves like strncmp. - * - * @param keynode node of type PLIST_KEY - * @param cmpval value to compare against - * @param n maximum number of characters to compare - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n); - - /** - * Helper function to match a given substring in the value of a - * PLIST_KEY node. - * - * @param keynode node of type PLIST_KEY - * @param substr value to match - * @return 1 if the node's value contains the given substring, - * or 0 if not. - */ - int plist_key_val_contains(plist_t keynode, const char* substr); - - /** - * Helper function to compare the data of a PLIST_DATA node against - * a given blob and size. - * This function basically behaves like memcmp after making sure the - * size of the node's data value is equal to the size of cmpval (n), - * making this a "full match" comparison. - * - * @param datanode node of type PLIST_DATA - * @param cmpval data blob to compare against - * @param n size of data blob passed in cmpval - * @return 0 if the node's data blob and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n); - - /** - * Helper function to compare the data of a PLIST_DATA node against - * a given blob and size, while no more than n bytes are compared. - * This function basically behaves like memcmp after making sure the - * size of the node's data value is at least n, making this a - * "starts with" comparison. - * - * @param datanode node of type PLIST_DATA - * @param cmpval data blob to compare against - * @param n size of data blob passed in cmpval - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n); - - /** - * Helper function to match a given data blob within the value of a - * PLIST_DATA node. - * - * @param datanode node of type PLIST_KEY - * @param cmpval data blob to match - * @param n size of data blob passed in cmpval - * @return 1 if the node's value contains the given data blob - * or 0 if not. - */ - int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n); - - /** - * Free memory allocated by relevant libplist API calls: - * - plist_to_xml() - * - plist_to_bin() - * - plist_get_key_val() - * - plist_get_string_val() - * - plist_get_data_val() - * - * @param ptr pointer to the memory to free - * - * @note Do not use this function to free plist_t nodes, use plist_free() - * instead. - */ - void plist_mem_free(void* ptr); - - /*@}*/ - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT deleted file mode 100755 index 4b24b750d..000000000 Binary files a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT and /dev/null differ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/_CodeSignature/CodeResources b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/_CodeSignature/CodeResources deleted file mode 100644 index 8cc62f1fe..000000000 --- a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/_CodeSignature/CodeResources +++ /dev/null @@ -1,205 +0,0 @@ - - - - - files - - .DS_Store - - 7Mfr8shT4pXWBr/plN+uNkIabdM= - - Headers/StosJIT-Swift.h - - h9vaTwhC6FlnyKmIkaxLQGlFd1g= - - Headers/StosJIT.h - - ggHr5wlLNIIPydwUL9Vxm6abxjo= - - Headers/idevice.h - - mHDz7368FsBID56/epJ2NgIkha4= - - Headers/plist.h - - bL/f0MQDpLfvIcI1zxPwMuJ/PfI= - - Info.plist - - ZTTwPKlta/gjXAr1HIHmyAxeU4E= - - Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo - - nihJghwM5m7kxkQD7UvrWyHkLy8= - - Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json - - gcwBsH4BgyFY4sVtNt+/xOKS3vY= - - Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc - - YPtkDrAuBiPPEp4ZdRdBVlFXnRM= - - Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule - - 9cIInnjJzJFtY+CZm2iNo5qL3MQ= - - Modules/module.modulemap - - cnpvYzvLIwWcxkQodj5uLbHkyRk= - - - files2 - - Headers/StosJIT-Swift.h - - hash2 - - 1obIr4IjMvtcyNyYIV/Nh/5wahcA1cFjc4n4XVlNt2I= - - - Headers/StosJIT.h - - hash2 - - yY9KyrRdOYRdlb7G6wVMU2hogasXMjwV5r8jUIk44ok= - - - Headers/idevice.h - - hash2 - - zR9/TB9Dnv3uRC8qqGvaQ6c2yyOFUURmrHKLdEiUh/g= - - - Headers/plist.h - - hash2 - - yFbGsiXBBp91tfsSFtS0Utt2Gpc3MEDFiMVXKG9q1rs= - - - Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo - - hash2 - - +Ehvco7cQbAaF7zufvBYTiGXFp37Hjym/Pav514sGPk= - - - Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json - - hash2 - - Qnesa0n4URGWAopawg9bGx36dUwkYV00BoCJ8LFzlyg= - - - Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc - - hash2 - - k7F2Xs2hh9iMbK8IE8TMtN6gjQ9kWs30NUKHeupq6VE= - - - Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule - - hash2 - - gMDYNHcBPCNwZw2A5mEUiCyYAS9VhtQG0z+/WqAUrOQ= - - - Modules/module.modulemap - - hash2 - - FGwGKs5SNvpCyiIWiOP4eml9m2e3KISmtCJVtNnUnUc= - - - - rules - - ^.* - - ^.*\.lproj/ - - optional - - weight - 1000 - - ^.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Base\.lproj/ - - weight - 1010 - - ^version.plist$ - - - rules2 - - .*\.dSYM($|/) - - weight - 11 - - ^(.*/)?\.DS_Store$ - - omit - - weight - 2000 - - ^.* - - ^.*\.lproj/ - - optional - - weight - 1000 - - ^.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Base\.lproj/ - - weight - 1010 - - ^Info\.plist$ - - omit - - weight - 20 - - ^PkgInfo$ - - omit - - weight - 20 - - ^embedded\.provisionprofile$ - - weight - 20 - - ^version\.plist$ - - weight - 20 - - - - diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib index 8996b7ca5..f44d89585 100755 Binary files a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib differ diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs index 5cb4509ff..1a5802a37 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs @@ -81,14 +81,14 @@ namespace Ryujinx.Audio.Renderer.Dsp [MethodImpl(MethodImplOptions.AggressiveInlining)] private static short GetCoefficientAtIndex(ReadOnlySpan coefficients, int index) { - if ((uint)index > (uint)coefficients.Length) + if ((uint)index < (uint)coefficients.Length) { - Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}"); - - return 0; + return coefficients[index]; } - return coefficients[index]; + Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}"); + + return 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 246889c48..d6381a179 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -11,7 +11,7 @@ using Ryujinx.Audio.Renderer.Server.Sink; using Ryujinx.Audio.Renderer.Server.Splitter; using Ryujinx.Audio.Renderer.Server.Types; using Ryujinx.Audio.Renderer.Server.Upsampler; -using Ryujinx.Audio.Renderer.Server.Voice; +using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Utils; using Ryujinx.Common; using Ryujinx.Common.Logging; diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/DualMappedNoWxCache.cs similarity index 64% rename from src/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs rename to src/Ryujinx.Cpu/LightningJit/Cache/DualMappedNoWxCache.cs index 212ee2f8b..8d2dbd0fb 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/DualMappedNoWxCache.cs @@ -2,23 +2,17 @@ using ARMeilleure.Memory; using Ryujinx.Common; using Ryujinx.Memory; using System; -using System.Runtime.InteropServices; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace Ryujinx.Cpu.LightningJit.Cache { - class WriteZeroCache : IDisposable + class DualMappedNoWxCache : IDisposable { - private const int CodeAlignment = 4; - private const int InitialCacheSize = 2 * 1024 * 1024; - private const int GrowthCacheSize = 2 * 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); + private const int CodeAlignment = 4; // Bytes. + private const int SharedCacheSize = 512 * 1024 * 1024; + private const int LocalCacheSize = 128 * 1024 * 1024; // 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. @@ -26,104 +20,24 @@ namespace Ryujinx.Cpu.LightningJit.Cache private class MemoryCache : IDisposable { - private readonly ReservedRegion _region; + private readonly DualMappedJitAllocator _allocator; private readonly CacheMemoryAllocator _cacheAllocator; - public readonly IJitMemoryAllocator Allocator; - private readonly ulong _maxSize; - private ulong _currentSize; - - private readonly Dictionary> _reusePages; - private readonly object _reuselock = new object(); + public DualMappedJitAllocator Allocator => _allocator; + public IntPtr RwPointer => _allocator.RwPtr; + public IntPtr RxPointer => _allocator.RxPtr; public CacheMemoryAllocator CacheAllocator => _cacheAllocator; - public IntPtr Pointer => _region.Block.Pointer; - public ulong CurrentSize => _currentSize; - public ulong MaxSize => _maxSize; + public IntPtr Pointer => _allocator.RwPtr; - public MemoryCache(IJitMemoryAllocator allocator, ulong maxSize) + public MemoryCache(ulong size) { - Allocator = allocator; - _maxSize = maxSize; - _currentSize = InitialCacheSize; - - - _region = new(allocator, maxSize); - _cacheAllocator = new((int)maxSize); - - _reusePages = new Dictionary>(); - - _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(); - _reusePages[size] = offsets; - } - offsets.Add(offset); - } + _allocator = new DualMappedJitAllocator(size); + _cacheAllocator = new((int)size); } public int Allocate(int 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); @@ -132,63 +46,19 @@ namespace Ryujinx.Cpu.LightningJit.Cache 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; } 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()) { - JitSupportDarwin.SysIcacheInvalidate(_region.Block.Pointer + offset, size); + JitSupportDarwin.SysIcacheInvalidate(_allocator.RxPtr + offset, size); } else { @@ -196,14 +66,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache } } - public void ClearReusePool() - { - lock (_reuselock) - { - _reusePages.Clear(); - } - } - private static int AlignCodeSize(int codeSize) { return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1); @@ -213,8 +75,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache { if (disposing) { - ClearReusePool(); - _region.Dispose(); + _allocator.Dispose(); _cacheAllocator.Clear(); } } @@ -259,12 +120,12 @@ namespace Ryujinx.Cpu.LightningJit.Cache [ThreadStatic] private static Dictionary _threadLocalCache; - public WriteZeroCache(IJitMemoryAllocator allocator, IStackWalker stackWalker, Translator translator) + public DualMappedNoWxCache(IJitMemoryAllocator allocator, IStackWalker stackWalker, Translator translator) { _stackWalker = stackWalker; _translator = translator; - _sharedCaches = new List { new(allocator, MaxSharedCacheSize) }; - _localCaches = new List { new(allocator, MaxLocalCacheSize) }; + _sharedCaches = new List { new(SharedCacheSize) }; + _localCaches = new List { new(LocalCacheSize) }; _pendingMaps = new Dictionary(); _lock = new(); } @@ -275,7 +136,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache if (!_pendingMaps.TryGetValue(cacheKey, out var pendingMap)) { pendingMap = new PageAlignedRangeList( - (offset, size) => _sharedCaches[cacheIndex].ReprotectAsRx(offset, size), + (offset, size) => _sharedCaches[cacheIndex].SysIcacheInvalidate(offset, size), (address, func) => RegisterFunction(address, func)); _pendingMaps[cacheKey] = pendingMap; } @@ -304,15 +165,13 @@ namespace Ryujinx.Cpu.LightningJit.Cache } catch (OutOfMemoryException) { - // Try next cache } } - // All existing caches are full, create a new one lock (_lock) { 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); } } @@ -327,14 +186,14 @@ namespace Ryujinx.Cpu.LightningJit.Cache } catch (OutOfMemoryException) { - // Try next cache + } } lock (_lock) { 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); } } @@ -360,8 +219,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache MemoryCache cache = _sharedCaches[cacheIndex]; funcPtr = cache.Pointer + funcOffset; - code.CopyTo(new Span((void*)funcPtr, code.Length)); + funcPtr = cache.RxPointer + funcOffset; TranslatedFunction function = new(funcPtr, guestSize); @@ -396,21 +255,22 @@ namespace Ryujinx.Cpu.LightningJit.Cache Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0); IntPtr funcPtr1 = _sharedCaches[cacheIndex].Pointer + funcOffset; - code.CopyTo(new Span((void*)funcPtr1, code.Length)); + funcPtr1 = _sharedCaches[cacheIndex].RxPointer + funcOffset; - _sharedCaches[cacheIndex].ReprotectAsRx(funcOffset, sizeAligned); + _sharedCaches[cacheIndex].SysIcacheInvalidate(funcOffset, sizeAligned); return funcPtr1; } catch (OutOfMemoryException) { - // Try next cache } } + + var allocator = _sharedCaches[0].Allocator; - var newCache = new MemoryCache(allocator, MaxSharedCacheSize); + var newCache = new MemoryCache(SharedCacheSize); _sharedCaches.Add(newCache); cacheIndex = _sharedCaches.Count - 1; @@ -425,8 +285,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache IntPtr funcPtr = newCache.Pointer + funcOffset; code.CopyTo(new Span((void*)funcPtr, code.Length)); + funcPtr = newCache.RxPointer + funcOffset; - newCache.ReprotectAsRx(funcOffset, newSizeAligned); + newCache.SysIcacheInvalidate(funcOffset, newSizeAligned); return funcPtr; } @@ -481,8 +342,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache for (int i = 0; i < _localCaches.Count; i++) { - cachePointers[i] = _localCaches[i].Pointer; - cacheSizes[i] = (int)_localCaches[i].CurrentSize; + cachePointers[i] = _localCaches[i].RxPointer; + cacheSizes[i] = LocalCacheSize; } IntPtr[] sharedPointers = new IntPtr[_sharedCaches.Count]; @@ -490,19 +351,20 @@ namespace Ryujinx.Cpu.LightningJit.Cache for (int i = 0; i < _sharedCaches.Count; i++) { - sharedPointers[i] = _sharedCaches[i].Pointer; - sharedSizes[i] = (int)_sharedCaches[i].CurrentSize; + sharedPointers[i] = _sharedCaches[i].RxPointer; + sharedSizes[i] = SharedCacheSize; } + // Iterate over the arrays and pass each element to GetCallStack IEnumerable callStack = null; for (int i = 0; i < _localCaches.Count; i++) { callStack = _stackWalker.GetCallStack( framePointer, - cachePointers[i], - cacheSizes[i], - sharedPointers[i], - sharedSizes[i] + cachePointers[i], // Passing each individual cachePointer + cacheSizes[i], // Passing each individual cacheSize + sharedPointers[i], // Passing each individual sharedPointer + sharedSizes[i] // Passing each individual sharedSize ); } @@ -510,12 +372,16 @@ namespace Ryujinx.Cpu.LightningJit.Cache 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); if (!canDelete) { 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) { 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); _localCaches[cacheIndex].Free(offset, sizeAligned); - _localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned); } } + public void ClearEntireThreadLocalCache() { + // Thread is exiting, delete everything. + if (_threadLocalCache == null) { return; @@ -560,7 +428,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache var (cacheIndex, offset) = SplitCacheOffset(entry.Offset); _localCaches[cacheIndex].Free(offset, sizeAligned); - _localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned); } _threadLocalCache.Clear(); @@ -577,10 +444,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache IntPtr funcPtr = _localCaches[cacheIndex].Pointer + funcOffset; code.CopyTo(new Span((void*)funcPtr, code.Length)); + funcPtr = _localCaches[cacheIndex].RxPointer + funcOffset; (_threadLocalCache ??= new()).Add(guestAddress, new(funcOffset, code.Length, funcPtr, cacheIndex)); - _localCaches[cacheIndex].ReprotectAsRx(funcOffset, alignedSize); + _localCaches[cacheIndex].SysIcacheInvalidate(funcOffset, alignedSize); return funcPtr; } diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs index 06c81045d..a75116231 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs @@ -5,6 +5,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Cpu.LightningJit.Cache { [SupportedOSPlatform("macos")] + [SupportedOSPlatform("ios")] static partial class JitSupportDarwin { [LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")] diff --git a/src/Ryujinx.Cpu/LightningJit/Translator.cs b/src/Ryujinx.Cpu/LightningJit/Translator.cs index 85593036e..43e0110ab 100644 --- a/src/Ryujinx.Cpu/LightningJit/Translator.cs +++ b/src/Ryujinx.Cpu/LightningJit/Translator.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Cpu.LightningJit private readonly ConcurrentQueue> _oldFuncs; private readonly NoWxCache _noWxCache; - private readonly WriteZeroCache _writeZeroCache; + private readonly DualMappedNoWxCache _dualMappedCache; private bool _disposed; internal TranslatorCache Functions { get; } @@ -57,27 +57,14 @@ namespace Ryujinx.Cpu.LightningJit 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"); - if (content.Contains("22E5200s") && content.Contains("18.4") && content.Contains("Beta")) - { - // 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(); - FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); - Stubs = new TranslatorStubs(FunctionTable, _writeZeroCache); - } - else - { - _noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this); - Functions = new TranslatorCache(); - FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); - Stubs = new TranslatorStubs(FunctionTable, _noWxCache); - } + Console.WriteLine($"Dual Mapped JIT enabled."); + _dualMappedCache = new(new JitMemoryAllocator(), CreateStackWalker(), this); + Functions = new TranslatorCache(); + FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); + Stubs = new TranslatorStubs(FunctionTable, _dualMappedCache); } else { @@ -125,7 +112,7 @@ namespace Ryujinx.Cpu.LightningJit NativeInterface.UnregisterThread(); _noWxCache?.ClearEntireThreadLocalCache(); - _writeZeroCache?.ClearEntireThreadLocalCache(); + _dualMappedCache?.ClearEntireThreadLocalCache(); } internal IntPtr GetOrTranslatePointer(IntPtr framePointer, ulong address, ExecutionMode mode) @@ -135,10 +122,10 @@ namespace Ryujinx.Cpu.LightningJit CompiledFunction func = Compile(address, mode); return _noWxCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength); } - else if (_writeZeroCache != null) + else if (_dualMappedCache != null) { 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; @@ -239,9 +226,9 @@ namespace Ryujinx.Cpu.LightningJit { _noWxCache.Dispose(); } - else if (_writeZeroCache != null) + else if (_dualMappedCache != null) { - _writeZeroCache.Dispose(); + _dualMappedCache.Dispose(); } else { diff --git a/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs b/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs index 91f3ec4f1..6acb4dadf 100644 --- a/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs +++ b/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Cpu.LightningJit private readonly AddressTable _functionTable; private readonly NoWxCache _noWxCache; - private readonly WriteZeroCache _writeZeroCache; + private readonly DualMappedNoWxCache _dualMappedCache; private readonly GetFunctionAddressDelegate _getFunctionAddressRef; private readonly IntPtr _getFunctionAddress; @@ -101,12 +101,12 @@ namespace Ryujinx.Cpu.LightningJit /// Function table used to store pointers to the functions that the guest code will call /// Cache used on iOS versions that need a debugger to make a debug map /// is null - public TranslatorStubs(AddressTable functionTable, WriteZeroCache writeZeroCache) + public TranslatorStubs(AddressTable functionTable, DualMappedNoWxCache dualMappedCache) { ArgumentNullException.ThrowIfNull(functionTable); _functionTable = functionTable; - _writeZeroCache = writeZeroCache; + _dualMappedCache = dualMappedCache; _getFunctionAddressRef = NativeInterface.GetFunctionAddress; _getFunctionAddress = Marshal.GetFunctionPointerForDelegate(_getFunctionAddressRef); _slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true); @@ -131,7 +131,7 @@ namespace Ryujinx.Cpu.LightningJit { if (!_disposed) { - if (_noWxCache == null) + if (_noWxCache == null || _dualMappedCache == null) { if (_dispatchStub.IsValueCreated) { @@ -383,9 +383,9 @@ namespace Ryujinx.Cpu.LightningJit { return _noWxCache.MapPageAligned(code); } - else if (_writeZeroCache != null) + else if (_dualMappedCache != null) { - return _writeZeroCache.MapPageAligned(code); + return _dualMappedCache.MapPageAligned(code); } else { diff --git a/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs index 48f13c0cd..873791183 100644 --- a/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs +++ b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs @@ -127,11 +127,12 @@ namespace Ryujinx.Cpu.Signal 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.Reprotect(0, codeSizeAligned, MemoryPermission.ReadAndExecute); - return _codeBlock.Pointer; + return _codeBlock.RxPointer; } private static unsafe ref SignalHandlerConfig GetConfigRef() diff --git a/src/Ryujinx.Graphics.Vulkan/Constants.cs b/src/Ryujinx.Graphics.Vulkan/Constants.cs index 8103e2de8..ae02f65c7 100644 --- a/src/Ryujinx.Graphics.Vulkan/Constants.cs +++ b/src/Ryujinx.Graphics.Vulkan/Constants.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan public const int MaxShaderStages = 5; public const int MaxUniformBuffersPerStage = 18; public const int MaxStorageBuffersPerStage = 16; - public const int MaxTexturesPerStage = 31; + public const int MaxTexturesPerStage = 32; // 31 public const int MaxImagesPerStage = 16; public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages; public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index e7fb5f1f7..5a3225b34 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -680,6 +680,8 @@ namespace Ryujinx.Graphics.Vulkan ShaderCollection program = _program; + + // UpdateAndBindTexturesWithoutTemplate jas been renamed to UpdateAndBind and supports more then just textures. if (_dirty.HasFlag(DirtyFlags.Uniform)) { if (program.UsePushDescriptors) @@ -688,51 +690,77 @@ namespace Ryujinx.Graphics.Vulkan } else { - UpdateAndBind(cbs, program, PipelineBase.UniformSetIndex, pbp); + try { + 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)) { - UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp); + try { + 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 (true) + if (program.UpdateTexturesWithoutTemplate) { - try - { - UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp); - } - catch (Exception e) - { - UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp); - } + UpdateAndBind(cbs, PipelineBase.TextureSetIndex, pbp); } else { try { + UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp); } 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)) { - UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp); + try + { + 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) { // 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; @@ -764,7 +792,7 @@ namespace Ryujinx.Graphics.Vulkan if (info.Buffer.Handle == 0) { info.Buffer = dummyBuffer?.Get(cbs).Value ?? default; - // info.Offset = 0; + info.Offset = 0; info.Range = Vk.WholeSize; } @@ -962,9 +990,9 @@ namespace Ryujinx.Graphics.Vulkan } [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]; if (bindingSegments.Length == 0) @@ -972,6 +1000,24 @@ namespace Ryujinx.Graphics.Vulkan 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 { ImageView = _dummyTexture.GetImageView().Get(cbs).Value, @@ -979,89 +1025,160 @@ namespace Ryujinx.Graphics.Vulkan ImageLayout = ImageLayout.General }; - var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs); - foreach (ResourceBindingSegment segment in bindingSegments) { int binding = segment.Binding; int count = segment.Count; - if (!segment.IsArray) + if (setIndex == PipelineBase.UniformSetIndex) { - if (segment.Type != ResourceType.BufferTexture) + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) + int index = binding + i; + + if (_uniformSet.Set(index)) { - int index = binding + i; - ref var textureRef = ref _textureRefs[index]; + ref BufferRef buffer = ref _uniformBufferRefs[index]; - var imageView = textureRef.ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView; - var sampler = textureRef.Sampler?.Get(cbs).Value ?? dummyImageInfo.Sampler; + bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true); - var imageInfo = new DescriptorImageInfo + _uniformMirrored.Set(index, mirrored); + } + } + + ReadOnlySpan 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 storageBuffers = _storageBuffers; + dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer); + } + else if (setIndex == PipelineBase.TextureSetIndex) + { + if (!segment.IsArray) + { + if (segment.Type != ResourceType.BufferTexture) + { + for (int i = 0; i < count; i++) { - ImageView = imageView.Handle != 0 ? imageView : dummyImageInfo.ImageView, - Sampler = sampler.Handle != 0 ? sampler : dummyImageInfo.Sampler, - ImageLayout = ImageLayout.General - }; + int index = binding + i; + ref var textureRef = ref _textureRefs[index]; - dsc.UpdateImages(0, index, new[] { imageInfo }, DescriptorType.CombinedImageSampler); + var imageView = textureRef.ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView; + var sampler = textureRef.Sampler?.Get(cbs).Value ?? dummyImageInfo.Sampler; + + var imageInfo = new DescriptorImageInfo + { + ImageView = imageView.Handle != 0 ? imageView : dummyImageInfo.ImageView, + Sampler = sampler.Handle != 0 ? sampler : dummyImageInfo.Sampler, + ImageLayout = ImageLayout.General + }; + + dsc.UpdateImages(0, index, new[] { imageInfo }, DescriptorType.CombinedImageSampler); + } + } + else + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + var bufferView = _bufferTextureRefs[index]?.GetBufferView(cbs, false) ?? default(BufferView); + dsc.UpdateBufferImages(0, index, new[] { bufferView }, DescriptorType.UniformTexelBuffer); + } } } else { - for (int i = 0; i < count; i++) + var arrayRef = _textureArrayRefs[binding]; + + if (segment.Type != ResourceType.BufferTexture) { - int index = binding + i; - var bufferView = _bufferTextureRefs[index]?.GetBufferView(cbs, false) ?? default; - dsc.UpdateBufferImages(0, index, new[] { bufferView }, DescriptorType.UniformTexelBuffer); + var imageInfos = arrayRef.Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler); + if (imageInfos != null) + { + for (int i = 0; i < imageInfos.Length && i < count; i++) + { + dsc.UpdateImages(0, binding + i, new[] { imageInfos[i] }, DescriptorType.CombinedImageSampler); + } + } + else + { + for (int i = 0; i < count; i++) + { + dsc.UpdateImages(0, binding + i, new[] { dummyImageInfo }, DescriptorType.CombinedImageSampler); + } + } + } + else + { + var bufferViews = arrayRef.Array.GetBufferViews(cbs); + if (bufferViews != null) + { + for (int i = 0; i < bufferViews.Length && i < count; i++) + { + dsc.UpdateBufferImages(0, binding + i, new[] { bufferViews[i] }, DescriptorType.UniformTexelBuffer); + } + } + else + { + for (int i = 0; i < count; i++) + { + dsc.UpdateBufferImages(0, binding + i, new[] { default(BufferView) }, DescriptorType.UniformTexelBuffer); + } + } } } } - else + else if (setIndex == PipelineBase.ImageSetIndex) { - var arrayRef = _textureArrayRefs[binding]; - - if (segment.Type != ResourceType.BufferTexture) + if (segment.Type != ResourceType.BufferImage) { - var imageInfos = arrayRef.Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler); - if (imageInfos != null) + Span images = _images; + + for (int i = 0; i < count; i++) { - for (int i = 0; i < imageInfos.Length && i < count; i++) - { - dsc.UpdateImages(0, binding + i, new[] { imageInfos[i] }, DescriptorType.CombinedImageSampler); - } - } - else - { - for (int i = 0; i < count; i++) - { - dsc.UpdateImages(0, binding + i, new[] { dummyImageInfo }, DescriptorType.CombinedImageSampler); - } + images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView; } + + dsc.UpdateImages(0, binding, images[..count], DescriptorType.StorageImage); } else { - var bufferViews = arrayRef.Array.GetBufferViews(cbs); - if (bufferViews != null) + Span bufferImages = _bufferImages; + + for (int i = 0; i < count; i++) { - for (int i = 0; i < bufferViews.Length && i < count; i++) - { - dsc.UpdateBufferImages(0, binding + i, new[] { bufferViews[i] }, DescriptorType.UniformTexelBuffer); - } - } - else - { - for (int i = 0; i < count; i++) - { - dsc.UpdateBufferImages(0, binding + i, new[] { default(BufferView) }, DescriptorType.UniformTexelBuffer); - } + bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default; } + + dsc.UpdateBufferImages(0, binding, bufferImages[..count], DescriptorType.StorageTexelBuffer); } } } var sets = dsc.GetSets(); + _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); } @@ -1232,4 +1349,4 @@ namespace Ryujinx.Graphics.Vulkan Dispose(true); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/ImageArray.cs b/src/Ryujinx.Graphics.Vulkan/ImageArray.cs index 205be5aeb..019286d28 100644 --- a/src/Ryujinx.Graphics.Vulkan/ImageArray.cs +++ b/src/Ryujinx.Graphics.Vulkan/ImageArray.cs @@ -186,8 +186,6 @@ namespace Ryujinx.Graphics.Vulkan return sets; } - var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs); - DescriptorSetTemplate template = program.Templates[setIndex]; DescriptorSetTemplateWriter tu = templateUpdater.Begin(template); @@ -203,8 +201,6 @@ namespace Ryujinx.Graphics.Vulkan templateUpdater.Commit(_gd, device, sets[0]); - sets = dsc.GetSets(); - return sets; } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 85069c6b2..037dc3277 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Vulkan { 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; @@ -185,8 +185,8 @@ namespace Ryujinx.Graphics.Vulkan if (gd.Capabilities.SupportsMultiView) { - pipeline.ScissorsCount = Constants.MaxViewports; - pipeline.ViewportsCount = Constants.MaxViewports; + pipeline.ScissorsCount = (uint)Constants.MaxViewports; + pipeline.ViewportsCount = (uint)Constants.MaxViewports; } else { diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index d6125066a..c9aab4018 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -137,7 +137,7 @@ namespace Ryujinx.Graphics.Vulkan (IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages); // 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; _firstBackgroundUse = false; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 231d61718..56b532816 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -402,9 +402,7 @@ namespace Ryujinx.Graphics.Vulkan properties.Limits.FramebufferDepthSampleCounts & properties.Limits.FramebufferStencilSampleCounts; - bool isDynamicStateSupported = OperatingSystem.IsIOS() - ? OperatingSystem.IsIOSVersionAtLeast(17) && _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName) - : _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName); + bool isDynamicStateSupported = OperatingSystem.IsIOSVersionAtLeast(17) && _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName); Capabilities = new HardwareCapabilities( _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"), @@ -422,8 +420,8 @@ namespace Ryujinx.Graphics.Vulkan features2.Features.ShaderStorageImageMultisample, _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), isDynamicStateSupported, - features2.Features.MultiViewport && !IsMoltenVk, // Workaround for AMD on MoltenVK issue - !IsMoltenVk ? featuresRobustness2.NullDescriptor : false, + features2.Features.MultiViewport, // && !IsMoltenVk, // Workaround for AMD on MoltenVK issue + featuresRobustness2.NullDescriptor && !IsMoltenVk, supportsPushDescriptors && !IsMoltenVk, propertiesPushDescriptor.MaxPushDescriptors, featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, @@ -722,7 +720,7 @@ namespace Ryujinx.Graphics.Vulkan SystemMemoryType memoryType; - if (IsSharedMemory && !IsMoltenVk) + if (IsSharedMemory) { memoryType = SystemMemoryType.UnifiedMemory; } @@ -784,10 +782,10 @@ namespace Ryujinx.Graphics.Vulkan imageSetIndex: PipelineBase.ImageSetIndex, extraSetBaseIndex: PipelineBase.DescriptorSetLayouts, maximumExtraSets: Math.Max(0, (int)limits.MaxBoundDescriptorSets - PipelineBase.DescriptorSetLayouts), - maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, - maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, - maximumTexturesPerStage: Constants.MaxTexturesPerStage, - maximumImagesPerStage: Constants.MaxImagesPerStage, + maximumUniformBuffersPerStage: (uint)Constants.MaxUniformBuffersPerStage, + maximumStorageBuffersPerStage: (uint)Constants.MaxStorageBuffersPerStage, + maximumTexturesPerStage: (uint)Constants.MaxTexturesPerStage, + maximumImagesPerStage: (uint)Constants.MaxImagesPerStage, maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize, maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy, shaderSubgroupSize: (int)Capabilities.SubgroupSize, diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index 581a2906b..80695f69e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService _generalServiceDetail = new GeneralServiceDetail { ClientId = GeneralServiceManager.Count, - IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request? + IsAnyInternetRequestAccepted = false, // NOTE: Why not accept any internet request? }; NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler; diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index d04acc69c..3282385bb 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -992,15 +992,16 @@ namespace Ryujinx.Headless.SDL2 bool isNintendoStyle = true; // gamepadName.Contains("Nintendo") || gamepadName.Contains("Joycons"); ControllerType currentController; + if (index == PlayerIndex.Handheld) { currentController = ControllerType.Handheld; - } + } else if (gamepadName.Contains("Joycons") || gamepadName.Contains("Backbone")) { currentController = ControllerType.JoyconPair; - } - else + } + else { currentController = ControllerType.ProController; } diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index af0176dfc..33f0045c6 100644 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -48,7 +48,7 @@ - + diff --git a/src/Ryujinx.Memory/DualMappedJitAllocator.cs b/src/Ryujinx.Memory/DualMappedJitAllocator.cs new file mode 100644 index 000000000..cb425a1b0 --- /dev/null +++ b/src/Ryujinx.Memory/DualMappedJitAllocator.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using Ryujinx.Common.Logging; + +namespace Ryujinx.Memory +{ + /// + /// 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. + /// + 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() + { + + } + } +} diff --git a/src/Ryujinx.Memory/MemoryAllocationFlags.cs b/src/Ryujinx.Memory/MemoryAllocationFlags.cs index d8d1a83c4..1bee79c6f 100644 --- a/src/Ryujinx.Memory/MemoryAllocationFlags.cs +++ b/src/Ryujinx.Memory/MemoryAllocationFlags.cs @@ -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. /// Jit = 1 << 5, + + /// + /// 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. + /// + DualMapping = 1 << 6, } } diff --git a/src/Ryujinx.Memory/MemoryBlock.cs b/src/Ryujinx.Memory/MemoryBlock.cs index 3bb379ced..2f3715e9f 100644 --- a/src/Ryujinx.Memory/MemoryBlock.cs +++ b/src/Ryujinx.Memory/MemoryBlock.cs @@ -13,14 +13,21 @@ namespace Ryujinx.Memory private readonly bool _isMirror; private readonly bool _viewCompatible; private readonly bool _forJit; + private DualMappedJitAllocator _dualMappedAllocator; private IntPtr _sharedMemory; private IntPtr _pointer; + private IntPtr _rxPointer; /// - /// Pointer to the memory block data. + /// Pointer to the memory block data (RW). /// public IntPtr Pointer => _pointer; + /// + /// Pointer to the RX mapping (for execution), or IntPtr.Zero if not dual-mapped. + /// + public IntPtr RxPointer => _rxPointer; + /// /// Size of the memory block. /// @@ -35,7 +42,16 @@ namespace Ryujinx.Memory /// Throw when the current platform is not supported 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)); @@ -58,7 +74,7 @@ namespace Ryujinx.Memory _pointer = MemoryManagement.Allocate(size, _forJit); } - Size = size; + _rxPointer = _pointer; } /// @@ -165,7 +181,10 @@ namespace Ryujinx.Memory /// Throw when is invalid public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true) { - MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, throwOnFail); + if (_rxPointer == _pointer) + { + MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, throwOnFail); + } } /// @@ -388,8 +407,13 @@ namespace Ryujinx.Memory { IntPtr ptr = Interlocked.Exchange(ref _pointer, IntPtr.Zero); - // If pointer is null, the memory was already freed or never allocated. - if (ptr != IntPtr.Zero) + if (_dualMappedAllocator != null) + { + _dualMappedAllocator.Dispose(); + _dualMappedAllocator = null; + _rxPointer = IntPtr.Zero; + } + else if (ptr != IntPtr.Zero) { if (_usesSharedMemory) {