diff --git a/distribution/ios/compile.sh b/distribution/ios/compile.sh index 658f6210b..22aa34495 100755 --- a/distribution/ios/compile.sh +++ b/distribution/ios/compile.sh @@ -3,11 +3,13 @@ # Define the destination directory (hardcoded) DESTINATION_DIR="src/MeloNX/Dependencies/Dynamic\ Libraries/Ryujinx.Headless.SDL2.dylib" +dotnet clean + # Restore the project dotnet restore # Build the project with the specified version -dotnet build -c Release +# dotnet build -c Release # Publish the project with the specified runtime and settings dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true diff --git a/distribution/ios/get_dotnet.sh b/distribution/ios/get_dotnet.sh index f69969682..c5306e183 100755 --- a/distribution/ios/get_dotnet.sh +++ b/distribution/ios/get_dotnet.sh @@ -1,8 +1,7 @@ #!/bin/bash -XCCONFIG_FILE="${SRCROOT}/MeloNX.xcconfig" +# XCCONFIG_FILE="${SRCROOT}/MeloNX.xcconfig" -# Define the common paths to search for dotnet, including user-specific directories SEARCH_PATHS=( "/usr/local/share/dotnet" "/usr/local/bin" @@ -14,10 +13,10 @@ SEARCH_PATHS=( "$HOME/Developer" ) -# Initialize DOTNET_PATH as empty + + DOTNET_PATH="" -# Search in the defined paths for path in "${SEARCH_PATHS[@]}"; do if [ -d "$path" ]; then DOTNET_PATH=$(find "$path" -name dotnet -type f -print -quit 2>/dev/null) @@ -27,20 +26,8 @@ for path in "${SEARCH_PATHS[@]}"; do fi done -# Check if the path was found if [ -z "$DOTNET_PATH" ]; then - echo "Error: dotnet path not found." exit 1 fi -echo "dotnet path: $DOTNET_PATH" - -# Escape the path for sed -ESCAPED_PATH=$(echo "$DOTNET_PATH" | sed 's/\//\\\//g') - -# Update the xcconfig file -sed -i '' "s/^DOTNET = .*/DOTNET = $ESCAPED_PATH/g" "$XCCONFIG_FILE" - -$DOTNET_PATH clean - -echo "Updated MeloNX.xcconfig with DOTNET path: $DOTNET_PATH" +echo "$DOTNET_PATH" diff --git a/distribution/ios/xc-compile.sh b/distribution/ios/xc-compile.sh new file mode 100755 index 000000000..3075b8e94 --- /dev/null +++ b/distribution/ios/xc-compile.sh @@ -0,0 +1,21 @@ +dotnet_output=$(./distribution/ios/get_dotnet.sh) +exit_code=$? + +if [ $exit_code -eq 0 ]; then + dotnet="$dotnet_output" +else + echo "error: .NET not found, Please follow the compilation instructions on the gitea." >&2 + exit 1 +fi + +$dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true + +if [ $? -ne 0 ]; then + echo "warning: Compiling MeloNX failed! Running dotnet clean + restore then Retrying..." + + $dotnet clean + + $dotnet restore + + $dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true +fi diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index 9b28bed53..81b87add2 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -75,7 +75,7 @@ namespace ARMeilleure.Translation FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); Stubs = new TranslatorStubs(FunctionTable); - FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; + FunctionTable.Fill = (ulong)Stubs.DispatchStub; } public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) diff --git a/src/MeloNX/MeloNX.xcconfig b/src/MeloNX/MeloNX.xcconfig index 9b97df59a..f8e5e88f1 100644 --- a/src/MeloNX/MeloNX.xcconfig +++ b/src/MeloNX/MeloNX.xcconfig @@ -9,5 +9,3 @@ // https://help.apple.com/xcode/#/dev745c5c974 VERSION = 1.7.0 - -DOTNET = /usr/local/share/dotnet/dotnet diff --git a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj index ef8ce7fa6..92cc5f999 100644 --- a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj +++ b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj @@ -31,7 +31,7 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 4E2953AB2D803BC9000497CD /* PBXContainerItemProxy */ = { + 4E59B0A32DEA5CA9004BFF2A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 4E80A9852CD6F54500029585 /* Project object */; proxyType = 1; @@ -119,6 +119,10 @@ CodeSignOnCopy, RemoveHeadersOnCopy, ); + "Dependencies/Dynamic Libraries/StosJIT.framework" = ( + CodeSignOnCopy, + RemoveHeadersOnCopy, + ); "Dependencies/Dynamic Libraries/libMoltenVK.dylib" = ( CodeSignOnCopy, ); @@ -173,6 +177,7 @@ "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, @@ -257,12 +262,12 @@ /* Begin PBXLegacyTarget section */ BD43C61D2D1B23AB003BBC42 /* Ryujinx */ = { isa = PBXLegacyTarget; - buildArgumentsString = "publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true"; + buildArgumentsString = "./distribution/ios/xc-compile.sh"; buildConfigurationList = BD43C61E2D1B23AB003BBC42 /* Build configuration list for PBXLegacyTarget "Ryujinx" */; buildPhases = ( ); - buildToolPath = "$(DOTNET)"; - buildWorkingDirectory = "$(SRCROOT)/../.."; + buildToolPath = /bin/sh; + buildWorkingDirectory = "$(SRCROOT)/../../"; dependencies = ( ); name = Ryujinx; @@ -287,7 +292,7 @@ buildRules = ( ); dependencies = ( - 4E2953AC2D803BC9000497CD /* PBXTargetDependency */, + 4E59B0A42DEA5CA9004BFF2A /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 4E80A98F2CD6F54500029585 /* MeloNX */, @@ -473,11 +478,10 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 4E2953AC2D803BC9000497CD /* PBXTargetDependency */ = { + 4E59B0A42DEA5CA9004BFF2A /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilter = ios; target = BD43C6212D1B248D003BBC42 /* com.Stossy11.MeloNX.RyujinxAg */; - targetProxy = 4E2953AB2D803BC9000497CD /* PBXContainerItemProxy */; + targetProxy = 4E59B0A32DEA5CA9004BFF2A /* PBXContainerItemProxy */; }; 4E80A99F2CD6F54700029585 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -562,6 +566,7 @@ ONLY_ACTIVE_ARCH = NO; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -628,6 +633,7 @@ SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_DISABLE_SAFETY_CHECKS = YES; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only"; VALIDATE_PRODUCT = YES; }; @@ -643,7 +649,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 95J8WZ4TN8; ENABLE_PREVIEWS = YES; ENABLE_TESTABILITY = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -730,6 +736,36 @@ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(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; @@ -742,7 +778,7 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UIRequiresFullScreen = YES; + INFOPLIST_KEY_UIRequiresFullScreen = NO; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UISupportsDocumentBrowser = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -916,6 +952,51 @@ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", ); MARKETING_VERSION = "$(VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; @@ -1026,6 +1107,36 @@ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(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; @@ -1038,7 +1149,7 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UIRequiresFullScreen = YES; + INFOPLIST_KEY_UIRequiresFullScreen = NO; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UISupportsDocumentBrowser = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -1212,6 +1323,51 @@ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", ); MARKETING_VERSION = "$(VERSION)"; 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 6ae07b7b3..33228a56b 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/App/Core/Entitlements/EntitlementChecker.swift b/src/MeloNX/MeloNX/App/Core/Entitlements/EntitlementChecker.swift index 6883f7dfc..49ddf5b07 100644 --- a/src/MeloNX/MeloNX/App/Core/Entitlements/EntitlementChecker.swift +++ b/src/MeloNX/MeloNX/App/Core/Entitlements/EntitlementChecker.swift @@ -17,11 +17,20 @@ func SecTaskCopyValueForEntitlement( _ error: NSErrorPointer ) -> CFTypeRef? +@_silgen_name("SecTaskCopyTeamIdentifier") +func SecTaskCopyTeamIdentifier( + _ task: SecTaskRef, + _ error: NSErrorPointer +) -> NSString? + @_silgen_name("SecTaskCreateFromSelf") func SecTaskCreateFromSelf( _ allocator: CFAllocator? ) -> SecTaskRef? +@_silgen_name("CFRelease") +func CFRelease(_ cf: CFTypeRef) + @_silgen_name("SecTaskCopyValuesForEntitlements") func SecTaskCopyValuesForEntitlements( _ task: SecTaskRef, @@ -29,30 +38,43 @@ func SecTaskCopyValuesForEntitlements( _ error: UnsafeMutablePointer?>? ) -> CFDictionary? +func releaseSecTask(_ task: SecTaskRef) { + let cf = unsafeBitCast(task, to: CFTypeRef.self) + CFRelease(cf) +} + func checkAppEntitlements(_ ents: [String]) -> [String: Any] { guard let task = SecTaskCreateFromSelf(nil) else { - // print("Failed to create SecTask") return [:] } - + defer { + releaseSecTask(task) + } + guard let entitlements = SecTaskCopyValuesForEntitlements(task, ents as CFArray, nil) else { - // print("Failed to get entitlements") return [:] } - - return (entitlements as? [String: Any]) ?? [:] + + return (entitlements as NSDictionary) as? [String: Any] ?? [:] } func checkAppEntitlement(_ ent: String) -> Bool { guard let task = SecTaskCreateFromSelf(nil) else { - // print("Failed to create SecTask") return false } - - guard let entitlements = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else { - // print("Failed to get entitlements") + defer { + releaseSecTask(task) + } + + guard let entitlement = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else { return false } - - return entitlements.boolValue != nil && entitlements.boolValue + + if let number = entitlement as? NSNumber { + return number.boolValue + } else if let bool = entitlement as? Bool { + return bool + } + + return false } diff --git a/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h b/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h index 97835afee..f6bf2441c 100644 --- a/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h +++ b/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h @@ -14,6 +14,7 @@ #include #include +#include #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 ec328c68f..385457516 100644 --- a/src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift +++ b/src/MeloNX/MeloNX/App/Core/JIT/IsJITEnabled.swift @@ -20,6 +20,14 @@ func isJITEnabled() -> Bool { return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0 ? allocateTest() : false } +func checkDebugged() -> Bool { + var flags: Int = 0 + if checkAppEntitlement("dynamic-codesigning") { + return true + } + return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0 +} + func checkMemoryPermissions(at address: UnsafeRawPointer) -> Bool { var region: vm_address_t = vm_address_t(UInt(bitPattern: address)) var regionSize: vm_size_t = 0 @@ -54,6 +62,7 @@ func allocateTest() -> Bool { memcpy(jitMemory, code, code.count) + if mprotect(jitMemory, pageSize, PROT_READ | PROT_EXEC) != 0 { return false } diff --git a/src/MeloNX/MeloNX/App/Core/JIT/JitStreamerEB/EnableJIT.swift b/src/MeloNX/MeloNX/App/Core/JIT/JitStreamerEB/EnableJIT.swift index 23f747397..d1eecfda4 100644 --- a/src/MeloNX/MeloNX/App/Core/JIT/JitStreamerEB/EnableJIT.swift +++ b/src/MeloNX/MeloNX/App/Core/JIT/JitStreamerEB/EnableJIT.swift @@ -118,6 +118,7 @@ func presentAlert(title: String, message: String, completion: (() -> Void)? = ni } } + struct LaunchApp: Codable { let success: Bool let message: String diff --git a/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift b/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift index 509ab4c1f..6e34644ea 100644 --- a/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift +++ b/src/MeloNX/MeloNX/App/Core/JIT/StikJIT/StikEnableJIT.swift @@ -9,11 +9,55 @@ import Foundation import Network import UIKit -func enableJITStik() { - let bundleid = Bundle.main.bundleIdentifier ?? "Unknown" + + +func stikJITorStikDebug() -> Int { + let teamid = SecTaskCopyTeamIdentifier(SecTaskCreateFromSelf(nil)!, nil) - let address = URL(string: "stikjit://enable-jit?bundle-id=\(bundleid)")! - if UIApplication.shared.canOpenURL(address) { - UIApplication.shared.open(address) + if checkifappinstalled("com.stik.sj") { + return 1 // StikDebug + } + + if checkifappinstalled("com.stik.sj.\(String(teamid ?? ""))") { + return 2 // StikJIT + } + + return 0 // Not Found +} + + +func checkifappinstalled(_ id: String) -> Bool { + + guard let handle = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_LAZY) else { + if let error = dlerror() { + print(String(cString: error)) + } + return false + // fatalError("Failed to open dylib") + } + + typealias SBSLaunchApplicationWithIdentifierFunc = @convention(c) (CFString, Bool) -> Int32 + guard let sym = dlsym(handle, "SBSLaunchApplicationWithIdentifier") else { + if let error = dlerror() { + print(String(cString: error)) + } + dlclose(handle) + return false + } + + let bundleID: CFString = id as CFString + let suspended: Bool = false + + + let SBSLaunchApplicationWithIdentifier = unsafeBitCast(sym, to: SBSLaunchApplicationWithIdentifierFunc.self) + let result = SBSLaunchApplicationWithIdentifier(bundleID, suspended) + + return result == 9 +} + +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) } } diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Display/MemoryDisplay/MemoryUsageMonitor.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Display/MemoryDisplay/MemoryUsageMonitor.swift index c67422752..22cdebc25 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Display/MemoryDisplay/MemoryUsageMonitor.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Display/MemoryDisplay/MemoryUsageMonitor.swift @@ -13,7 +13,7 @@ class MemoryUsageMonitor: ObservableObject { private var timer: Timer? init() { - timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { [weak self] _ in self?.updateMemoryUsage() } } @@ -32,6 +32,7 @@ class MemoryUsageMonitor: ObservableObject { } if result == KERN_SUCCESS { + memoryUsage = 0 memoryUsage = taskInfo.phys_footprint } else { @@ -46,7 +47,6 @@ class MemoryUsageMonitor: ObservableObject { formatter.countStyle = .memory return formatter.string(fromByteCount: Int64(bytes)) } - } diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Display/PerformanceDisplay/PerformanceOverlay.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Display/PerformanceDisplay/PerformanceOverlay.swift deleted file mode 100644 index ac014ff4a..000000000 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Display/PerformanceDisplay/PerformanceOverlay.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Untitled.swift -// MeloNX -// -// Created by Stossy11 on 21/12/2024. -// - -import SwiftUI - -struct PerformanceOverlayView: View { - @StateObject private var memorymonitor = MemoryUsageMonitor() - - @StateObject private var fpsmonitor = FPSMonitor() - - var body: some View { - VStack { - Text("\(fpsmonitor.formatFPS())") - Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage)) - } - } -} - diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/MetalHUD/MTLHUD.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/MetalHUD/MTLHUD.swift index 7b7ec9bed..8985973a6 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/MetalHUD/MTLHUD.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/MetalHUD/MTLHUD.swift @@ -6,34 +6,28 @@ // import Foundation +import SwiftUI - -class MTLHud { - +class MTLHud: ObservableObject { @Published var canMetalHud: Bool = false - var isEnabled: Bool { - if let getenv = getenv("MTL_HUD_ENABLED") { - return String(cString: getenv).contains("1") + @AppStorage("MTL_HUD_ENABLED") var metalHudEnabled: Bool = false { + didSet { + if metalHudEnabled { + enable() + } else { + disable() + } } - return false } + static let shared = MTLHud() private init() { - let _ = openMetalDylib() // i'm fixing the warnings just because you said i suck at coding Autumn (propenchiefer, - https://youtu.be/tc65SNOTMz4 7:23) - if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") { - enable() - } else { - disable() - } - } - - func toggle() { - // print(UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED")) - if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") { + canMetalHud = openMetalDylib() // i'm fixing the warnings just because you said i suck at coding Autumn (propenchiefer, https://youtu.be/tc65SNOTMz4 7:23) + + if metalHudEnabled { enable() } else { disable() @@ -44,14 +38,8 @@ class MTLHud { let path = "/usr/lib/libMTLHud.dylib" if dlopen(path, RTLD_NOW) != nil { - // print("Library loaded from \(path)") - canMetalHud = true return true } else { - if let error = String(validatingUTF8: dlerror()) { - // print("Error loading library: \(error)") - } - canMetalHud = false return false } } diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift index 6efb4edfe..0064bfb63 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift @@ -10,6 +10,7 @@ import SwiftUI import GameController import MetalKit import Metal +import Darwin class LogCapture { static let shared = LogCapture() @@ -117,6 +118,11 @@ struct iOSNav: View { } } +func threadEntry(_ arg: () -> Void) -> UnsafeMutableRawPointer? { + arg() + return nil +} + class Ryujinx : ObservableObject { @Published var isRunning = false @@ -128,12 +134,12 @@ class Ryujinx : ObservableObject { @Published var isPortrait = false @Published var firmwareversion = "0" @Published var emulationUIView: MeloMTKView? = nil - @Published var config: Ryujinx.Configuration? = nil + @Published var config: Ryujinx.Arguments? = nil @Published var games: [Game] = [] @Published var defMLContentSize: CGFloat? - var thread: Thread = Thread { } + var thread: pthread_t? = nil @Published var jitenabled = false @@ -142,8 +148,8 @@ class Ryujinx : ObservableObject { } static let shared = Ryujinx() - - private init() { + + func addGames() { self.games = loadGames() } @@ -153,45 +159,63 @@ class Ryujinx : ObservableObject { cool() } } else { - thread = Thread { - cool() + // Box the closure + let boxed = Unmanaged.passRetained(ClosureBox(cool)).toOpaque() + + var thread: pthread_t? + let result = pthread_create(&thread, nil, { arg in + let unmanaged = Unmanaged.fromOpaque(arg) + let box = unmanaged.takeRetainedValue() + box.closure() + return nil + }, boxed) + + if result == 0 { + pthread_detach(thread!) + } else { + print("Failed to create thread: \(result)") + Unmanaged.fromOpaque(boxed).release() } - - thread.qualityOfService = .userInteractive - thread.name = "MeloNX" - thread.start() + } + } + + private class ClosureBox { + let closure: () -> Void + init(_ closure: @escaping () -> Void) { + self.closure = closure } } - public struct Configuration : Codable, Equatable { + public class Arguments : Observable, Codable, Equatable { var gamepath: String var inputids: [String] - var resscale: Float - var debuglogs: Bool - var tracelogs: Bool - var nintendoinput: Bool - var enableInternet: Bool - var listinputids: Bool - var aspectRatio: AspectRatio - var memoryManagerMode: String - var disableShaderCache: Bool - var hypervisor: Bool - var disableDockedMode: Bool - var enableTextureRecompression: Bool - var additionalArgs: [String] - var maxAnisotropy: Float - var macroHLE: Bool - var ignoreMissingServices: Bool - var expandRam: Bool - var dfsIntegrityChecks: Bool - var disablePTC: Bool - var disablevsync: Bool - var language: SystemLanguage - var regioncode: SystemRegionCode - var handHeldController: Bool + var resscale: Float = 1.0 + var debuglogs: Bool = false + var tracelogs: Bool = false + var nintendoinput: Bool = true + var enableInternet: Bool = false + var listinputids: Bool = false + var aspectRatio: AspectRatio = .fixed16x9 + var memoryManagerMode: String = "HostMappedUnsafe" + var disableShaderCache: Bool = false + var hypervisor: Bool = false + var disableDockedMode: Bool = false + var enableTextureRecompression: Bool = true + var additionalArgs: [String] = [] + var maxAnisotropy: Float = 1.0 + var macroHLE: Bool = true + var ignoreMissingServices: Bool = false + var expandRam: Bool = false + var dfsIntegrityChecks: Bool = false + var disablePTC: Bool = false + var disablevsync: Bool = false + var language: SystemLanguage = .americanEnglish + var regioncode: SystemRegionCode = .usa + var handHeldController: Bool = true + var backendMultithreading: Bool = true - - init(gamepath: String, + + init(gamepath: String = "", inputids: [String] = [], debuglogs: Bool = false, tracelogs: Bool = false, @@ -215,7 +239,8 @@ class Ryujinx : ObservableObject { disablevsync: Bool = false, language: SystemLanguage = .americanEnglish, regioncode: SystemRegionCode = .usa, - handHeldController: Bool = false + handHeldController: Bool = false, + backendMultithreading: Bool = true ) { self.gamepath = gamepath self.inputids = inputids @@ -242,11 +267,39 @@ class Ryujinx : ObservableObject { self.language = language self.regioncode = regioncode self.handHeldController = handHeldController + self.backendMultithreading = backendMultithreading + } + + + static func == (lhs: Arguments, rhs: Arguments) -> Bool { + return lhs.resscale == rhs.resscale && + lhs.debuglogs == rhs.debuglogs && + lhs.tracelogs == rhs.tracelogs && + lhs.nintendoinput == rhs.nintendoinput && + lhs.enableInternet == rhs.enableInternet && + lhs.listinputids == rhs.listinputids && + lhs.aspectRatio == rhs.aspectRatio && + lhs.memoryManagerMode == rhs.memoryManagerMode && + lhs.disableShaderCache == rhs.disableShaderCache && + lhs.hypervisor == rhs.hypervisor && + lhs.disableDockedMode == rhs.disableDockedMode && + lhs.enableTextureRecompression == rhs.enableTextureRecompression && + lhs.additionalArgs == rhs.additionalArgs && + lhs.maxAnisotropy == rhs.maxAnisotropy && + lhs.macroHLE == rhs.macroHLE && + lhs.ignoreMissingServices == rhs.ignoreMissingServices && + lhs.expandRam == rhs.expandRam && + lhs.dfsIntegrityChecks == rhs.dfsIntegrityChecks && + lhs.disablePTC == rhs.disablePTC && + lhs.disablevsync == rhs.disablevsync && + lhs.language == rhs.language && + lhs.regioncode == rhs.regioncode && + lhs.handHeldController == rhs.handHeldController } } - func start(with config: Configuration) throws { + func start(with config: Arguments) throws { guard !isRunning else { throw RyujinxError.alreadyRunning } @@ -254,6 +307,32 @@ class Ryujinx : ObservableObject { self.config = config + if UserDefaults.standard.bool(forKey: "lockInApp") { + let cool = Thread { + while true { + if UserDefaults.standard.bool(forKey: "lockInApp") { + if let workspaceClass = NSClassFromString("LSApplicationWorkspace") as? NSObject.Type, + let workspace = workspaceClass.perform(NSSelectorFromString("defaultWorkspace"))?.takeUnretainedValue() { + + let selector = NSSelectorFromString("openApplicationWithBundleID:") + + if workspace.responds(to: selector) { + workspace.perform(selector, with: Bundle.main.bundleIdentifier ?? "") + } else { + print("Selector not found or not responding.") + } + } else { + print("Could not get LSApplicationWorkspace class.") + } + } + } + } + + cool.qualityOfService = .userInteractive + cool.start() + } + + runloop { [self] in isRunning = true @@ -380,10 +459,12 @@ class Ryujinx : ObservableObject { isRunning = false + UserDefaults.standard.set(false, forKey: "lockInApp") + self.emulationUIView = nil self.metalLayer = nil - thread.cancel() + } @@ -431,10 +512,9 @@ class Ryujinx : ObservableObject { // print("Error loading games from roms folder: \(error)") return games } - } - private func buildCommandLineArgs(from config: Configuration) -> [String] { + private func buildCommandLineArgs(from config: Arguments) -> [String] { var args: [String] = [] // Add the game path @@ -476,6 +556,14 @@ class Ryujinx : ObservableObject { args.append(contentsOf: ["--aspect-ratio", config.aspectRatio.rawValue]) + // args.append(contentsOf: ["--system-timezone", TimeZone.current.identifier]) + + args.append(contentsOf: ["--system-time-offset", String(TimeZone.current.secondsFromGMT())]) + + if !config.backendMultithreading { + args.append(contentsOf: ["--backend-multithreading", "Off"]) + } + if config.nintendoinput { args.append("--correct-controller") 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 13c9829c0..733e53dfc 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/ControllerView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/ControllerView/ControllerView.swift @@ -45,23 +45,25 @@ struct ControllerView: View { HStack(spacing: 30) { VStack(spacing: 15) { ShoulderButtonsViewLeft() + .padding(.vertical) ZStack { JoystickController(showBackground: $hideDpad) - if !hideDpad { - DPadView() - .animation(.easeInOut(duration: 0.2), value: hideDpad) - } + DPadView() + .opacity(hideDpad ? 0 : 1) + .allowsHitTesting(!hideDpad) + .animation(.easeInOut(duration: 0.2), value: hideDpad) } } VStack(spacing: 15) { ShoulderButtonsViewRight() + .padding(.vertical) ZStack { JoystickController(iscool: true, showBackground: $hideABXY) - if !hideABXY { - ABXYView() - .animation(.easeInOut(duration: 0.2), value: hideABXY) - } + ABXYView() + .opacity(hideABXY ? 0 : 1) + .allowsHitTesting(!hideABXY) + .animation(.easeInOut(duration: 0.2), value: hideABXY) } } } @@ -90,12 +92,13 @@ struct ControllerView: View { HStack { VStack(spacing: 20) { ShoulderButtonsViewLeft() + .padding(.vertical) ZStack { JoystickController(showBackground: $hideDpad) - if !hideDpad { - DPadView() - .animation(.easeInOut(duration: 0.2), value: hideDpad) - } + DPadView() + .opacity(hideDpad ? 0 : 1) + .allowsHitTesting(!hideDpad) + .animation(.easeInOut(duration: 0.2), value: hideDpad) } } @@ -107,12 +110,13 @@ struct ControllerView: View { VStack(spacing: 20) { ShoulderButtonsViewRight() + .padding(.vertical) ZStack { JoystickController(iscool: true, showBackground: $hideABXY) - if !hideABXY { - ABXYView() - .animation(.easeInOut(duration: 0.2), value: hideABXY) - } + ABXYView() + .opacity(hideABXY ? 0 : 1) + .allowsHitTesting(!hideABXY) + .animation(.easeInOut(duration: 0.2), value: hideABXY) } } } @@ -282,7 +286,7 @@ struct ButtonView: View { .scaledToFit() .frame(width: size.width, height: size.height) .foregroundStyle(.white) - .opacity(isPressed ? 0.6 : 1.0) + .opacity(isPressed ? 0.6 : 0.8) .allowsHitTesting(false) } .frame(width: size.width, height: size.height) 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 3b455d0ac..281d83f5f 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 @@ -9,6 +9,8 @@ import SwiftUI struct Joystick: View { + @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 + @Binding var position: CGPoint @State var joystickSize: CGFloat var boundarySize: CGFloat @@ -17,6 +19,7 @@ struct Joystick: View { @Binding var showBackground: Bool let sensitivity: CGFloat = 1.5 + var dragGesture: some Gesture { DragGesture() @@ -56,12 +59,14 @@ struct Joystick: View { Circle() .fill(Color.clear.opacity(0)) .frame(width: boundarySize, height: boundarySize) + .scaleEffect(controllerScale) if showBackground { Circle() .fill(Color.gray.opacity(0.4)) .frame(width: boundarySize, height: boundarySize) .animation(.easeInOut(duration: 0.1), value: showBackground) + .scaleEffect(controllerScale) } Circle() @@ -74,6 +79,7 @@ struct Joystick: View { ) .offset(offset) .gesture(dragGesture) + .scaleEffect(controllerScale) } .frame(width: boundarySize, height: boundarySize) .onChange(of: showBackground) { newValue in 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 06a978cfc..32b791309 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 @@ -16,7 +16,7 @@ struct JoystickController: View { @State var position: CGPoint = CGPoint(x: 0, y: 0) var dragDiameter: CGFloat { var selfs = CGFloat(160) - selfs *= controllerScale + // selfs *= controllerScale if UIDevice.current.systemName.contains("iPadOS") { return selfs * 1.2 } @@ -28,9 +28,6 @@ struct JoystickController: View { VStack { Joystick(position: $position, joystickSize: dragDiameter * 0.2, boundarySize: dragDiameter, showBackground: $showBackground) .onChange(of: position) { newValue in - let scaledX = Float(newValue.x) - let scaledY = Float(newValue.y) // my dumbass broke this by having -y instead of y :/ - // print("Joystick Position: (\(scaledX), \(scaledY))") if iscool != nil { Ryujinx.shared.virtualController.thumbstickMoved(.right, x: newValue.x, y: newValue.y) 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 796154736..3118bbf52 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/EmulationView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/EmulationView.swift @@ -14,6 +14,10 @@ struct EmulationView: View { @AppStorage("showScreenShotButton") var ssb: Bool = false @AppStorage("showlogsgame") var showlogsgame: Bool = false + @AppStorage("On-ScreenControllerOpacity") var controllerOpacity: Double = 1.0 + + @AppStorage("disableTouch") var blackScreen = false + @State var isPresentedThree: Bool = false @State var isAirplaying = Air.shared.connected @Binding var startgame: Game? @@ -31,6 +35,11 @@ struct EmulationView: View { .onAppear { Air.play(AnyView(MetalView().ignoresSafeArea().edgesIgnoringSafeArea(.all))) } + + Color.black + .ignoresSafeArea() + .edgesIgnoringSafeArea(.all) + .allowsHitTesting(false) } else { MetalView() // The Emulation View .ignoresSafeArea() @@ -41,6 +50,8 @@ struct EmulationView: View { if isVCA { ControllerView() // Virtual Controller + .opacity(controllerOpacity) + .allowsHitTesting(true) } Group { 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 new file mode 100644 index 000000000..5573e1282 --- /dev/null +++ b/src/MeloNX/MeloNX/App/Views/Main/Emulation/EmulationView/PerformanceDisplay/PerformanceOverlay.swift @@ -0,0 +1,70 @@ +// +// Untitled.swift +// MeloNX +// +// Created by Stossy11 on 21/12/2024. +// + +import SwiftUI + +struct PerformanceOverlayView: View { + @StateObject private var memorymonitor = MemoryUsageMonitor() + + @StateObject private var fpsmonitor = FPSMonitor() + + 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) + } + } +} + +extension View { + func stroke(color: Color, width: CGFloat = 1) -> some View { + modifier(StrokeModifier(strokeSize: width, strokeColor: color)) + } +} + +struct StrokeModifier: ViewModifier { + private let id = UUID() + var strokeSize: CGFloat = 1 + var strokeColor: Color = .blue + + func body(content: Content) -> some View { + if strokeSize > 0 { + appliedStrokeBackground(content: content) + } else { + content + } + } + + private func appliedStrokeBackground(content: Content) -> some View { + content + .padding(strokeSize*2) + .background( + Rectangle() + .foregroundColor(strokeColor) + .mask(alignment: .center) { + mask(content: content) + } + ) + } + + func mask(content: Content) -> some View { + Canvas { context, size in + context.addFilter(.alphaThreshold(min: 0.01)) + if let resolvedView = context.resolveSymbol(id: id) { + context.draw(resolvedView, at: .init(x: size.width/2, y: size.height/2)) + } + } symbols: { + content + .tag(id) + .blur(radius: strokeSize) + } + } +} diff --git a/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift b/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift index f3f195d1f..b736e8d85 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/ContentView.swift @@ -11,6 +11,7 @@ import Darwin import UIKit import MetalKit import CoreLocation +import StosJIT struct MoltenVKSettings: Codable, Hashable { let string: String @@ -32,11 +33,16 @@ struct ContentView: View { @AppStorage("isVirtualController") var isVCA: Bool = true // Settings and Configuration - @State private var config: Ryujinx.Configuration + private var config: Ryujinx.Arguments { + settingsManager.config + } + + @StateObject private var settingsManager = SettingsManager.shared + @State var settings: [MoltenVKSettings] - @AppStorage("useTrollStore") var useTrollStore: Bool = false // JIT + @AppStorage("useTrollStore") var useTrollStore: Bool = false @AppStorage("jitStreamerEB") var jitStreamerEB: Bool = false @AppStorage("stikJIT") var stikJIT: Bool = false @@ -55,6 +61,9 @@ struct ContentView: View { private let animationDuration: Double = 1.0 @State private var isAnimating = false @State var isLoading = true + + + // MARK: - CORE @StateObject var ryujinx = Ryujinx.shared // MARK: - SDL @@ -62,14 +71,6 @@ struct ContentView: View { // MARK: - Initialization init() { - var defaultConfig = loadSettings() - if defaultConfig == nil { - saveSettings(config: .init(gamepath: "")) - defaultConfig = loadSettings() - } - - _config = State(initialValue: defaultConfig!) - let defaultSettings: [MoltenVKSettings] = [ MoltenVKSettings(string: "MVK_USE_METAL_PRIVATE_API", value: "1"), MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_PRIVATE_API", value: "1"), @@ -139,7 +140,6 @@ struct ContentView: View { private var mainMenuView: some View { MainTabView( startemu: $game, - config: $config, MVKconfig: $settings, controllersList: $controllersList, currentControllers: $currentControllers, @@ -155,6 +155,8 @@ struct ContentView: View { } + UserDefaults.standard.set(false, forKey: "lockInApp") + // print(MTLHud.shared.isEnabled) initControllerObservers() @@ -163,6 +165,11 @@ struct ContentView: View { ControllerListView(game: $game) )) + refreshControllersList() + + + ryujinx.addGames() + checkJitStatus() } .onOpenURL { url in @@ -373,6 +380,8 @@ struct ContentView: View { if jitStreamerEB { jitStreamerEB = false // byee jitstreamer eb } + + if !ryujinx.jitenabled { if useTrollStore { askForJIT() @@ -381,7 +390,12 @@ struct ContentView: View { } else if jitStreamerEB { enableJITEB() } else { - // print("no JIT") + if !allocateTest(), checkDebugged() { + loop_heartbeat() + sleep(5) + let cool = String(cString: attach(getpid())!) + print(cool) + } } } } @@ -389,13 +403,13 @@ struct ContentView: View { private func handleDeepLink(_ url: URL) { if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), components.host == "game" { - DispatchQueue.main.async { - 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 { - game = ryujinx.games.first(where: { $0.titleName == text }) - } + + 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 { + game = ryujinx.games.first(where: { $0.titleName == text }) } } } @@ -454,7 +468,6 @@ class LocationManager: NSObject, CLLocationManagerDelegate { } } - struct ControllerListView: View { @State private var selectedIndex = 0 @Binding var game: Game? 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 029b1ca63..c2357cb8a 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/GamesList/GameListView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/GamesList/GameListView.swift @@ -273,7 +273,7 @@ struct GameLibraryView: View { gameRequirements: $gameRequirements, gameInfo: $gameInfo ) - .padding(.horizontal, 3) + .padding(.horizontal) .padding(.vertical, 8) } } @@ -290,7 +290,7 @@ struct GameLibraryView: View { gameRequirements: $gameRequirements, gameInfo: $gameInfo ) - .padding(.horizontal, 3) + .padding(.horizontal) .padding(.vertical, 8) } } @@ -314,12 +314,19 @@ struct GameLibraryView: View { } else { Menu("Applets") { Button { - let game = Game(containerFolder: URL(string: "none")!, fileType: .item, fileURL: URL(string: "MiiMaker")!, titleName: "Mii Maker", titleId: "0", developer: "Nintendo", version: firmwareversion) + let game = Game(containerFolder: URL(string: "none")!, fileType: .item, fileURL: URL(string: "0x0100000000001009")!, titleName: "Mii Maker", titleId: "0", developer: "Nintendo", version: firmwareversion) self.startemu = game } label: { Label("Launch Mii Maker", systemImage: "person.crop.circle") } + Button { + let game = Game(containerFolder: URL(string: "none")!, fileType: .item, fileURL: URL(string: "0x0100000000001000")!, titleName: "Home Menu (Broken)", titleId: "0", developer: "Nintendo", version: firmwareversion) + self.startemu = game + } label: { + Label("Home Menu (Broken)", systemImage: "house.circle") + } + .foregroundStyle(.red) } } } @@ -658,6 +665,7 @@ struct GameCardView: View { @Binding var gameRequirements: [GameRequirements] @Binding var gameInfo: Game? @Environment(\.colorScheme) var colorScheme + let totalMemory = ProcessInfo.processInfo.physicalMemory var gameRequirement: GameRequirements? { gameRequirements.first(where: { $0.game_id == game.titleId }) @@ -729,7 +737,17 @@ struct GameCardView: View { .foregroundColor(.white) .padding(.horizontal, 6) .padding(.vertical, 2) - .background(Color.blue) + .background(req.memoryInt <= Int(String(format: "%.0f", Double(totalMemory) / 1_000_000_000)) ?? 0 ? Color.blue : Color.red) + .cornerRadius(4) + } + } else { + HStack(spacing: 4) { + Text("0GB") + .font(.system(size: 10, weight: .medium)) + .foregroundColor(.clear) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(Color.clear) .cornerRadius(4) } } @@ -796,16 +814,15 @@ struct GameListRow: View { HStack { Text(game.developer) - .font(.system(size: 14)) + .font(.system(size: 12)) .foregroundColor(.secondary) .multilineTextAlignment(.leading) if !game.version.isEmpty && game.version != "0" { - Text("•") - .foregroundColor(.secondary) + Divider().frame(width: 1, height: 15) Text("v\(game.version)") - .font(.system(size: 14)) + .font(.system(size: 10)) .foregroundColor(.secondary) } } @@ -820,7 +837,6 @@ struct GameListRow: View { let totalMemory = ProcessInfo.processInfo.physicalMemory HStack(spacing: 4) { - // Memory requirement badge Text(gameReq.device_memory) .font(.system(size: 11, weight: .medium)) .foregroundColor(.white) @@ -830,8 +846,11 @@ struct GameListRow: View { Capsule() .fill(gameReq.memoryInt <= Int(String(format: "%.0f", Double(totalMemory) / 1_000_000_000)) ?? 0 ? Color.blue : Color.red) ) - - // Compatibility badge + .lineLimit(1) + .truncationMode(.tail) + .fixedSize(horizontal: true, vertical: false) + .layoutPriority(1) + Text(gameReq.compatibility) .font(.system(size: 11, weight: .medium)) .foregroundColor(.white) @@ -841,6 +860,10 @@ struct GameListRow: View { Capsule() .fill(gameReq.color) ) + .lineLimit(1) + .truncationMode(.tail) + .fixedSize(horizontal: true, vertical: false) + .layoutPriority(1) } } 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 9e1c7303b..4d2844503 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/SettingsView/SettingsView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/SettingsView/SettingsView.swift @@ -7,9 +7,206 @@ import SwiftUI import SwiftSVG +import UIKit -struct SettingsView: View { - @Binding var config: Ryujinx.Configuration + +class SplitViewController: UISplitViewController { + private let sidebarViewController: UIViewController + private let contentViewController: UIViewController + + init(sidebarViewController: UIViewController, contentViewController: UIViewController) { + self.sidebarViewController = sidebarViewController + self.contentViewController = contentViewController + super.init(style: .doubleColumn) + + self.preferredDisplayMode = .oneBesideSecondary + self.preferredSplitBehavior = .tile + self.presentsWithGesture = true + + self.setViewController(sidebarViewController, for: .primary) + self.setViewController(contentViewController, for: .secondary) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + self.primaryBackgroundStyle = .sidebar + + let displayModeButtonItem = self.displayModeButtonItem + contentViewController.navigationItem.leftBarButtonItem = displayModeButtonItem + } + + func showSidebar() { + self.preferredDisplayMode = .oneBesideSecondary + } + + func hideSidebar() { + self.preferredDisplayMode = .secondaryOnly + } + + func toggleSidebar() { + if self.displayMode == .oneBesideSecondary { + self.preferredDisplayMode = .secondaryOnly + } else { + self.preferredDisplayMode = .oneBesideSecondary + } + } +} + +struct SidebarView: View { + var sidebar: () -> AnyView + var content: () -> Content + @Binding var showSidebar: Bool + + init(sidebar: @escaping () -> AnyView, content: @escaping () -> Content, showSidebar: Binding) { + self.sidebar = sidebar + self.content = content + self._showSidebar = showSidebar + } + + var body: some View { + SidebarViewRepresentable( + sidebar: sidebar(), + content: content(), + showSidebar: $showSidebar + ) + } +} + +struct SidebarViewRepresentable: UIViewControllerRepresentable { + var sidebar: Sidebar + var content: Content + @Binding var showSidebar: Bool + + func makeUIViewController(context: Context) -> SplitViewController { + let sidebarVC = UIHostingController(rootView: sidebar) + let contentVC = UINavigationController(rootViewController: UIHostingController(rootView: content)) + + let splitVC = SplitViewController(sidebarViewController: sidebarVC, contentViewController: contentVC) + splitVC.setOverrideTraitCollection( + UITraitCollection(horizontalSizeClass: .regular), + forChild: splitVC + ) + return splitVC + } + + func updateUIViewController(_ uiViewController: SplitViewController, context: Context) { + if let sidebarVC = uiViewController.viewController(for: .primary) as? UIHostingController { + sidebarVC.rootView = sidebar + } + if let navController = uiViewController.viewController(for: .secondary) as? UINavigationController, + let contentVC = navController.topViewController as? UIHostingController { + contentVC.rootView = content + } + + if showSidebar { + uiViewController.showSidebar() + } else { + uiViewController.hideSidebar() + } + } + + static func dismantleUIViewController(_ uiViewController: SplitViewController, coordinator: Coordinator) { + } +} + +class SettingsManager: ObservableObject { + @Published var config: Ryujinx.Arguments { + didSet { + debouncedSave() + } + } + + private var saveWorkItem: DispatchWorkItem?; + + public static var shared = SettingsManager() + + private init() { + self.config = SettingsManager.loadSettings() ?? Ryujinx.Arguments() + } + + func debouncedSave() { + saveWorkItem?.cancel() + + let workItem = DispatchWorkItem { [weak self] in + guard let self = self else { return } + self.saveSettings() + } + + saveWorkItem = workItem + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: workItem) + } + + func saveSettings() { + do { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let data = try encoder.encode(config) + + let fileURL = URL.documentsDirectory.appendingPathComponent("config.json") + + try data.write(to: fileURL) + print("Settings saved successfully") + } catch { + print("Failed to save settings: \(error)") + } + } + + static func loadSettings() -> Ryujinx.Arguments? { + do { + let fileURL = URL.documentsDirectory.appendingPathComponent("config.json") + + guard FileManager.default.fileExists(atPath: fileURL.path) else { + print("Config file does not exist, creating new config") + return nil + } + + let data = try Data(contentsOf: fileURL) + + let decoder = JSONDecoder() + let configs = try decoder.decode(Ryujinx.Arguments.self, from: data) + return configs + } catch { + print("Failed to load settings: \(error)") + return nil + } + } + + func loadSettings() { + do { + let fileURL = URL.documentsDirectory.appendingPathComponent("config.json") + + guard FileManager.default.fileExists(atPath: fileURL.path) else { + print("Config file does not exist, creating new config") + saveSettings() + return + } + + let data = try Data(contentsOf: fileURL) + + let decoder = JSONDecoder() + let configs = try decoder.decode(Ryujinx.Arguments.self, from: data) + + self.config = configs + } catch { + print("Failed to load settings: \(error)") + } + } +} + + + +struct SettingsViewNew: View { + @StateObject private var settingsManager = SettingsManager.shared + + private var config: Binding { + $settingsManager.config + } + @Binding var MoltenVKSettings: [MoltenVKSettings] @Binding var controllersList: [Controller] @@ -30,8 +227,7 @@ struct SettingsView: View { ] @AppStorage("RyuDemoControls") var ryuDemo: Bool = false - @AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false - + @AppStorage("showScreenShotButton") var ssb: Bool = false @AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: Bool = false @@ -45,12 +241,16 @@ struct SettingsView: View { @AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0 + @AppStorage("On-ScreenControllerOpacity") var controllerOpacity: Double = 1.0 + @AppStorage("hasbeenfinished") var finishedStorage: Bool = false @AppStorage("showlogsloading") var showlogsloading: Bool = true @AppStorage("showlogsgame") var showlogsgame: Bool = false + @AppStorage("toggleGreen") var toggleGreen: Bool = false + @AppStorage("stick-button") var stickButton = false @AppStorage("waitForVPN") var waitForVPN = false @@ -60,10 +260,17 @@ struct SettingsView: View { @AppStorage("disableTouch") var disableTouch = false + @AppStorage("disableTouch") var blackScreen = false + + @AppStorage("location-enabled") var locationenabled: Bool = false + @AppStorage("runOnMainThread") var runOnMainThread = false @AppCodableStorage("toggleButtons") var toggleButtons = ToggleButtonsState() - + + let totalMemory = ProcessInfo.processInfo.physicalMemory + + @AppStorage("lockInApp") var restartApp = false @State private var showResolutionInfo = false @State private var showAnisotropicInfo = false @@ -77,11 +284,26 @@ struct SettingsView: View { @State private var selectedCategory: SettingsCategory = .graphics + @StateObject var metalHudEnabler = MTLHud.shared + var filteredMemoryModes: [(String, String)] { guard !searchText.isEmpty else { return memoryManagerModes } return memoryManagerModes.filter { $0.1.localizedCaseInsensitiveContains(searchText) } } + var appVersion: String { + guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { + return "Unknown" + } + return version + } + + @FocusState private var isArgumentsKeyboardVisible: Bool + + + @State private var selectedView = "Data Management" + @State private var sidebar = true + enum SettingsCategory: String, CaseIterable, Identifiable { case graphics = "Graphics" case input = "Input" @@ -102,16 +324,132 @@ struct SettingsView: View { } } - var appVersion: String { - guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { - return "Unknown" + var body: some View { + if UIDevice.current.userInterfaceIdiom == .phone { + iOSSettings + } else { + iPadOSSettings + .ignoresSafeArea() + .edgesIgnoringSafeArea(.all) } - return version } - @FocusState private var isArgumentsKeyboardVisible: Bool + var iPadOSSettings: some View { + VStack { + SidebarView( + sidebar: { + AnyView( + ScrollView(.vertical) { + VStack { + VStack(spacing: 16) { + HStack { + Circle() + .fill(ryujinx.jitenabled ? Color.green : Color.red) + .frame(width: 12, height: 12) + + Text(ryujinx.jitenabled ? "JIT Enabled" : "JIT Not Acquired") + .font(.subheadline.weight(.medium)) + .foregroundColor(ryujinx.jitenabled ? .green : .red) + + Spacer() + + let memoryText = ProcessInfo.processInfo.isiOSAppOnMac + ? String(format: "%.0f GB", Double(totalMemory) / (1024 * 1024 * 1024)) + : String(format: "%.0f GB", Double(totalMemory) / 1_000_000_000) + + Text("\(memoryText) RAM") + .font(.subheadline.weight(.medium)) + .foregroundColor(.secondary) + + } + + InfoCard( + title: "Device", + value: UIDevice.modelName, + icon: deviceIcon, + color: .blue + ) + + InfoCard( + title: "System", + value: "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)", + icon: "applelogo", + color: .gray + ) + + InfoCard( + title: "Increased Memory Limit", + value: checkAppEntitlement("com.apple.developer.kernel.increased-memory-limit") ? "Enabled" : "Disabled", + icon: "memorychip.fill", + color: .orange + ) + } + .padding() + + Divider() + + ForEach(SettingsCategory.allCases, id: \.id) { key in + HStack { + Rectangle() + .frame(width: 2.5, height: 35) + .foregroundStyle(selectedCategory == key ? Color.accentColor : Color.clear) + Text(key.rawValue) // Fix here + Spacer() + } + .foregroundStyle(selectedCategory == key ? Color.accentColor : Color.primary) + .padding(5) + .background( + Color(uiColor: .secondarySystemBackground).opacity(selectedCategory == key ? 1 : 0) + ) + .background( + Rectangle() + .stroke(selectedCategory == key ? .teal : .clear, lineWidth: 2.5) + ) + .contentShape(Rectangle()) + .onTapGesture { + withAnimation(.smooth) { + selectedCategory = key // Uncommented and fixed + } + } + } + } + .padding() + } + ) + }, + content: { + ScrollView { + switch selectedCategory { + case .graphics: + graphicsSettings + case .input: + inputSettings + case .system: + systemSettings + case .advanced: + advancedSettings + case .misc: + miscSettings + } + } + }, + showSidebar: $sidebar + ) + .onAppear { + mVKPreFillBuffer = false + + + if let configs = SettingsManager.loadSettings() { + settingsManager.loadSettings() + } else { + settingsManager.saveSettings() + } + } + } + } - var body: some View { + + var iOSSettings: some View { iOSNav { ZStack { // Background color @@ -141,7 +479,6 @@ struct SettingsView: View { // Settings content ScrollView { VStack(spacing: 24) { - // Device Info Card deviceInfoCard .padding(.horizontal) .padding(.top) @@ -172,15 +509,12 @@ struct SettingsView: View { .onAppear { mVKPreFillBuffer = false - if let configs = loadSettings() { - self.config = configs + if let configs = SettingsManager.loadSettings() { + settingsManager.loadSettings() } else { - saveSettings() + settingsManager.saveSettings() } } - .onChange(of: config) { _ in - saveSettings() - } } } @@ -200,7 +534,6 @@ struct SettingsView: View { Spacer() - let totalMemory = ProcessInfo.processInfo.physicalMemory let memoryText = ProcessInfo.processInfo.isiOSAppOnMac ? String(format: "%.0f GB", Double(totalMemory) / (1024 * 1024 * 1024)) : String(format: "%.0f GB", Double(totalMemory) / 1_000_000_000) @@ -317,7 +650,7 @@ struct SettingsView: View { } VStack(spacing: 8) { - Slider(value: $config.resscale, in: 0.1...3.0, step: 0.05) + Slider(value: config.resscale, in: 0.1...3.0, step: 0.05) HStack { Text("0.1x") @@ -326,7 +659,7 @@ struct SettingsView: View { Spacer() - Text("\(config.resscale, specifier: "%.2f")x") + Text("\(settingsManager.config.resscale, specifier: "%.2f")x") .font(.headline) .foregroundColor(.blue) @@ -364,7 +697,7 @@ struct SettingsView: View { } VStack(spacing: 8) { - Slider(value: $config.maxAnisotropy, in: 0...16.0, step: 0.1) + Slider(value: config.maxAnisotropy, in: 0...16.0, step: 0.1) HStack { Text("Off") @@ -373,7 +706,7 @@ struct SettingsView: View { Spacer() - Text("\(config.maxAnisotropy, specifier: "%.1f")x") + Text("\(settingsManager.config.maxAnisotropy, specifier: "%.1f")x") .font(.headline) .foregroundColor(.blue) @@ -390,23 +723,23 @@ struct SettingsView: View { // Toggle options card SettingsCard { VStack(spacing: 4) { - SettingsToggle(isOn: $config.disableShaderCache, icon: "memorychip", label: "Shader Cache") + SettingsToggle(isOn: config.disableShaderCache, icon: "memorychip", label: "Shader Cache") Divider() - SettingsToggle(isOn: $config.disablevsync, icon: "arrow.triangle.2.circlepath", label: "Disable VSync") + SettingsToggle(isOn: config.disablevsync, icon: "arrow.triangle.2.circlepath", label: "Disable VSync") Divider() - SettingsToggle(isOn: $config.enableTextureRecompression, icon: "rectangle.compress.vertical", label: "Texture Recompression") + SettingsToggle(isOn: config.enableTextureRecompression, icon: "rectangle.compress.vertical", label: "Texture Recompression") Divider() - SettingsToggle(isOn: $config.disableDockedMode, icon: "dock.rectangle", label: "Docked Mode") + SettingsToggle(isOn: config.disableDockedMode, icon: "dock.rectangle", label: "Docked Mode") Divider() - SettingsToggle(isOn: $config.macroHLE, icon: "gearshape", label: "Macro HLE") + SettingsToggle(isOn: config.macroHLE, icon: "gearshape", label: "Macro HLE") Divider() @@ -421,7 +754,7 @@ struct SettingsView: View { .font(.headline) if (horizontalSizeClass == .regular && verticalSizeClass == .regular) || (horizontalSizeClass == .regular && verticalSizeClass == .compact) { - Picker(selection: $config.aspectRatio) { + Picker(selection: config.aspectRatio) { ForEach(AspectRatio.allCases, id: \.self) { ratio in Text(ratio.displayName).tag(ratio) } @@ -430,7 +763,7 @@ struct SettingsView: View { } .pickerStyle(.segmented) } else { - Picker(selection: $config.aspectRatio) { + Picker(selection: config.aspectRatio) { ForEach(AspectRatio.allCases, id: \.self) { ratio in Text(ratio.displayName).tag(ratio) } @@ -473,7 +806,7 @@ struct SettingsView: View { // On-screen controls card SettingsCard { VStack(spacing: 4) { - SettingsToggle(isOn: $config.handHeldController, icon: "formfitting.gamecontroller", label: "Player 1 to Handheld") + SettingsToggle(isOn: config.handHeldController, icon: "formfitting.gamecontroller", label: "Player 1 to Handheld") Divider() @@ -503,45 +836,97 @@ struct SettingsView: View { // Controller scale card SettingsCard { VStack(alignment: .leading, spacing: 12) { - HStack { - labelWithIcon("On-Screen Controller Scale", iconName: "magnifyingglass") - .font(.headline) - Spacer() - Button { - showControllerInfo.toggle() - } label: { - Image(systemName: "info.circle") - .foregroundColor(.secondary) + Text("On-Screen Controller") + .font(.headline) + .foregroundColor(.primary) + + Group { + HStack { + labelWithIcon("Scale", iconName: "magnifyingglass") + .font(.headline) + Spacer() + Button { + showControllerInfo.toggle() + } label: { + Image(systemName: "info.circle") + .foregroundColor(.secondary) + } + .buttonStyle(.plain) + .alert(isPresented: $showControllerInfo) { + Alert( + title: Text("On-Screen Controller Scale"), + message: Text("Adjust the On-Screen Controller size."), + dismissButton: .default(Text("OK")) + ) + } } - .buttonStyle(.plain) - .alert(isPresented: $showControllerInfo) { - Alert( - title: Text("On-Screen Controller Scale"), - message: Text("Adjust the On-Screen Controller size."), - dismissButton: .default(Text("OK")) - ) + + VStack(spacing: 8) { + Slider(value: $controllerScale, in: 0.1...3.0, step: 0.05) + + HStack { + Text("Smaller") + .font(.caption2) + .foregroundColor(.secondary) + + Spacer() + + Text("\(controllerScale, specifier: "%.2f")x") + .font(.headline) + .foregroundColor(.blue) + + Spacer() + + Text("Larger") + .font(.caption2) + .foregroundColor(.secondary) + } } } - VStack(spacing: 8) { - Slider(value: $controllerScale, in: 0.1...3.0, step: 0.05) - + Divider() + + Group { HStack { - Text("Smaller") - .font(.caption2) - .foregroundColor(.secondary) - - Spacer() - - Text("\(controllerScale, specifier: "%.2f")x") + labelWithIcon("Opacity", iconName: "magnifyingglass") .font(.headline) - .foregroundColor(.blue) - Spacer() + Button { + showControllerInfo.toggle() + } label: { + Image(systemName: "info.circle") + .foregroundColor(.secondary) + } + .buttonStyle(.plain) + .alert(isPresented: $showControllerInfo) { + Alert( + title: Text("On-Screen Controller Opacity"), + message: Text("Adjust the On-Screen Controller transparency."), + dismissButton: .default(Text("OK")) + ) + } + } + + VStack(spacing: 8) { + Slider(value: $controllerOpacity, in: 0.1...1.0, step: 0.05) - Text("Larger") - .font(.caption2) - .foregroundColor(.secondary) + HStack { + Text("More Transparent") + .font(.caption2) + .foregroundColor(.secondary) + + Spacer() + + Text("\(controllerOpacity, specifier: "%.2f")x") + .font(.headline) + .foregroundColor(.blue) + + Spacer() + + Text("Less Transparent") + .font(.caption2) + .foregroundColor(.secondary) + } } } } @@ -632,7 +1017,7 @@ struct SettingsView: View { labelWithIcon("System Language", iconName: "character.bubble") .font(.headline) - Picker(selection: $config.language) { + Picker(selection: config.language) { ForEach(SystemLanguage.allCases, id: \.self) { language in Text(language.displayName).tag(language) } @@ -650,7 +1035,7 @@ struct SettingsView: View { labelWithIcon("Region", iconName: "globe") .font(.headline) - Picker(selection: $config.regioncode) { + Picker(selection: config.regioncode) { ForEach(SystemRegionCode.allCases, id: \.self) { region in Text(region.displayName).tag(region) } @@ -676,7 +1061,7 @@ struct SettingsView: View { .font(.subheadline) .foregroundColor(.secondary) - Picker(selection: $config.memoryManagerMode) { + Picker(selection: config.memoryManagerMode) { ForEach(filteredMemoryModes, id: \.0) { key, displayName in Text(displayName).tag(key) } @@ -688,7 +1073,7 @@ struct SettingsView: View { Divider() - SettingsToggle(isOn: $config.disablePTC, icon: "cpu", label: "Disable PTC") + SettingsToggle(isOn: config.disablePTC, icon: "cpu", label: "Disable PTC") if let gpuInfo = getGPUInfo(), gpuInfo.hasPrefix("Apple M") { Divider() @@ -697,24 +1082,11 @@ struct SettingsView: View { SettingsToggle(isOn: .constant(false), icon: "bolt", label: "Hypervisor") .disabled(true) } else if checkAppEntitlement("com.apple.private.hypervisor") { - SettingsToggle(isOn: $config.hypervisor, icon: "bolt", label: "Hypervisor") + SettingsToggle(isOn: config.hypervisor, icon: "bolt", label: "Hypervisor") } } } } - - // Memory hacks card - SettingsCard { - VStack(spacing: 4) { - SettingsToggle(isOn: $config.expandRam, icon: "exclamationmark.bubble", label: "Expand Guest RAM (6GB)") - .accentColor(.red) - - Divider() - - SettingsToggle(isOn: $config.ignoreMissingServices, icon: "waveform.path", label: "Ignore Missing Services") - .accentColor(.red) - } - } } } @@ -733,11 +1105,11 @@ struct SettingsView: View { Divider() - SettingsToggle(isOn: $config.debuglogs, icon: "exclamationmark.bubble", label: "Debug Logs") + SettingsToggle(isOn: config.debuglogs, icon: "exclamationmark.bubble", label: "Debug Logs") Divider() - SettingsToggle(isOn: $config.tracelogs, icon: "waveform.path", label: "Trace Logs") + SettingsToggle(isOn: config.tracelogs, icon: "waveform.path", label: "Trace Logs") } } @@ -748,15 +1120,16 @@ struct SettingsView: View { Divider() - SettingsToggle(isOn: $config.dfsIntegrityChecks, icon: "checkmark.shield", label: "Disable FS Integrity Checks") + SettingsToggle(isOn: config.dfsIntegrityChecks, icon: "checkmark.shield", label: "Disable FS Integrity Checks") + + Divider() + + SettingsToggle(isOn: config.backendMultithreading, icon: "inset.filled.rectangle.and.person.filled", label: "Backend Multithreading") Divider() if MTLHud.shared.canMetalHud { - SettingsToggle(isOn: $metalHUDEnabled, icon: "speedometer", label: "Metal Performance HUD") - .onChange(of: metalHUDEnabled) { newValue in - MTLHud.shared.toggle() - } + SettingsToggle(isOn: $metalHudEnabler.metalHudEnabled, icon: "speedometer", label: "Metal Performance HUD") Divider() } @@ -775,8 +1148,23 @@ struct SettingsView: View { .foregroundColor(.blue) Spacer() } - .padding(.vertical, 8) + .padding(8) } + + } + } + + // Memory hacks card + SettingsCard { + VStack(spacing: 4) { + SettingsToggle(isOn: config.expandRam, icon: "exclamationmark.bubble", label: "Expand Guest RAM") + .accentColor(.red) + .disabled(totalMemory < 5723) + + Divider() + + SettingsToggle(isOn: config.ignoreMissingServices, icon: "waveform.path", label: "Ignore Missing Services") + .accentColor(.red) } } @@ -787,45 +1175,40 @@ struct SettingsView: View { .font(.headline) .foregroundColor(.primary) + let binding = Binding( + get: { + config.additionalArgs.wrappedValue.joined(separator: ", ") + }, + set: { newValue in + settingsManager.config.additionalArgs = newValue + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) } + } + ) + + if #available(iOS 15.0, *) { - TextField("Separate arguments with commas" ,text: Binding( - get: { - config.additionalArgs.joined(separator: ", ") - }, - set: { newValue in - config.additionalArgs = newValue - .split(separator: ",") - .map { $0.trimmingCharacters(in: .whitespaces) } - } - )) - .font(.system(.body, design: .monospaced)) - .textFieldStyle(.roundedBorder) - .textInputAutocapitalization(.none) - .disableAutocorrection(true) - .padding(.vertical, 4) - .toolbar { - ToolbarItem(placement: .keyboard) { - Button("Dismiss") { - isArgumentsKeyboardVisible = false + + TextField("Separate arguments with commas", text: binding) + .font(.system(.body, design: .monospaced)) + .textFieldStyle(.roundedBorder) + .textInputAutocapitalization(.none) + .disableAutocorrection(true) + .padding(.vertical, 4) + .toolbar { + ToolbarItem(placement: .keyboard) { + Button("Dismiss") { + isArgumentsKeyboardVisible = false + } } } - } - .focused($isArgumentsKeyboardVisible) + .focused($isArgumentsKeyboardVisible) } else { - TextField("Separate arguments with commas", text: Binding( - get: { - config.additionalArgs.joined(separator: ", ") - }, - set: { newValue in - config.additionalArgs = newValue - .split(separator: ",") - .map { $0.trimmingCharacters(in: .whitespaces) } - } - )) - .font(.system(.body, design: .monospaced)) - .textFieldStyle(.roundedBorder) - .disableAutocorrection(true) - .padding(.vertical, 4) + TextField("Separate arguments with commas", text: binding) + .font(.system(.body, design: .monospaced)) + .textFieldStyle(.roundedBorder) + .disableAutocorrection(true) + .padding(.vertical, 4) } } } @@ -858,27 +1241,55 @@ struct SettingsView: 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\"") + + Divider() + } + + // Disable Touch card SettingsToggle(isOn: $disableTouch, icon: "rectangle.and.hand.point.up.left.filled", label: "Disable Touch") Divider() - // Screenshot button card + if colorScheme == .light { + SettingsToggle(isOn: $disableTouch, icon: "iphone.slash", label: "Black Screen when using AirPlay") + + Divider() + } + + // Exit button card SettingsToggle(isOn: $ssb, icon: "arrow.left.circle", label: "Exit Button") Divider() + // Restarts app when it crashes card + SettingsToggle(isOn: $restartApp, icon: "arrow.clockwise", label: "Lock in App") + + Divider() + + + // Location to keep app in Background + SettingsToggle(isOn: $locationenabled, icon: "location.viewfinder", label: "Keep app in background") + + Divider() + + // JIT options if #available(iOS 17.0.1, *) { - SettingsToggle(isOn: $stikJIT, icon: "bolt.heart", label: "StikJIT") + let checked = stikJITorStikDebug() + let stikJIT = checked == 1 ? "StikDebug" : checked == 2 ? "StikJIT" : "StikDebug" + + SettingsToggle(isOn: $stikJIT, icon: "bolt.heart", label: stikJIT) .contextMenu { Button { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let mainWindow = windowScene.windows.last { - let alertController = UIAlertController(title: "About StikJIT", message: "StikJIT is a really amazing iOS Application to Enable JIT on the go on-device, made by the best, most kind, helpful and nice developers of all time jkcoxson and Blu <3", preferredStyle: .alert) + let alertController = UIAlertController(title: "About \(stikJIT)", message: "\(stikJIT) is a really amazing iOS Application to Enable JIT on the go on-device, made by the best, most kind, helpful and nice developers of all time jkcoxson and Blu <3", preferredStyle: .alert) let learnMoreButton = UIAlertAction(title: "Learn More", style: .default) {_ in - UIApplication.shared.open(URL(string: "https://github.com/0-Blu/StikJIT")!) + UIApplication.shared.open(URL(string: "https://github.com/StephenDev0/StikJIT")!) } alertController.addAction(learnMoreButton) @@ -947,9 +1358,6 @@ struct SettingsView: View { } } - func saveSettings() { - MeloNX.saveSettings(config: config) - } func getGPUInfo() -> String? { let device = MTLCreateSystemDefaultDevice() @@ -1012,7 +1420,7 @@ struct SVGView: UIViewRepresentable { } } -func saveSettings(config: Ryujinx.Configuration) { +func saveSettings(config: Ryujinx.Arguments) { do { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted @@ -1027,7 +1435,7 @@ func saveSettings(config: Ryujinx.Configuration) { } } -func loadSettings() -> Ryujinx.Configuration? { +func loadSettings() -> Ryujinx.Arguments? { do { let fileURL = URL.documentsDirectory.appendingPathComponent("config.json") @@ -1039,7 +1447,7 @@ func loadSettings() -> Ryujinx.Configuration? { let data = try Data(contentsOf: fileURL) let decoder = JSONDecoder() - let configs = try decoder.decode(Ryujinx.Configuration.self, from: data) + let configs = try decoder.decode(Ryujinx.Arguments.self, from: data) return configs } catch { // print("Failed to load settings: \(error)") @@ -1104,42 +1512,82 @@ struct SettingsCard: View { } var body: some View { - content + if UIDevice.current.userInterfaceIdiom == .phone { + content + .padding() + .background( + RoundedRectangle(cornerRadius: 12) + .fill(colorScheme == .dark ? Color(.systemGray6) : Color.white) + .shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: 2) + ) + .padding(.horizontal) + } else { + VStack { + Divider() + content + Divider() + } .padding() - .background( - RoundedRectangle(cornerRadius: 12) - .fill(colorScheme == .dark ? Color(.systemGray6) : Color.white) - .shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: 2) - ) - .padding(.horizontal) + } } } struct SettingsToggle: View { - let isOn: Binding + @Binding var isOn: Bool let icon: String let label: String var disabled: Bool = false + @AppStorage("toggleGreen") var toggleGreen: Bool = false var body: some View { - Toggle(isOn: isOn) { - HStack(spacing: 8) { - if icon.hasSuffix(".svg") { - SVGView(svgName: icon, color: .blue) - .frame(width: 20, height: 20) - } else { - Image(systemName: icon) + if UIDevice.current.userInterfaceIdiom == .phone { + Toggle(isOn: $isOn) { + HStack(spacing: 8) { + if icon.hasSuffix(".svg") { + SVGView(svgName: icon, color: .blue) + .frame(width: 20, height: 20) + } else { + Image(systemName: icon) // .symbolRenderingMode(.hierarchical) - .foregroundColor(.blue) + .foregroundColor(.blue) + } + + Text(label) + .font(.body) + } + } + .toggleStyle(SwitchToggleStyle(tint: .blue)) + .disabled(disabled) + .padding(.vertical, 6) + } else { + Group { + HStack(spacing: 8) { + HStack { + if icon.hasSuffix(".svg") { + SVGView(svgName: icon, color: .blue) + .frame(width: 20, height: 20) + } else { + Image(systemName: icon) + // .symbolRenderingMode(.hierarchical) + .foregroundStyle(.blue) + } + + Text(label) + .font(.body) + } + + Spacer() + + + Text(isOn ? "ON" : "Off") + .foregroundStyle(isOn ? (toggleGreen ? .green : .blue) : .blue) + } + .padding() + .onTapGesture { + isOn.toggle() } - - Text(label) - .font(.body) } } - .toggleStyle(SwitchToggleStyle(tint: .blue)) - .disabled(disabled) - .padding(.vertical, 6) } func disabled(_ disabled: Bool) -> SettingsToggle { diff --git a/src/MeloNX/MeloNX/App/Views/Main/UI/TabView/TabView.swift b/src/MeloNX/MeloNX/App/Views/Main/UI/TabView/TabView.swift index ccf7796bc..ac6d33ee7 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/TabView/TabView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/TabView/TabView.swift @@ -11,7 +11,6 @@ import UniformTypeIdentifiers struct MainTabView: View { @Binding var startemu: Game? - @Binding var config: Ryujinx.Configuration @Binding var MVKconfig: [MoltenVKSettings] @Binding var controllersList: [Controller] @Binding var currentControllers: [Controller] @@ -25,7 +24,8 @@ struct MainTabView: View { Label("Games", systemImage: "gamecontroller.fill") } - SettingsView(config: $config, MoltenVKSettings: $MVKconfig, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller) + // SettingsView(config: $config, MoltenVKSettings: $MVKconfig, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller) + SettingsViewNew(MoltenVKSettings: $MVKconfig, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller) .tabItem { Label("Settings", systemImage: "gear") } diff --git a/src/MeloNX/MeloNX/App/Views/Main/UI/Updates/Games/GameUpdateManagerSheet.swift b/src/MeloNX/MeloNX/App/Views/Main/UI/Updates/Games/GameUpdateManagerSheet.swift index f6d83191b..6ee97b6f7 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/UI/Updates/Games/GameUpdateManagerSheet.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/UI/Updates/Games/GameUpdateManagerSheet.swift @@ -280,11 +280,10 @@ struct UpdateManagerSheet: View { print("toggle selection \(update.path)") updates = updates.map { item in - var mutableItem = item - mutableItem.isSelected = item.path == update.path && !update.isSelected + item.isSelected = item.path == update.path && !update.isSelected // print(mutableItem.isSelected) // print(update.isSelected) - return mutableItem + return item } // print(updates) diff --git a/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift b/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift index eadc076e0..a869f5e8c 100644 --- a/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift +++ b/src/MeloNX/MeloNX/App/Views/MeloNXApp.swift @@ -28,6 +28,8 @@ struct MeloNXApp: App { @State var showOutOfDateSheet = false @State var updateInfo: LatestVersionResponse? = nil + @StateObject var metalHudEnabler = MTLHud.shared + @State var finished = false @AppStorage("hasbeenfinished") var finishedStorage: Bool = false @@ -47,6 +49,10 @@ struct MeloNXApp: App { if checkForUpdate { checkLatestVersion() } + + print(metalHudEnabler.canMetalHud) + + UserDefaults.standard.set(false, forKey: "lockInApp") } .sheet(isPresented: Binding( get: { showOutOfDateSheet && updateInfo != nil }, 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 new file mode 100644 index 000000000..e87058bf0 --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT-Swift.h @@ -0,0 +1,330 @@ +#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 new file mode 100644 index 000000000..572ca9033 --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/StosJIT.h @@ -0,0 +1,19 @@ +// +// 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 new file mode 100644 index 000000000..836b1e94d --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/idevice.h @@ -0,0 +1,2916 @@ +// 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 new file mode 100644 index 000000000..0e211278e --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Headers/plist.h @@ -0,0 +1,1096 @@ +/** + * @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/Info.plist b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Info.plist new file mode 100644 index 000000000..228311e3f Binary files /dev/null and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Info.plist differ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo new file mode 100644 index 000000000..7bed1d051 Binary files /dev/null and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo differ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json new file mode 100644 index 000000000..d2f988e4e --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json @@ -0,0 +1,9 @@ +{ + "ABIRoot": { + "kind": "Root", + "name": "NO_MODULE", + "printedName": "NO_MODULE", + "json_format_version": 8 + }, + "ConstValues": [] +} \ No newline at end of file diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc new file mode 100644 index 000000000..8a75bd2e1 Binary files /dev/null and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc differ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule new file mode 100644 index 000000000..85523df30 Binary files /dev/null and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule differ diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/module.modulemap b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/module.modulemap new file mode 100644 index 000000000..31e383431 --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/Modules/module.modulemap @@ -0,0 +1,11 @@ +framework module StosJIT { + umbrella header "StosJIT.h" + export * + + module * { export * } +} + +module StosJIT.Swift { + header "StosJIT-Swift.h" + requires objc +} diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT new file mode 100755 index 000000000..9bc158566 Binary files /dev/null and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT 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 new file mode 100644 index 000000000..0b83293b3 --- /dev/null +++ b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/_CodeSignature/CodeResources @@ -0,0 +1,201 @@ + + + + + files + + 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 + + 2mJoWBgg56N+3OxKfIDMLZFNHVk= + + 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 + + sZBe57nozztJzv83RPLjKIRYGSQmeE7XYCqr63xZONM= + + + 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/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs new file mode 100644 index 000000000..212ee2f8b --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs @@ -0,0 +1,622 @@ +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 + { + 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); + + // 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. + private const int MinCallsForPad = 8; + + private class MemoryCache : IDisposable + { + private readonly ReservedRegion _region; + 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 CacheMemoryAllocator CacheAllocator => _cacheAllocator; + public IntPtr Pointer => _region.Block.Pointer; + public ulong CurrentSize => _currentSize; + public ulong MaxSize => _maxSize; + + public MemoryCache(IJitMemoryAllocator allocator, ulong maxSize) + { + 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); + } + } + + 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); + + if (allocOffset < 0) + { + 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); + } + } + + public void ReprotectAsRw(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); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public void ClearReusePool() + { + lock (_reuselock) + { + _reusePages.Clear(); + } + } + + private static int AlignCodeSize(int codeSize) + { + return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ClearReusePool(); + _region.Dispose(); + _cacheAllocator.Clear(); + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + + private readonly IStackWalker _stackWalker; + private readonly Translator _translator; + private readonly List _sharedCaches; + private readonly List _localCaches; + private readonly Dictionary _pendingMaps; + private readonly object _lock; + + class ThreadLocalCacheEntry + { + public readonly int Offset; + public readonly int Size; + public readonly IntPtr FuncPtr; + public readonly int CacheIndex; + private int _useCount; + + public ThreadLocalCacheEntry(int offset, int size, IntPtr funcPtr, int cacheIndex) + { + Offset = offset; + Size = size; + FuncPtr = funcPtr; + CacheIndex = cacheIndex; + _useCount = 0; + } + + public int IncrementUseCount() + { + return ++_useCount; + } + } + + [ThreadStatic] + private static Dictionary _threadLocalCache; + + public WriteZeroCache(IJitMemoryAllocator allocator, IStackWalker stackWalker, Translator translator) + { + _stackWalker = stackWalker; + _translator = translator; + _sharedCaches = new List { new(allocator, MaxSharedCacheSize) }; + _localCaches = new List { new(allocator, MaxLocalCacheSize) }; + _pendingMaps = new Dictionary(); + _lock = new(); + } + + private PageAlignedRangeList GetPendingMapForCache(int cacheIndex) + { + ulong cacheKey = (ulong)cacheIndex; + if (!_pendingMaps.TryGetValue(cacheKey, out var pendingMap)) + { + pendingMap = new PageAlignedRangeList( + (offset, size) => _sharedCaches[cacheIndex].ReprotectAsRx(offset, size), + (address, func) => RegisterFunction(address, func)); + _pendingMaps[cacheKey] = pendingMap; + } + return pendingMap; + } + + private bool HasInAnyPendingMap(ulong guestAddress) + { + foreach (var pendingMap in _pendingMaps.Values) + { + if (pendingMap.Has(guestAddress)) + { + return true; + } + } + return false; + } + + private int AllocateInSharedCache(int codeLength) + { + for (int i = 0; i < _sharedCaches.Count; i++) + { + try + { + return (i << 28) | _sharedCaches[i].Allocate(codeLength); + } + 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)); + return (_sharedCaches.Count - 1) << 28 | _sharedCaches[_sharedCaches.Count - 1].Allocate(codeLength); + } + } + + private int AllocateInLocalCache(int codeLength) + { + for (int i = 0; i < _localCaches.Count; i++) + { + try + { + return (i << 28) | _localCaches[i].Allocate(codeLength); + } + catch (OutOfMemoryException) + { + // Try next cache + } + } + + lock (_lock) + { + var allocator = _localCaches[0].Allocator; + _localCaches.Add(new(allocator, MaxLocalCacheSize)); + return (_localCaches.Count - 1) << 28 | _localCaches[_localCaches.Count - 1].Allocate(codeLength); + } + } + + private static (int cacheIndex, int offset) SplitCacheOffset(int combinedOffset) + { + return (combinedOffset >> 28, combinedOffset & 0xFFFFFFF); + } + + public unsafe IntPtr Map(IntPtr framePointer, ReadOnlySpan code, ulong guestAddress, ulong guestSize) + { + if (TryGetThreadLocalFunction(guestAddress, out IntPtr funcPtr)) + { + return funcPtr; + } + + lock (_lock) + { + if (!HasInAnyPendingMap(guestAddress) && !_translator.Functions.ContainsKey(guestAddress)) + { + int combinedOffset = AllocateInSharedCache(code.Length); + var (cacheIndex, funcOffset) = SplitCacheOffset(combinedOffset); + + MemoryCache cache = _sharedCaches[cacheIndex]; + funcPtr = cache.Pointer + funcOffset; + + code.CopyTo(new Span((void*)funcPtr, code.Length)); + + TranslatedFunction function = new(funcPtr, guestSize); + + GetPendingMapForCache(cacheIndex).Add(funcOffset, code.Length, guestAddress, function); + } + + ClearThreadLocalCache(framePointer); + + return AddThreadLocalFunction(code, guestAddress); + } + } + + public unsafe IntPtr MapPageAligned(ReadOnlySpan code) + { + lock (_lock) + { + int cacheIndex; + int funcOffset; + IntPtr mappedFuncPtr = IntPtr.Zero; + + for (cacheIndex = 0; cacheIndex < _sharedCaches.Count; cacheIndex++) + { + try + { + var pendingMap = GetPendingMapForCache(cacheIndex); + + pendingMap.Pad(_sharedCaches[cacheIndex].CacheAllocator); + + int sizeAligned = BitUtils.AlignUp(code.Length, (int)MemoryBlock.GetPageSize()); + funcOffset = _sharedCaches[cacheIndex].Allocate(sizeAligned); + + Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0); + + IntPtr funcPtr1 = _sharedCaches[cacheIndex].Pointer + funcOffset; + + code.CopyTo(new Span((void*)funcPtr1, code.Length)); + + _sharedCaches[cacheIndex].ReprotectAsRx(funcOffset, sizeAligned); + + return funcPtr1; + } + catch (OutOfMemoryException) + { + // Try next cache + } + } + + var allocator = _sharedCaches[0].Allocator; + var newCache = new MemoryCache(allocator, MaxSharedCacheSize); + _sharedCaches.Add(newCache); + cacheIndex = _sharedCaches.Count - 1; + + var newPendingMap = GetPendingMapForCache(cacheIndex); + + newPendingMap.Pad(newCache.CacheAllocator); + + int newSizeAligned = BitUtils.AlignUp(code.Length, (int)MemoryBlock.GetPageSize()); + funcOffset = newCache.Allocate(newSizeAligned); + + Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0); + + IntPtr funcPtr = newCache.Pointer + funcOffset; + code.CopyTo(new Span((void*)funcPtr, code.Length)); + + newCache.ReprotectAsRx(funcOffset, newSizeAligned); + + return funcPtr; + } + } + + private bool TryGetThreadLocalFunction(ulong guestAddress, out IntPtr funcPtr) + { + if ((_threadLocalCache ??= new()).TryGetValue(guestAddress, out var entry)) + { + if (entry.IncrementUseCount() >= MinCallsForPad) + { + // Function is being called often, let's make it available in the shared cache so that the guest code + // can take the fast path and stop calling the emulator to get the function from the thread local cache. + // To do that we pad all "pending" function until they complete a page of memory, allowing us to reprotect them as RX. + + lock (_lock) + { + foreach (var pendingMap in _pendingMaps.Values) + { + // Get the cache index from the pendingMap key + if (_pendingMaps.FirstOrDefault(x => x.Value == pendingMap).Key is ulong cacheIndex) + { + // Use the correct shared cache for padding based on the cache index + pendingMap.Pad(_sharedCaches[(int)cacheIndex].CacheAllocator); + } + } + } + } + + funcPtr = entry.FuncPtr; + + return true; + } + + funcPtr = IntPtr.Zero; + + return false; + } + + private void ClearThreadLocalCache(IntPtr framePointer) + { + // Try to delete functions that are already on the shared cache + // and no longer being executed. + + if (_threadLocalCache == null) + { + return; + } + + IntPtr[] cachePointers = new IntPtr[_localCaches.Count]; + int[] cacheSizes = new int[_localCaches.Count]; + + for (int i = 0; i < _localCaches.Count; i++) + { + cachePointers[i] = _localCaches[i].Pointer; + cacheSizes[i] = (int)_localCaches[i].CurrentSize; + } + + IntPtr[] sharedPointers = new IntPtr[_sharedCaches.Count]; + int[] sharedSizes = new int[_sharedCaches.Count]; + + for (int i = 0; i < _sharedCaches.Count; i++) + { + sharedPointers[i] = _sharedCaches[i].Pointer; + sharedSizes[i] = (int)_sharedCaches[i].CurrentSize; + } + + IEnumerable callStack = null; + for (int i = 0; i < _localCaches.Count; i++) + { + callStack = _stackWalker.GetCallStack( + framePointer, + cachePointers[i], + cacheSizes[i], + sharedPointers[i], + sharedSizes[i] + ); + } + + List<(ulong, ThreadLocalCacheEntry)> toDelete = new(); + + foreach ((ulong address, ThreadLocalCacheEntry entry) in _threadLocalCache) + { + bool canDelete = !HasInAnyPendingMap(address); + if (!canDelete) + { + continue; + } + + foreach (ulong funcAddress in callStack) + { + if (funcAddress >= (ulong)entry.FuncPtr && funcAddress < (ulong)entry.FuncPtr + (ulong)entry.Size) + { + canDelete = false; + break; + } + } + + if (canDelete) + { + toDelete.Add((address, entry)); + } + } + + int pageSize = (int)MemoryBlock.GetPageSize(); + + foreach ((ulong address, ThreadLocalCacheEntry entry) in toDelete) + { + _threadLocalCache.Remove(address); + + int sizeAligned = BitUtils.AlignUp(entry.Size, pageSize); + var (cacheIndex, offset) = SplitCacheOffset(entry.Offset); + + _localCaches[cacheIndex].Free(offset, sizeAligned); + _localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned); + } + } + + public void ClearEntireThreadLocalCache() + { + if (_threadLocalCache == null) + { + return; + } + + int pageSize = (int)MemoryBlock.GetPageSize(); + + foreach ((_, ThreadLocalCacheEntry entry) in _threadLocalCache) + { + int sizeAligned = BitUtils.AlignUp(entry.Size, pageSize); + var (cacheIndex, offset) = SplitCacheOffset(entry.Offset); + + _localCaches[cacheIndex].Free(offset, sizeAligned); + _localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned); + } + + _threadLocalCache.Clear(); + _threadLocalCache = null; + } + + private unsafe IntPtr AddThreadLocalFunction(ReadOnlySpan code, ulong guestAddress) + { + int alignedSize = BitUtils.AlignUp(code.Length, (int)MemoryBlock.GetPageSize()); + int combinedOffset = AllocateInLocalCache(alignedSize); + var (cacheIndex, funcOffset) = SplitCacheOffset(combinedOffset); + + Debug.Assert((funcOffset & (int)(MemoryBlock.GetPageSize() - 1)) == 0); + + IntPtr funcPtr = _localCaches[cacheIndex].Pointer + funcOffset; + code.CopyTo(new Span((void*)funcPtr, code.Length)); + + (_threadLocalCache ??= new()).Add(guestAddress, new(funcOffset, code.Length, funcPtr, cacheIndex)); + + _localCaches[cacheIndex].ReprotectAsRx(funcOffset, alignedSize); + + return funcPtr; + } + + private void RegisterFunction(ulong address, TranslatedFunction func) + { + TranslatedFunction oldFunc = _translator.Functions.GetOrAdd(address, func.GuestSize, func); + + Debug.Assert(oldFunc == func); + + _translator.RegisterFunction(address, func); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + foreach (var cache in _localCaches) + { + cache.Dispose(); + } + + foreach (var cache in _sharedCaches) + { + cache.Dispose(); + } + + _localCaches.Clear(); + _sharedCaches.Clear(); + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Cpu/LightningJit/Translator.cs b/src/Ryujinx.Cpu/LightningJit/Translator.cs index bbacecef7..85593036e 100644 --- a/src/Ryujinx.Cpu/LightningJit/Translator.cs +++ b/src/Ryujinx.Cpu/LightningJit/Translator.cs @@ -11,6 +11,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; +using System.IO; namespace Ryujinx.Cpu.LightningJit { @@ -40,6 +41,7 @@ namespace Ryujinx.Cpu.LightningJit private readonly ConcurrentQueue> _oldFuncs; private readonly NoWxCache _noWxCache; + private readonly WriteZeroCache _writeZeroCache; private bool _disposed; internal TranslatorCache Functions { get; } @@ -55,17 +57,44 @@ namespace Ryujinx.Cpu.LightningJit if (IsNoWxPlatform) { - _noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this); + if (File.Exists("/System/Library/CoreServices/SystemVersion.plist")) + { + 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); + } + } + else + { + _noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this); + Functions = new TranslatorCache(); + FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); + Stubs = new TranslatorStubs(FunctionTable, _noWxCache); + } } else { JitCache.Initialize(new JitMemoryAllocator(forJit: true)); + Functions = new TranslatorCache(); + FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); + Stubs = new TranslatorStubs(FunctionTable, (NoWxCache)null); } - Functions = new TranslatorCache(); - FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); - Stubs = new TranslatorStubs(FunctionTable, _noWxCache); - FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; if (memory.Type.IsHostMappedOrTracked()) @@ -96,6 +125,7 @@ namespace Ryujinx.Cpu.LightningJit NativeInterface.UnregisterThread(); _noWxCache?.ClearEntireThreadLocalCache(); + _writeZeroCache?.ClearEntireThreadLocalCache(); } internal IntPtr GetOrTranslatePointer(IntPtr framePointer, ulong address, ExecutionMode mode) @@ -103,9 +133,13 @@ namespace Ryujinx.Cpu.LightningJit if (_noWxCache != null) { CompiledFunction func = Compile(address, mode); - return _noWxCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength); } + else if (_writeZeroCache != null) + { + CompiledFunction func = Compile(address, mode); + return _writeZeroCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength); + } return GetOrTranslate(address, mode).FuncPointer; } @@ -205,6 +239,10 @@ namespace Ryujinx.Cpu.LightningJit { _noWxCache.Dispose(); } + else if (_writeZeroCache != null) + { + _writeZeroCache.Dispose(); + } else { ClearJitCache(); diff --git a/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs b/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs index 914712bb1..91f3ec4f1 100644 --- a/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs +++ b/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs @@ -25,6 +25,8 @@ namespace Ryujinx.Cpu.LightningJit private readonly AddressTable _functionTable; private readonly NoWxCache _noWxCache; + private readonly WriteZeroCache _writeZeroCache; + private readonly GetFunctionAddressDelegate _getFunctionAddressRef; private readonly IntPtr _getFunctionAddress; private readonly Lazy _dispatchStub; @@ -92,6 +94,26 @@ namespace Ryujinx.Cpu.LightningJit _dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true); } + /// + /// Initializes a new instance of the class with the specified + /// instance. + /// + /// 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) + { + ArgumentNullException.ThrowIfNull(functionTable); + + _functionTable = functionTable; + _writeZeroCache = writeZeroCache; + _getFunctionAddressRef = NativeInterface.GetFunctionAddress; + _getFunctionAddress = Marshal.GetFunctionPointerForDelegate(_getFunctionAddressRef); + _slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true); + _dispatchStub = new(GenerateDispatchStub, isThreadSafe: true); + _dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true); + } + /// /// Releases all resources used by the instance. /// @@ -361,6 +383,10 @@ namespace Ryujinx.Cpu.LightningJit { return _noWxCache.MapPageAligned(code); } + else if (_writeZeroCache != null) + { + return _writeZeroCache.MapPageAligned(code); + } else { return JitCache.Map(code); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index 7523913ec..6610a4f0e 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.Vulkan var externalMemoryBuffer = new ExternalMemoryBufferCreateInfo { SType = StructureType.ExternalMemoryBufferCreateInfo, + // For MoltenVK 1.3 + // ExternalMemoryHandleTypeFlagsExt.MtlBufferBitExt HandleTypes = ExternalMemoryHandleTypeFlags.HostAllocationBitExt, }; @@ -676,4 +678,10 @@ namespace Ryujinx.Graphics.Vulkan Dispose(true); } } + + public static class ExternalMemoryHandleTypeFlagsExt + { + public const ExternalMemoryHandleTypeFlags MtlBufferBitExt = (ExternalMemoryHandleTypeFlags)0x00010000; + public const ExternalMemoryHandleTypeFlags MtlHeapBitExt = (ExternalMemoryHandleTypeFlags)0x00040000; + } } diff --git a/src/Ryujinx.Graphics.Vulkan/Constants.cs b/src/Ryujinx.Graphics.Vulkan/Constants.cs index 20ce65818..8103e2de8 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 = 64; + public const int MaxTexturesPerStage = 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/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs index 8d7faa653..4872d9b60 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs @@ -47,25 +47,25 @@ namespace Ryujinx.Graphics.Vulkan } } + public unsafe void UpdateBuffers(int setIndex, int baseBinding, ReadOnlySpan bufferInfo, DescriptorType type) { - - - // Proceed if all checks pass - fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo) + for (int i = 0; i < bufferInfo.Length; i++) { - var writeDescriptorSet = new WriteDescriptorSet + fixed (DescriptorBufferInfo* pBufferInfo = &bufferInfo[i]) { - SType = StructureType.WriteDescriptorSet, - DstSet = _descriptorSets[setIndex], - DstBinding = (uint)baseBinding, - DescriptorType = type, - DescriptorCount = (uint)bufferInfo.Length, - PBufferInfo = pBufferInfo - }; + var writeDescriptorSet = new WriteDescriptorSet + { + SType = StructureType.WriteDescriptorSet, + DstSet = _descriptorSets[setIndex], + DstBinding = (uint)(baseBinding + i), + DescriptorType = type, + DescriptorCount = 1, + PBufferInfo = pBufferInfo + }; - // Update descriptor sets - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + } } } @@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Vulkan { if (imageInfo.ImageView.Handle != 0UL) { - + var writeDescriptorSet = new WriteDescriptorSet { SType = StructureType.WriteDescriptorSet, @@ -90,19 +90,22 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void UpdateImages(int setIndex, int baseBinding, ReadOnlySpan imageInfo, DescriptorType type) { - fixed (DescriptorImageInfo* pImageInfo = imageInfo) + for (int i = 0; i < imageInfo.Length; i++) { - var writeDescriptorSet = new WriteDescriptorSet + fixed (DescriptorImageInfo* pImageInfo = &imageInfo[i]) { - SType = StructureType.WriteDescriptorSet, - DstSet = _descriptorSets[setIndex], - DstBinding = (uint)baseBinding, - DescriptorType = type, - DescriptorCount = (uint)imageInfo.Length, - PImageInfo = pImageInfo, - }; + var writeDescriptorSet = new WriteDescriptorSet + { + SType = StructureType.WriteDescriptorSet, + DstSet = _descriptorSets[setIndex], + DstBinding = (uint)(baseBinding + i), + DescriptorType = type, + DescriptorCount = 1, + PImageInfo = pImageInfo, + }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); + } } } @@ -112,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan { return; } - + fixed (DescriptorImageInfo* pImageInfo = imageInfo) { for (int i = 0; i < imageInfo.Length; i++) @@ -120,33 +123,24 @@ namespace Ryujinx.Graphics.Vulkan bool nonNull = imageInfo[i].ImageView.Handle != 0 && imageInfo[i].Sampler.Handle != 0; if (nonNull) { - int count = 1; - - while (i + count < imageInfo.Length && - imageInfo[i + count].ImageView.Handle != 0 && - imageInfo[i + count].Sampler.Handle != 0) - { - count++; - } - var writeDescriptorSet = new WriteDescriptorSet { SType = StructureType.WriteDescriptorSet, DstSet = _descriptorSets[setIndex], DstBinding = (uint)(baseBinding + i), - DescriptorType = DescriptorType.CombinedImageSampler, + DescriptorType = type, DescriptorCount = 1, - PImageInfo = pImageInfo, + PImageInfo = pImageInfo + i, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); - i += count - 1; + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } } } } + public unsafe void UpdateBufferImage(int setIndex, int bindingIndex, BufferView texelBufferView, DescriptorType type) { if (texelBufferView.Handle != 0UL) @@ -174,31 +168,22 @@ namespace Ryujinx.Graphics.Vulkan fixed (BufferView* pTexelBufferView = texelBufferView) { - for (uint i = 0; i < texelBufferView.Length;) + for (int i = 0; i < texelBufferView.Length; i++) { - uint count = 1; - - if (texelBufferView[(int)i].Handle != 0UL) + if (texelBufferView[i].Handle != 0UL) { - while (i + count < texelBufferView.Length && texelBufferView[(int)(i + count)].Handle != 0UL) - { - count++; - } - var writeDescriptorSet = new WriteDescriptorSet { SType = StructureType.WriteDescriptorSet, DstSet = _descriptorSets[setIndex], - DstBinding = (uint)baseBinding + i, + DstBinding = (uint)baseBinding + (uint)i, DescriptorType = type, - DescriptorCount = 1, + DescriptorCount = 1, PTexelBufferView = pTexelBufferView + i, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } - - i += count; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index f47b1c87d..1d0409f0e 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -757,6 +757,7 @@ namespace Ryujinx.Graphics.Vulkan return mirrored; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateAndBind(CommandBufferScoped cbs, ShaderCollection program, int setIndex, PipelineBindPoint pbp) { @@ -810,9 +811,12 @@ namespace Ryujinx.Graphics.Vulkan } } + // Split buffer updates into individual slices for MoltenVK compatibility ReadOnlySpan uniformBuffers = _uniformBuffers; - - tu.Push(uniformBuffers.Slice(binding, count)); + for (int i = 0; i < count; i++) + { + tu.Push(uniformBuffers.Slice(binding + i, 1)); + } } else if (setIndex == PipelineBase.StorageSetIndex) { @@ -836,9 +840,12 @@ namespace Ryujinx.Graphics.Vulkan } } + // Split buffer updates into individual slices for MoltenVK compatibility ReadOnlySpan storageBuffers = _storageBuffers; - - tu.Push(storageBuffers.Slice(binding, count)); + for (int i = 0; i < count; i++) + { + tu.Push(storageBuffers.Slice(binding + i, 1)); + } } else if (setIndex == PipelineBase.TextureSetIndex) { @@ -867,7 +874,10 @@ namespace Ryujinx.Graphics.Vulkan } } - tu.Push(textures[..count]); + for (int i = 0; i < count; i++) + { + tu.Push(textures.Slice(i, 1)); + } } else { @@ -878,7 +888,10 @@ namespace Ryujinx.Graphics.Vulkan bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default; } - tu.Push(bufferTextures[..count]); + for (int i = 0; i < count; i++) + { + tu.Push(bufferTextures.Slice(i, 1)); + } } } else @@ -906,7 +919,10 @@ namespace Ryujinx.Graphics.Vulkan images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default; } - tu.Push(images[..count]); + for (int i = 0; i < count; i++) + { + tu.Push(images.Slice(i, 1)); + } } else { @@ -917,7 +933,10 @@ namespace Ryujinx.Graphics.Vulkan bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default; } - tu.Push(bufferImages[..count]); + for (int i = 0; i < count; i++) + { + tu.Push(bufferImages.Slice(i, 1)); + } } } else @@ -940,6 +959,7 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp) { int setIndex = PipelineBase.TextureSetIndex; @@ -950,11 +970,12 @@ namespace Ryujinx.Graphics.Vulkan return; } - if (_updateDescriptorCacheCbIndex) + var dummyImageInfo = new DescriptorImageInfo { - _updateDescriptorCacheCbIndex = false; - program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex); - } + ImageView = _dummyTexture.GetImageView().Get(cbs).Value, + Sampler = _dummySampler.GetSampler().Get(cbs).Value, + ImageLayout = ImageLayout.General + }; var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs); @@ -967,71 +988,78 @@ namespace Ryujinx.Graphics.Vulkan { if (segment.Type != ResourceType.BufferTexture) { - Span textures = _textures; - for (int i = 0; i < count; i++) { - ref var texture = ref textures[i]; - ref var refs = ref _textureRefs[binding + i]; + int index = binding + i; + ref var textureRef = ref _textureRefs[index]; - texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default; - texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; + var imageView = textureRef.ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView; + var sampler = textureRef.Sampler?.Get(cbs).Value ?? dummyImageInfo.Sampler; - if (texture.ImageView.Handle == 0) + var imageInfo = new DescriptorImageInfo { - texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; - } + ImageView = imageView.Handle != 0 ? imageView : dummyImageInfo.ImageView, + Sampler = sampler.Handle != 0 ? sampler : dummyImageInfo.Sampler, + ImageLayout = ImageLayout.General + }; - if (texture.Sampler.Handle == 0) - { - texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; - } - - if (OperatingSystem.IsIOS()) { - Span singleTexture = textures.Slice(i, 1); - dsc.UpdateImages(0, binding + i, singleTexture, DescriptorType.CombinedImageSampler); - } - } - if (!OperatingSystem.IsIOS()) { - dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler); + dsc.UpdateImages(0, index, new[] { imageInfo }, DescriptorType.CombinedImageSampler); } } else { - Span bufferTextures = _bufferTextures; - for (int i = 0; i < count; i++) { - bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default; + int index = binding + i; + var bufferView = _bufferTextureRefs[index]?.GetBufferView(cbs, false) ?? default; + dsc.UpdateBufferImages(0, index, new[] { bufferView }, DescriptorType.UniformTexelBuffer); } - - dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer); } } else { + var arrayRef = _textureArrayRefs[binding]; + if (segment.Type != ResourceType.BufferTexture) { - // Span images = _imageRefs.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler); - - if (OperatingSystem.IsIOS()) + var imageInfos = arrayRef.Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler); + if (imageInfos != null) { - dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler); + for (int i = 0; i < imageInfos.Length && i < count; i++) + { + dsc.UpdateImages(0, binding + i, new[] { imageInfos[i] }, DescriptorType.CombinedImageSampler); + } } else { - dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler); + for (int i = 0; i < count; i++) + { + dsc.UpdateImages(0, binding + i, new[] { dummyImageInfo }, DescriptorType.CombinedImageSampler); + } } } else { - dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer); + 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); + } + } } } } var sets = dsc.GetSets(); - _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 5fc8351a5..addad83fd 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1241,7 +1241,7 @@ namespace Ryujinx.Graphics.Vulkan int vbSize = vertexBuffer.Buffer.Size; - if ((Gd.Vendor == Vendor.Amd || !OperatingSystem.IsIOSVersionAtLeast(17)) && !Gd.IsMoltenVk && vertexBuffer.Stride > 0) + if (Gd.Vendor == Vendor.Amd && !Gd.IsMoltenVk && vertexBuffer.Stride > 0) { // AMD has a bug where if offset + stride * count is greater than // the size, then the last attribute will have the wrong value. diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index a726b9edb..352a69521 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -409,7 +409,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PipelineVertexInputStateCreateInfo, VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount, - PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, + PVertexAttributeDescriptions = pVertexAttributeDescriptions2, // isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, VertexBindingDescriptionCount = VertexBindingDescriptionsCount, PVertexBindingDescriptions = pVertexBindingDescriptions, }; @@ -521,6 +521,7 @@ namespace Ryujinx.Graphics.Vulkan uint blendEnables = 0; + if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0) { // Blend can't be enabled for integer formats, so let's make sure it is disabled. diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index a77dba8e6..5c9497920 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan features2.Features.ShaderStorageImageMultisample, _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), isDynamicStateSupported, - features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue + features2.Features.MultiViewport && !IsMoltenVk, // Workaround for AMD on MoltenVK issue !IsMoltenVk ? featuresRobustness2.NullDescriptor : false, supportsPushDescriptors && !IsMoltenVk, propertiesPushDescriptor.MaxPushDescriptors, @@ -785,7 +785,7 @@ namespace Ryujinx.Graphics.Vulkan shaderSubgroupSize: (int)Capabilities.SubgroupSize, storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment, textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment, - gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0, + gatherBiasPrecision: (int)Capabilities.SubTexelPrecisionBits, //IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0, maximumGpuMemory: GetTotalGPUMemory()); } diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index ecbba6c6b..f35ad72c3 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -93,6 +93,7 @@ using Ryujinx.Input.HLE; using Silk.NET.Vulkan; using System; using System.IO; +using System.Text.RegularExpressions; using System.Runtime.InteropServices; using SDL2; @@ -1139,7 +1140,7 @@ namespace Ryujinx.Headless.SDL2 _libHacHorizonManager.InitializeBcatServer(); _libHacHorizonManager.InitializeSystemClients(); - _contentManager = new ContentManager(_virtualFileSystem); + // _contentManager = new ContentManager(_virtualFileSystem); _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile); @@ -1201,9 +1202,14 @@ namespace Ryujinx.Headless.SDL2 return; } + - if (option.InputPath == "MiiMaker") { - string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); + Match match = Regex.Match(option.InputPath, @"0x[0-9A-Fa-f]+"); + if (match.Success) + { + string hexStr = match.Value.Substring(2); + ulong id = Convert.ToUInt64(hexStr, 16); + string contentPath = _contentManager.GetInstalledContentPath(id, StorageId.BuiltInSystem, NcaContentType.Program); option.InputPath = contentPath; }