Merge branch 'XC-ios-ht' into rumbleDev

This commit is contained in:
MediaMoots 2025-05-31 10:54:14 +08:00
commit 6ece04918d
46 changed files with 6610 additions and 463 deletions

View file

@ -48,7 +48,7 @@ MeloNX works on iPhone XS/XR and later and iPad 8th Gen and later. Check out the
4. **Enable JIT**
- Use your preferred method to enable Just-In-Time (JIT) compilation.
- We reccomend using [JitStreamer](https://jkcoxson.com/jitstreamer)
- We reccomend using [StikDebug](https://apps.apple.com/us/app/stikdebug/id6744045754)
5. **Add Necessary Files**
@ -90,7 +90,7 @@ If having Issues installing firmware (Make sure your keys are installed first)
9. **Enable JIT**
- Use your preferred method to enable Just-In-Time (JIT) compilation.
- We recommend using [JitStreamer](https://jkcoxson.com/jitstreamer)
- We recommend using [StikDebug](https://apps.apple.com/us/app/stikdebug/id6744045754)
### TrollStore

View file

@ -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

View file

@ -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"

21
distribution/ios/xc-compile.sh Executable file
View file

@ -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

View file

@ -75,7 +75,7 @@ namespace ARMeilleure.Translation
FunctionTable = new AddressTable<ulong>(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)

View file

@ -9,5 +9,3 @@
// https://help.apple.com/xcode/#/dev745c5c974
VERSION = 1.7.0
DOTNET = /usr/local/share/dotnet/dotnet

View file

@ -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;

View file

@ -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<Unmanaged<CFError>?>?
) -> 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
}
defer {
releaseSecTask(task)
}
guard let entitlement = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else {
return false
}
guard let entitlements = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else {
// print("Failed to get entitlements")
return false
if let number = entitlement as? NSNumber {
return number.boolValue
} else if let bool = entitlement as? Bool {
return bool
}
return entitlements.boolValue != nil && entitlements.boolValue
return false
}

View file

@ -14,6 +14,7 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
#include <StosJIT/StosJIT-Swift.h>
#ifdef __cplusplus
extern "C" {

View file

@ -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
}

View file

@ -118,6 +118,7 @@ func presentAlert(title: String, message: String, completion: (() -> Void)? = ni
}
}
struct LaunchApp: Codable {
let success: Bool
let message: String

View file

@ -9,11 +9,55 @@ import Foundation
import Network
import UIKit
func enableJITStik() {
let bundleid = Bundle.main.bundleIdentifier ?? "Unknown"
let address = URL(string: "stikjit://enable-jit?bundle-id=\(bundleid)")!
if UIApplication.shared.canOpenURL(address) {
UIApplication.shared.open(address)
func stikJITorStikDebug() -> Int {
let teamid = SecTaskCopyTeamIdentifier(SecTaskCreateFromSelf(nil)!, nil)
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)
}
}

View file

@ -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))
}
}

View file

@ -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))
}
}
}

View file

@ -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()
}
}
canMetalHud = openMetalDylib() // i'm fixing the warnings just because you said i suck at coding Autumn (propenchiefer, https://youtu.be/tc65SNOTMz4 7:23)
func toggle() {
// print(UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED"))
if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") {
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
}
}

View file

@ -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<Content: View>: 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
@ -143,7 +149,7 @@ 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()
thread.qualityOfService = .userInteractive
thread.name = "MeloNX"
thread.start()
var thread: pthread_t?
let result = pthread_create(&thread, nil, { arg in
let unmanaged = Unmanaged<ClosureBox>.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<ClosureBox>.fromOpaque(boxed).release()
}
}
}
public struct Configuration : Codable, Equatable {
private class ClosureBox {
let closure: () -> Void
init(_ closure: @escaping () -> Void) {
self.closure = closure
}
}
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")

View file

@ -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)

View file

@ -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
@ -18,6 +20,7 @@ struct Joystick: View {
let sensitivity: CGFloat = 1.5
var dragGesture: some Gesture {
DragGesture()
.onChanged { value in
@ -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

View file

@ -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)

View file

@ -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 {

View file

@ -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)
}
}
}

View file

@ -32,11 +32,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 +60,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 +70,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 +139,6 @@ struct ContentView: View {
private var mainMenuView: some View {
MainTabView(
startemu: $game,
config: $config,
MVKconfig: $settings,
controllersList: $controllersList,
currentControllers: $currentControllers,
@ -155,6 +154,8 @@ struct ContentView: View {
}
UserDefaults.standard.set(false, forKey: "lockInApp")
// print(MTLHud.shared.isEnabled)
initControllerObservers()
@ -163,6 +164,11 @@ struct ContentView: View {
ControllerListView(game: $game)
))
refreshControllersList()
ryujinx.addGames()
checkJitStatus()
}
.onOpenURL { url in
@ -373,6 +379,8 @@ struct ContentView: View {
if jitStreamerEB {
jitStreamerEB = false // byee jitstreamer eb
}
if !ryujinx.jitenabled {
if useTrollStore {
askForJIT()
@ -381,7 +389,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 +402,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 +467,6 @@ class LocationManager: NSObject, CLLocationManagerDelegate {
}
}
struct ControllerListView: View {
@State private var selectedIndex = 0
@Binding var game: Game?

View file

@ -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)
)
.lineLimit(1)
.truncationMode(.tail)
.fixedSize(horizontal: true, vertical: false)
.layoutPriority(1)
// Compatibility badge
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)
}
}

View file

@ -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")
}

View file

@ -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)

View file

@ -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 },

View file

@ -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(<swift/objc-prologue.h>)
# include <swift/objc-prologue.h>
#endif
#pragma clang diagnostic ignored "-Wauto-import"
#if defined(__OBJC__)
#include <Foundation/Foundation.h>
#endif
#if defined(__cplusplus)
#include <cstdint>
#include <cstddef>
#include <cstdbool>
#include <cstring>
#include <stdlib.h>
#include <new>
#include <type_traits>
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#endif
#if defined(__cplusplus)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
# include <ptrauth.h>
#else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
# ifndef __ptrauth_swift_value_witness_function_pointer
# define __ptrauth_swift_value_witness_function_pointer(x)
# endif
# ifndef __ptrauth_swift_class_method_pointer
# define __ptrauth_swift_class_method_pointer(x)
# endif
#pragma clang diagnostic pop
#endif
#pragma clang diagnostic pop
#endif
#if !defined(SWIFT_TYPEDEFS)
# define SWIFT_TYPEDEFS 1
# if __has_include(<uchar.h>)
# include <uchar.h>
# elif !defined(__cplusplus)
typedef uint_least16_t char16_t;
typedef uint_least32_t char32_t;
# endif
typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
#endif
#if !defined(SWIFT_PASTE)
# define SWIFT_PASTE_HELPER(x, y) x##y
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
#endif
#if !defined(SWIFT_METATYPE)
# define SWIFT_METATYPE(X) Class
#endif
#if !defined(SWIFT_CLASS_PROPERTY)
# if __has_feature(objc_class_property)
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
# else
# define SWIFT_CLASS_PROPERTY(...)
# endif
#endif
#if !defined(SWIFT_RUNTIME_NAME)
# if __has_attribute(objc_runtime_name)
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
# else
# define SWIFT_RUNTIME_NAME(X)
# endif
#endif
#if !defined(SWIFT_COMPILE_NAME)
# if __has_attribute(swift_name)
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
# else
# define SWIFT_COMPILE_NAME(X)
# endif
#endif
#if !defined(SWIFT_METHOD_FAMILY)
# if __has_attribute(objc_method_family)
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
# else
# define SWIFT_METHOD_FAMILY(X)
# endif
#endif
#if !defined(SWIFT_NOESCAPE)
# if __has_attribute(noescape)
# define SWIFT_NOESCAPE __attribute__((noescape))
# else
# define SWIFT_NOESCAPE
# endif
#endif
#if !defined(SWIFT_RELEASES_ARGUMENT)
# if __has_attribute(ns_consumed)
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
# else
# define SWIFT_RELEASES_ARGUMENT
# endif
#endif
#if !defined(SWIFT_WARN_UNUSED_RESULT)
# if __has_attribute(warn_unused_result)
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
# else
# define SWIFT_WARN_UNUSED_RESULT
# endif
#endif
#if !defined(SWIFT_NORETURN)
# if __has_attribute(noreturn)
# define SWIFT_NORETURN __attribute__((noreturn))
# else
# define SWIFT_NORETURN
# endif
#endif
#if !defined(SWIFT_CLASS_EXTRA)
# define SWIFT_CLASS_EXTRA
#endif
#if !defined(SWIFT_PROTOCOL_EXTRA)
# define SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_ENUM_EXTRA)
# define SWIFT_ENUM_EXTRA
#endif
#if !defined(SWIFT_CLASS)
# if __has_attribute(objc_subclassing_restricted)
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# else
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# endif
#endif
#if !defined(SWIFT_RESILIENT_CLASS)
# if __has_attribute(objc_class_stub)
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
# else
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
# endif
#endif
#if !defined(SWIFT_PROTOCOL)
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_EXTENSION)
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
#endif
#if !defined(OBJC_DESIGNATED_INITIALIZER)
# if __has_attribute(objc_designated_initializer)
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
# else
# define OBJC_DESIGNATED_INITIALIZER
# endif
#endif
#if !defined(SWIFT_ENUM_ATTR)
# if __has_attribute(enum_extensibility)
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
# else
# define SWIFT_ENUM_ATTR(_extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM)
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# if __has_feature(generalized_swift_name)
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# else
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
# endif
#endif
#if !defined(SWIFT_UNAVAILABLE)
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
#endif
#if !defined(SWIFT_UNAVAILABLE_MSG)
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
#endif
#if !defined(SWIFT_AVAILABILITY)
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
#endif
#if !defined(SWIFT_WEAK_IMPORT)
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
#endif
#if !defined(SWIFT_DEPRECATED)
# define SWIFT_DEPRECATED __attribute__((deprecated))
#endif
#if !defined(SWIFT_DEPRECATED_MSG)
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
#endif
#if !defined(SWIFT_DEPRECATED_OBJC)
# if __has_feature(attribute_diagnose_if_objc)
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
# else
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
# endif
#endif
#if defined(__OBJC__)
#if !defined(IBSegueAction)
# define IBSegueAction
#endif
#endif
#if !defined(SWIFT_EXTERN)
# if defined(__cplusplus)
# define SWIFT_EXTERN extern "C"
# else
# define SWIFT_EXTERN extern
# endif
#endif
#if !defined(SWIFT_CALL)
# define SWIFT_CALL __attribute__((swiftcall))
#endif
#if !defined(SWIFT_INDIRECT_RESULT)
# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
#endif
#if !defined(SWIFT_CONTEXT)
# define SWIFT_CONTEXT __attribute__((swift_context))
#endif
#if !defined(SWIFT_ERROR_RESULT)
# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
#endif
#if defined(__cplusplus)
# define SWIFT_NOEXCEPT noexcept
#else
# define SWIFT_NOEXCEPT
#endif
#if !defined(SWIFT_C_INLINE_THUNK)
# if __has_attribute(always_inline)
# if __has_attribute(nodebug)
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug))
# else
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline))
# endif
# else
# define SWIFT_C_INLINE_THUNK inline
# endif
#endif
#if defined(_WIN32)
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
#endif
#else
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL
#endif
#endif
#if defined(__OBJC__)
#if __has_feature(objc_modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
#endif
#endif
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
#if __has_warning("-Wpragma-clang-attribute")
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wnullability"
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#if __has_attribute(external_source_symbol)
# pragma push_macro("any")
# undef any
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="StosJIT",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
# pragma pop_macro("any")
#endif
#if defined(__OBJC__)
SWIFT_EXTERN char * _Nullable attach(int32_t pid) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
SWIFT_EXTERN char * _Nullable debugattachanddetachApp(char * _Nonnull bundleId) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
SWIFT_EXTERN void detach(void) SWIFT_NOEXCEPT;
SWIFT_EXTERN void loop_heartbeat(void) SWIFT_NOEXCEPT;
SWIFT_EXTERN BOOL writeZeroToMemory(uint64_t addr, int32_t length) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
#endif
#if __has_attribute(external_source_symbol)
# pragma clang attribute pop
#endif
#if defined(__cplusplus)
#endif
#pragma clang diagnostic pop
#endif
#else
#error unsupported Swift architecture
#endif

View file

@ -0,0 +1,19 @@
//
// StosJIT.h
// StosJIT
//
// Created by Stossy11 on 10/05/2025.
//
#import <Foundation/Foundation.h>
#import <StosJIT/idevice.h>
//! Project version number for StosJIT.
FOUNDATION_EXPORT double StosJITVersionNumber;
//! Project version string for StosJIT.
FOUNDATION_EXPORT const unsigned char StosJITVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <StosJIT/PublicHeader.h>

View file

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Headers/StosJIT-Swift.h</key>
<data>
h9vaTwhC6FlnyKmIkaxLQGlFd1g=
</data>
<key>Headers/StosJIT.h</key>
<data>
ggHr5wlLNIIPydwUL9Vxm6abxjo=
</data>
<key>Headers/idevice.h</key>
<data>
mHDz7368FsBID56/epJ2NgIkha4=
</data>
<key>Headers/plist.h</key>
<data>
bL/f0MQDpLfvIcI1zxPwMuJ/PfI=
</data>
<key>Info.plist</key>
<data>
ZTTwPKlta/gjXAr1HIHmyAxeU4E=
</data>
<key>Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
<data>
2mJoWBgg56N+3OxKfIDMLZFNHVk=
</data>
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json</key>
<data>
gcwBsH4BgyFY4sVtNt+/xOKS3vY=
</data>
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc</key>
<data>
YPtkDrAuBiPPEp4ZdRdBVlFXnRM=
</data>
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule</key>
<data>
9cIInnjJzJFtY+CZm2iNo5qL3MQ=
</data>
<key>Modules/module.modulemap</key>
<data>
cnpvYzvLIwWcxkQodj5uLbHkyRk=
</data>
</dict>
<key>files2</key>
<dict>
<key>Headers/StosJIT-Swift.h</key>
<dict>
<key>hash2</key>
<data>
1obIr4IjMvtcyNyYIV/Nh/5wahcA1cFjc4n4XVlNt2I=
</data>
</dict>
<key>Headers/StosJIT.h</key>
<dict>
<key>hash2</key>
<data>
yY9KyrRdOYRdlb7G6wVMU2hogasXMjwV5r8jUIk44ok=
</data>
</dict>
<key>Headers/idevice.h</key>
<dict>
<key>hash2</key>
<data>
zR9/TB9Dnv3uRC8qqGvaQ6c2yyOFUURmrHKLdEiUh/g=
</data>
</dict>
<key>Headers/plist.h</key>
<dict>
<key>hash2</key>
<data>
yFbGsiXBBp91tfsSFtS0Utt2Gpc3MEDFiMVXKG9q1rs=
</data>
</dict>
<key>Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
<dict>
<key>hash2</key>
<data>
sZBe57nozztJzv83RPLjKIRYGSQmeE7XYCqr63xZONM=
</data>
</dict>
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json</key>
<dict>
<key>hash2</key>
<data>
Qnesa0n4URGWAopawg9bGx36dUwkYV00BoCJ8LFzlyg=
</data>
</dict>
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc</key>
<dict>
<key>hash2</key>
<data>
k7F2Xs2hh9iMbK8IE8TMtN6gjQ9kWs30NUKHeupq6VE=
</data>
</dict>
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule</key>
<dict>
<key>hash2</key>
<data>
gMDYNHcBPCNwZw2A5mEUiCyYAS9VhtQG0z+/WqAUrOQ=
</data>
</dict>
<key>Modules/module.modulemap</key>
<dict>
<key>hash2</key>
<data>
FGwGKs5SNvpCyiIWiOP4eml9m2e3KISmtCJVtNnUnUc=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

View file

@ -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<int, HashSet<int>> _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<int, HashSet<int>>();
_region.Block.MapAsRw(0, _currentSize);
_region.ExpandIfNeeded(_currentSize);
WriteZeroToMemory((ulong)_region.Block.Pointer.ToInt64(), (int)_currentSize);
}
public bool TryGetReusablePage(int size, out int offset)
{
lock (_reuselock)
{
if (_reusePages.TryGetValue(size, out var exactOffsets) && exactOffsets.Count > 0)
{
offset = exactOffsets.First();
exactOffsets.Remove(offset);
return true;
}
var largerSizes = _reusePages.Where(kvp => kvp.Key > size && kvp.Value.Count > 0)
.OrderBy(kvp => kvp.Key)
.FirstOrDefault();
if (largerSizes.Value != null && largerSizes.Value.Count > 0)
{
int largerSize = largerSizes.Key;
var largerOffsets = largerSizes.Value;
offset = largerOffsets.First();
largerOffsets.Remove(offset);
int remainingSize = largerSize - size;
if (remainingSize > 0)
{
AddReusablePage(offset + size, remainingSize);
}
return true;
}
offset = -1;
return false;
}
}
public void AddReusablePage(int offset, int size)
{
if (size < (int)MemoryBlock.GetPageSize())
{
return;
}
lock (_reuselock)
{
if (!_reusePages.TryGetValue(size, out var offsets))
{
offsets = new HashSet<int>();
_reusePages[size] = offsets;
}
offsets.Add(offset);
}
}
public int Allocate(int codeSize)
{
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<MemoryCache> _sharedCaches;
private readonly List<MemoryCache> _localCaches;
private readonly Dictionary<ulong, PageAlignedRangeList> _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<ulong, ThreadLocalCacheEntry> _threadLocalCache;
public WriteZeroCache(IJitMemoryAllocator allocator, IStackWalker stackWalker, Translator translator)
{
_stackWalker = stackWalker;
_translator = translator;
_sharedCaches = new List<MemoryCache> { new(allocator, MaxSharedCacheSize) };
_localCaches = new List<MemoryCache> { new(allocator, MaxLocalCacheSize) };
_pendingMaps = new Dictionary<ulong, PageAlignedRangeList>();
_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<byte> 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<byte>((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<byte> 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<byte>((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<byte>((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<ulong> 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<byte> 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<byte>((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);
}
}
}

View file

@ -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<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
private readonly NoWxCache _noWxCache;
private readonly WriteZeroCache _writeZeroCache;
private bool _disposed;
internal TranslatorCache<TranslatedFunction> 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<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
Stubs = new TranslatorStubs(FunctionTable, _writeZeroCache);
}
else
{
_noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
}
}
else
{
_noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
}
}
else
{
JitCache.Initialize(new JitMemoryAllocator(forJit: true));
Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
Stubs = new TranslatorStubs(FunctionTable, (NoWxCache)null);
}
Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(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();

View file

@ -25,6 +25,8 @@ namespace Ryujinx.Cpu.LightningJit
private readonly AddressTable<ulong> _functionTable;
private readonly NoWxCache _noWxCache;
private readonly WriteZeroCache _writeZeroCache;
private readonly GetFunctionAddressDelegate _getFunctionAddressRef;
private readonly IntPtr _getFunctionAddress;
private readonly Lazy<IntPtr> _dispatchStub;
@ -92,6 +94,26 @@ namespace Ryujinx.Cpu.LightningJit
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
}
/// <summary>
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
/// <see cref="Translator"/> instance.
/// </summary>
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
/// <param name="writeZeroCache">Cache used on iOS versions that need a debugger to make a debug map</param>
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
public TranslatorStubs(AddressTable<ulong> 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);
}
/// <summary>
/// Releases all resources used by the <see cref="TranslatorStubs"/> instance.
/// </summary>
@ -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);

View file

@ -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;
}
}

View file

@ -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;

View file

@ -47,25 +47,25 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public unsafe void UpdateBuffers(int setIndex, int baseBinding, ReadOnlySpan<DescriptorBufferInfo> 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);
}
}
}
@ -90,19 +90,22 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void UpdateImages(int setIndex, int baseBinding, ReadOnlySpan<DescriptorImageInfo> 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);
}
}
}
@ -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,22 +168,15 @@ 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,
PTexelBufferView = pTexelBufferView + i,
@ -197,8 +184,6 @@ namespace Ryujinx.Graphics.Vulkan
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
i += count;
}
}
}

View file

@ -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<DescriptorBufferInfo> 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<DescriptorBufferInfo> 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<DescriptorImageInfo>(textures[..count]);
for (int i = 0; i < count; i++)
{
tu.Push<DescriptorImageInfo>(textures.Slice(i, 1));
}
}
else
{
@ -878,7 +888,10 @@ namespace Ryujinx.Graphics.Vulkan
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
}
tu.Push<BufferView>(bufferTextures[..count]);
for (int i = 0; i < count; i++)
{
tu.Push<BufferView>(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<DescriptorImageInfo>(images[..count]);
for (int i = 0; i < count; i++)
{
tu.Push<DescriptorImageInfo>(images.Slice(i, 1));
}
}
else
{
@ -917,7 +933,10 @@ namespace Ryujinx.Graphics.Vulkan
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default;
}
tu.Push<BufferView>(bufferImages[..count]);
for (int i = 0; i < count; i++)
{
tu.Push<BufferView>(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<uint>.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<DescriptorImageInfo> 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<DescriptorImageInfo> 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<BufferView> 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<DescriptorImageInfo> 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<uint>.Empty);
}

View file

@ -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.

View file

@ -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.

View file

@ -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());
}

View file

@ -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);
@ -1202,8 +1203,13 @@ 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;
}