forked from MeloNX/MeloNX
WIP: Android support
This commit is contained in:
parent
8b37abec1e
commit
89777c18f1
@ -2,6 +2,7 @@
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -112,9 +113,15 @@ namespace ARMeilleure.Signal
|
||||
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || PlatformInfo.IsBionic)
|
||||
{
|
||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
||||
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
config.StructAddressOffset = 16; // si_addr
|
||||
config.StructWriteOffset = 8; // si_code
|
||||
}
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
{
|
||||
@ -19,10 +20,26 @@ namespace ARMeilleure.Signal
|
||||
public int sa_flags;
|
||||
public IntPtr sa_restorer;
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("android"), StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
public struct SigActionBionic
|
||||
{
|
||||
public int sa_flags;
|
||||
public IntPtr sa_handler;
|
||||
public SigSet sa_mask;
|
||||
public IntPtr sa_restorer;
|
||||
}
|
||||
|
||||
private const int SIGSEGV = 11;
|
||||
private const int SIGBUS = 10;
|
||||
private const int SA_SIGINFO = 0x00000004;
|
||||
private const int SA_ONSTACK = 0x08000000;
|
||||
|
||||
[SupportedOSPlatform("android"), LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, ref SigActionBionic sigAction, out SigActionBionic oldAction);
|
||||
|
||||
[SupportedOSPlatform("android"), LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, IntPtr sigAction, out SigActionBionic oldAction);
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
||||
@ -35,7 +52,26 @@ namespace ARMeilleure.Signal
|
||||
|
||||
public static SigAction GetSegfaultExceptionHandler()
|
||||
{
|
||||
int result = sigaction(SIGSEGV, IntPtr.Zero, out SigAction old);
|
||||
int result;
|
||||
SigAction old;
|
||||
|
||||
if (Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
result = sigaction(SIGSEGV, IntPtr.Zero, out SigActionBionic tmp);
|
||||
|
||||
old = new SigAction
|
||||
{
|
||||
sa_handler = tmp.sa_handler,
|
||||
sa_mask = tmp.sa_mask,
|
||||
sa_flags = tmp.sa_flags,
|
||||
sa_restorer = tmp.sa_restorer
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result = sigaction(SIGSEGV, IntPtr.Zero, out old);
|
||||
}
|
||||
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
@ -47,28 +83,50 @@ namespace ARMeilleure.Signal
|
||||
|
||||
public static SigAction RegisterExceptionHandler(IntPtr action)
|
||||
{
|
||||
SigAction sig = new()
|
||||
int result;
|
||||
SigAction old;
|
||||
|
||||
if (Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
sa_handler = action,
|
||||
sa_flags = SA_SIGINFO,
|
||||
};
|
||||
SigActionBionic sig = new()
|
||||
{
|
||||
sa_handler = action,
|
||||
sa_flags = SA_SIGINFO | SA_ONSTACK,
|
||||
};
|
||||
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
|
||||
int result = sigaction(SIGSEGV, ref sig, out SigAction old);
|
||||
result = sigaction(SIGSEGV, ref sig, out SigActionBionic tmp);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}");
|
||||
old = new SigAction
|
||||
{
|
||||
sa_handler = tmp.sa_handler,
|
||||
sa_mask = tmp.sa_mask,
|
||||
sa_flags = tmp.sa_flags,
|
||||
sa_restorer = tmp.sa_restorer
|
||||
};
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else
|
||||
{
|
||||
result = sigaction(SIGBUS, ref sig, out _);
|
||||
SigAction sig = new() { sa_handler = action, sa_flags = SA_SIGINFO, };
|
||||
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
|
||||
result = sigaction(SIGSEGV, ref sig, out old);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
|
||||
throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}");
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
{
|
||||
result = sigaction(SIGBUS, ref sig, out _);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +135,29 @@ namespace ARMeilleure.Signal
|
||||
|
||||
public static bool RestoreExceptionHandler(SigAction oldAction)
|
||||
{
|
||||
return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0 && (!OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || sigaction(SIGBUS, ref oldAction, out SigAction _) == 0);
|
||||
if (Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
SigActionBionic tmp = new SigActionBionic
|
||||
{
|
||||
sa_handler = oldAction.sa_handler,
|
||||
sa_mask = oldAction.sa_mask,
|
||||
sa_flags = oldAction.sa_flags,
|
||||
sa_restorer = oldAction.sa_restorer
|
||||
};
|
||||
|
||||
return sigaction(SIGSEGV, ref tmp, out SigActionBionic _) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool success = sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0;
|
||||
|
||||
if (success && (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()))
|
||||
{
|
||||
success = sigaction(SIGBUS, ref oldAction, out SigAction _) == 0;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1006,6 +1006,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
osPlatform |= (OperatingSystem.IsMacOS() ? 1u : 0u) << 2;
|
||||
osPlatform |= (OperatingSystem.IsWindows() ? 1u : 0u) << 3;
|
||||
osPlatform |= (OperatingSystem.IsIOS() ? 1u : 0u) << 4;
|
||||
osPlatform |= (Ryujinx.Common.PlatformInfo.IsBionic ? 1u : 0u) << 5;
|
||||
#pragma warning restore IDE0055
|
||||
|
||||
return osPlatform;
|
||||
|
Binary file not shown.
@ -0,0 +1,28 @@
|
||||
//
|
||||
// AspectRatio.swift
|
||||
// melonx-native
|
||||
//
|
||||
// Created by Даниил Виноградов on 04.03.2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum AspectRatio: String, Codable, CaseIterable {
|
||||
case fixed4x3 = "Fixed4x3"
|
||||
case fixed16x9 = "Fixed16x9"
|
||||
case fixed16x10 = "Fixed16x10"
|
||||
case fixed21x9 = "Fixed21x9"
|
||||
case fixed32x9 = "Fixed32x9"
|
||||
case stretched = "Stretched"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .fixed4x3: return "4:3"
|
||||
case .fixed16x9: return "16:9 (Default)"
|
||||
case .fixed16x10: return "16:10"
|
||||
case .fixed21x9: return "21:9"
|
||||
case .fixed32x9: return "32:9"
|
||||
case .stretched: return "Stretched (Full Screen)"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
//
|
||||
// Language.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 16/02/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum SystemLanguage: String, Codable, CaseIterable {
|
||||
case japanese = "Japanese"
|
||||
case americanEnglish = "AmericanEnglish"
|
||||
case french = "French"
|
||||
case german = "German"
|
||||
case italian = "Italian"
|
||||
case spanish = "Spanish"
|
||||
case chinese = "Chinese"
|
||||
case korean = "Korean"
|
||||
case dutch = "Dutch"
|
||||
case portuguese = "Portuguese"
|
||||
case russian = "Russian"
|
||||
case taiwanese = "Taiwanese"
|
||||
case britishEnglish = "BritishEnglish"
|
||||
case canadianFrench = "CanadianFrench"
|
||||
case latinAmericanSpanish = "LatinAmericanSpanish"
|
||||
case simplifiedChinese = "SimplifiedChinese"
|
||||
case traditionalChinese = "TraditionalChinese"
|
||||
case brazilianPortuguese = "BrazilianPortuguese"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .japanese: return "Japanese"
|
||||
case .americanEnglish: return "American English"
|
||||
case .french: return "French"
|
||||
case .german: return "German"
|
||||
case .italian: return "Italian"
|
||||
case .spanish: return "Spanish"
|
||||
case .chinese: return "Chinese"
|
||||
case .korean: return "Korean"
|
||||
case .dutch: return "Dutch"
|
||||
case .portuguese: return "Portuguese"
|
||||
case .russian: return "Russian"
|
||||
case .taiwanese: return "Taiwanese"
|
||||
case .britishEnglish: return "British English"
|
||||
case .canadianFrench: return "Canadian French"
|
||||
case .latinAmericanSpanish: return "Latin American Spanish"
|
||||
case .simplifiedChinese: return "Simplified Chinese"
|
||||
case .traditionalChinese: return "Traditional Chinese"
|
||||
case .brazilianPortuguese: return "Brazilian Portuguese"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Region.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 16/02/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum SystemRegionCode: String, Codable, CaseIterable {
|
||||
case japan = "Japan"
|
||||
case usa = "USA"
|
||||
case europe = "Europe"
|
||||
case australia = "Australia"
|
||||
case china = "China"
|
||||
case korea = "Korea"
|
||||
case taiwan = "Taiwan"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .japan: return "Japan"
|
||||
case .usa: return "United States"
|
||||
case .europe: return "Europe"
|
||||
case .australia: return "Australia"
|
||||
case .china: return "China"
|
||||
case .korea: return "Korea"
|
||||
case .taiwan: return "Taiwan"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
// Created by Даниил Виноградов on 04.03.2025.
|
||||
//
|
||||
|
||||
//import LibRyujinx
|
||||
// import LibRyujinx
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
@ -23,6 +23,7 @@ import Darwin
|
||||
|
||||
private init() {}
|
||||
|
||||
private var isRunning = false
|
||||
private var basePath: URL!
|
||||
}
|
||||
|
||||
@ -44,15 +45,15 @@ public extension Ryujinx {
|
||||
|
||||
let version = fetchFirmwareVersion()
|
||||
if !version.isEmpty {
|
||||
self.firmwareversion = version
|
||||
firmwareversion = version
|
||||
}
|
||||
}
|
||||
|
||||
func loadGames(){
|
||||
func loadGames() {
|
||||
let fileManager = FileManager.default
|
||||
let romsDirectory = basePath.appendingPathComponent("roms")
|
||||
|
||||
if (!fileManager.fileExists(atPath: romsDirectory.path)) {
|
||||
if !fileManager.fileExists(atPath: romsDirectory.path) {
|
||||
do {
|
||||
try fileManager.createDirectory(at: romsDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch {
|
||||
@ -88,6 +89,237 @@ public extension Ryujinx {
|
||||
logger.error("Error loading games from roms folder: \(error)")
|
||||
self.games = []
|
||||
}
|
||||
}
|
||||
|
||||
func start(with config: Configuration) throws {
|
||||
guard !isRunning else {
|
||||
throw RyujinxError.alreadyRunning
|
||||
}
|
||||
|
||||
isRunning = true
|
||||
|
||||
// RunLoop.current.perform {
|
||||
|
||||
let url = URL(string: config.gamepath)
|
||||
|
||||
do {
|
||||
let args = self.buildCommandLineArgs(from: config)
|
||||
// let accessing = url?.startAccessingSecurityScopedResource()
|
||||
|
||||
// Start the emulation
|
||||
logger.info("Emulation starting")
|
||||
let result = RyujinxLib.shared.mainRyujinxSdl(args: args)
|
||||
logger.info("Emulation starting result: \(result)")
|
||||
|
||||
if result != 0 {
|
||||
self.isRunning = false
|
||||
// if let accessing, accessing {
|
||||
// url!.stopAccessingSecurityScopedResource()
|
||||
// }
|
||||
|
||||
throw RyujinxError.executionError(code: result)
|
||||
}
|
||||
} catch {
|
||||
self.isRunning = false
|
||||
logger.error("Emulation failed to start: \(error)")
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
func getCurrentFps() -> Int {
|
||||
RyujinxLib.shared.getCurrentFps()
|
||||
}
|
||||
}
|
||||
|
||||
private extension Ryujinx {
|
||||
func buildCommandLineArgs(from config: Configuration) -> [String] {
|
||||
var args: [String] = []
|
||||
|
||||
// Add the game path
|
||||
args.append(config.gamepath)
|
||||
|
||||
// Starts with vulkan
|
||||
args.append("--graphics-backend")
|
||||
args.append("Vulkan")
|
||||
|
||||
args.append(contentsOf: ["--memory-manager-mode", config.memoryManagerMode])
|
||||
|
||||
// args.append(contentsOf: ["--exclusive-fullscreen", String(true)])
|
||||
// args.append(contentsOf: ["--exclusive-fullscreen-width", "\(Int(UIScreen.main.bounds.width))"])
|
||||
// args.append(contentsOf: ["--exclusive-fullscreen-height", "\(Int(UIScreen.main.bounds.height))"])
|
||||
// We don't need this. Ryujinx should handle it fine :3
|
||||
// this also causes crashes in some games :3
|
||||
|
||||
args.append(contentsOf: ["--system-language", config.language.rawValue])
|
||||
|
||||
args.append(contentsOf: ["--system-region", config.regioncode.rawValue])
|
||||
|
||||
args.append(contentsOf: ["--aspect-ratio", config.aspectRatio.rawValue])
|
||||
|
||||
if config.nintendoinput {
|
||||
args.append("--correct-controller")
|
||||
}
|
||||
|
||||
if config.disablePTC {
|
||||
args.append("--disable-ptc")
|
||||
}
|
||||
|
||||
if config.disablevsync {
|
||||
args.append("--disable-vsync")
|
||||
}
|
||||
|
||||
|
||||
if config.hypervisor {
|
||||
args.append("--use-hypervisor")
|
||||
}
|
||||
|
||||
if config.dfsIntegrityChecks {
|
||||
args.append("--disable-fs-integrity-checks")
|
||||
}
|
||||
|
||||
|
||||
if config.resscale != 1.0 {
|
||||
args.append(contentsOf: ["--resolution-scale", String(config.resscale)])
|
||||
}
|
||||
|
||||
if config.expandRam {
|
||||
args.append(contentsOf: ["--expand-ram", String(config.expandRam)])
|
||||
}
|
||||
|
||||
if config.ignoreMissingServices {
|
||||
// args.append(contentsOf: ["--ignore-missing-services"])
|
||||
args.append("--ignore-missing-services")
|
||||
}
|
||||
|
||||
if config.maxAnisotropy != 0 {
|
||||
args.append(contentsOf: ["--max-anisotropy", String(config.maxAnisotropy)])
|
||||
}
|
||||
|
||||
if !config.macroHLE {
|
||||
args.append("--disable-macro-hle")
|
||||
}
|
||||
|
||||
if !config.disableShaderCache { // same with disableShaderCache
|
||||
args.append("--disable-shader-cache")
|
||||
}
|
||||
|
||||
if !config.disableDockedMode { // disableDockedMode is actually enableDockedMode, i just have flipped it around in the settings page to make it easier to understand :3
|
||||
args.append("--disable-docked-mode")
|
||||
}
|
||||
if config.enableTextureRecompression {
|
||||
args.append("--enable-texture-recompression")
|
||||
}
|
||||
|
||||
if config.debuglogs {
|
||||
args.append("--enable-debug-logs")
|
||||
}
|
||||
if config.tracelogs {
|
||||
args.append("--enable-trace-logs")
|
||||
}
|
||||
|
||||
// List the input ids
|
||||
if config.listinputids {
|
||||
args.append("--list-inputs-ids")
|
||||
}
|
||||
|
||||
// Append the input ids (limit to 8 (used to be 4) just in case)
|
||||
if !config.inputids.isEmpty {
|
||||
config.inputids.prefix(8).enumerated().forEach { index, inputId in
|
||||
if config.handHeldController {
|
||||
args.append(contentsOf: ["\(index == 0 ? "--input-id-handheld" : "--input-id-\(index + 1)")", inputId])
|
||||
} else {
|
||||
args.append(contentsOf: ["--input-id-\(index + 1)", inputId])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apped any additional arguments
|
||||
args.append(contentsOf: config.additionalArgs)
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Ryujinx {
|
||||
struct Configuration: Codable, Equatable {
|
||||
public var gamepath: String
|
||||
public var inputids: [String]
|
||||
public var resscale: Double
|
||||
public var debuglogs: Bool
|
||||
public var tracelogs: Bool
|
||||
public var nintendoinput: Bool
|
||||
public var enableInternet: Bool
|
||||
public var listinputids: Bool
|
||||
public var aspectRatio: AspectRatio
|
||||
public var memoryManagerMode: String
|
||||
public var disableShaderCache: Bool
|
||||
public var hypervisor: Bool
|
||||
public var disableDockedMode: Bool
|
||||
public var enableTextureRecompression: Bool
|
||||
public var additionalArgs: [String]
|
||||
public var maxAnisotropy: Double
|
||||
public var macroHLE: Bool
|
||||
public var ignoreMissingServices: Bool
|
||||
public var expandRam: Bool
|
||||
public var dfsIntegrityChecks: Bool
|
||||
public var disablePTC: Bool
|
||||
public var disablevsync: Bool
|
||||
public var language: SystemLanguage
|
||||
public var regioncode: SystemRegionCode
|
||||
public var handHeldController: Bool
|
||||
|
||||
public init(gamepath: String,
|
||||
inputids: [String] = [],
|
||||
debuglogs: Bool = false,
|
||||
tracelogs: Bool = false,
|
||||
listinputids: Bool = false,
|
||||
aspectRatio: AspectRatio = .fixed16x9,
|
||||
memoryManagerMode: String = "HostMappedUnsafe",
|
||||
disableShaderCache: Bool = false,
|
||||
disableDockedMode: Bool = false,
|
||||
nintendoinput: Bool = true,
|
||||
enableInternet: Bool = false,
|
||||
enableTextureRecompression: Bool = true,
|
||||
additionalArgs: [String] = [],
|
||||
resscale: Double = 1.00,
|
||||
maxAnisotropy: Double = 0,
|
||||
macroHLE: Bool = false,
|
||||
ignoreMissingServices: Bool = false,
|
||||
hypervisor: Bool = false,
|
||||
expandRam: Bool = false,
|
||||
dfsIntegrityChecks: Bool = false,
|
||||
disablePTC: Bool = false,
|
||||
disablevsync: Bool = false,
|
||||
language: SystemLanguage = .americanEnglish,
|
||||
regioncode: SystemRegionCode = .usa,
|
||||
handHeldController: Bool = false)
|
||||
{
|
||||
self.gamepath = gamepath
|
||||
self.inputids = inputids
|
||||
self.debuglogs = debuglogs
|
||||
self.tracelogs = tracelogs
|
||||
self.listinputids = listinputids
|
||||
self.aspectRatio = aspectRatio
|
||||
self.disableShaderCache = disableShaderCache
|
||||
self.disableDockedMode = disableDockedMode
|
||||
self.enableTextureRecompression = enableTextureRecompression
|
||||
self.additionalArgs = additionalArgs
|
||||
self.memoryManagerMode = memoryManagerMode
|
||||
self.resscale = resscale
|
||||
self.nintendoinput = nintendoinput
|
||||
self.enableInternet = enableInternet
|
||||
self.maxAnisotropy = maxAnisotropy
|
||||
self.macroHLE = macroHLE
|
||||
self.expandRam = expandRam
|
||||
self.ignoreMissingServices = ignoreMissingServices
|
||||
self.hypervisor = hypervisor
|
||||
self.dfsIntegrityChecks = dfsIntegrityChecks
|
||||
self.disablePTC = disablePTC
|
||||
self.disablevsync = disablevsync
|
||||
self.language = language
|
||||
self.regioncode = regioncode
|
||||
self.handHeldController = handHeldController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
//
|
||||
// RyujinxError.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 3/11/2024.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum RyujinxError: Error {
|
||||
case libraryLoadError
|
||||
case executionError(code: Int32)
|
||||
case alreadyRunning
|
||||
case notRunning
|
||||
}
|
@ -68,6 +68,28 @@ extension RyujinxLib {
|
||||
let f = unsafeBitCast(sym, to: initializeFunc.self)
|
||||
return f(fileDescriptor, fileURLCandidateCString)
|
||||
}
|
||||
|
||||
func mainRyujinxSdl(args: [String]) -> Int32 {
|
||||
// Convert Arguments to ones that Ryujinx can Read
|
||||
let cArgs = args.map { strdup($0) }
|
||||
defer { cArgs.forEach { free($0) } }
|
||||
var argvPtrs = cArgs
|
||||
|
||||
let sym = dlsym(handle, "main_ryujinx_sdl")
|
||||
|
||||
typealias initializeFunc = @convention(c) (Int32, UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>) -> (Int32)
|
||||
let f = unsafeBitCast(sym, to: initializeFunc.self)
|
||||
return f(Int32(args.count), &argvPtrs)
|
||||
}
|
||||
|
||||
func getCurrentFps() -> Int {
|
||||
let sym = dlsym(handle, "get_current_fps")
|
||||
|
||||
typealias initializeFunc = @convention(c) () -> (Int32)
|
||||
let f = unsafeBitCast(sym, to: initializeFunc.self)
|
||||
return Int(f())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//public struct GameInfo {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import SwiftUI
|
||||
import MeloNXModel
|
||||
import OSLog
|
||||
|
||||
enum ContentTab: String, Hashable {
|
||||
case games, home, settings
|
||||
@ -10,35 +11,115 @@ struct ContentView: View {
|
||||
@State var viewModel = ViewModel()
|
||||
@State var appearance = ""
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $tab) {
|
||||
NavigationStack {
|
||||
NavigationStack {
|
||||
GamesView()
|
||||
}
|
||||
}
|
||||
.tabItem { Label("Games", systemImage: "house.fill") }
|
||||
.tag(ContentTab.games)
|
||||
@State private var config: Ryujinx.Configuration
|
||||
@State private var game: Game?
|
||||
@State private var isLoading = true
|
||||
|
||||
NavigationStack {
|
||||
SettingsView(appearance: $appearance)
|
||||
.navigationTitle("Settings")
|
||||
init() {
|
||||
// let defaultConfig = loadSettings() ?? Ryujinx.Configuration(gamepath: "")
|
||||
let defaultConfig = Ryujinx.Configuration(gamepath: "")
|
||||
_config = State(initialValue: defaultConfig)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if game != nil {
|
||||
if isLoading {
|
||||
ZStack {
|
||||
emulationView
|
||||
}
|
||||
} else {
|
||||
EmulationView()
|
||||
}
|
||||
} else {
|
||||
TabView(selection: $tab) {
|
||||
NavigationStack {
|
||||
NavigationStack {
|
||||
GamesView(startemu: $game)
|
||||
}
|
||||
}
|
||||
.tabItem { Label("Games", systemImage: "house.fill") }
|
||||
.tag(ContentTab.games)
|
||||
|
||||
NavigationStack {
|
||||
SettingsView(appearance: $appearance)
|
||||
.navigationTitle("Settings")
|
||||
}
|
||||
.tabItem { Label("Settings", systemImage: "gearshape.fill") }
|
||||
.tag(ContentTab.settings)
|
||||
}
|
||||
.environment(viewModel)
|
||||
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
|
||||
.onAppear {
|
||||
initialize()
|
||||
}
|
||||
.tabItem { Label("Settings", systemImage: "gearshape.fill") }
|
||||
.tag(ContentTab.settings)
|
||||
}
|
||||
.environment(viewModel)
|
||||
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
|
||||
.onAppear {
|
||||
initializeSDL()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helper Methods
|
||||
// var SdlInitFlags: uint = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO; // Initialises SDL2 for Events, Game Controller, Joystick, Audio and Video.
|
||||
private func initializeSDL() {
|
||||
|
||||
}
|
||||
|
||||
private extension ContentView {
|
||||
func initialize() {
|
||||
// setMoltenVKSettings()
|
||||
let path = URL.documentsDirectory
|
||||
Ryujinx.shared.initialize(basePath: path)
|
||||
}
|
||||
|
||||
var emulationView: some View {
|
||||
Text("Loading...")
|
||||
.onAppear {
|
||||
setupEmulation()
|
||||
|
||||
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
|
||||
if Ryujinx.shared.getCurrentFps() != 0 {
|
||||
withAnimation {
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
// isAnimating = false
|
||||
timer.invalidate()
|
||||
}
|
||||
logger.info("FPS: \(Ryujinx.shared.getCurrentFps())")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func setupEmulation() {
|
||||
// patchMakeKeyAndVisible()
|
||||
// isVCA = (currentControllers.first(where: { $0 == onscreencontroller }) != nil)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
func start() {
|
||||
guard let game else { return }
|
||||
|
||||
config.gamepath = game.fileURL.path
|
||||
// config.inputids = Array(Set(currentControllers.map(\.id)))
|
||||
//
|
||||
// if mVKPreFillBuffer {
|
||||
// let setting = MoltenVKSettings(string: "MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS", value: "2")
|
||||
// setenv(setting.string, setting.value, 1)
|
||||
// }
|
||||
//
|
||||
// if syncqsubmits {
|
||||
// let setting = MoltenVKSettings(string: "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", value: "2")
|
||||
// setenv(setting.string, setting.value, 1)
|
||||
// }
|
||||
|
||||
if config.inputids.isEmpty {
|
||||
config.inputids.append("0")
|
||||
}
|
||||
|
||||
do {
|
||||
try Ryujinx.shared.start(with: config)
|
||||
} catch {
|
||||
logger.error("Error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"EmulationView" : {
|
||||
|
||||
},
|
||||
"Firmware" : {
|
||||
|
||||
@ -151,6 +154,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Loading..." : {
|
||||
|
||||
},
|
||||
"Mii Maker" : {
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
//
|
||||
// EmulationView.swift
|
||||
// melonx-native
|
||||
//
|
||||
// Created by Даниил Виноградов on 04.03.2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct EmulationView: View {
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Text("EmulationView")
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import MeloNXModel
|
||||
import SwiftUI
|
||||
|
||||
struct GamesView: View {
|
||||
@Binding var startemu: Game?
|
||||
@State var firmwareversion = "0"
|
||||
@State var searchQuery: String = ""
|
||||
|
||||
@ -38,7 +39,7 @@ struct GamesView: View {
|
||||
List {
|
||||
// Section {
|
||||
ForEach(games) { game in
|
||||
GameView(game: game)
|
||||
GameView(game: game, startemu: $startemu)
|
||||
}
|
||||
// }
|
||||
}
|
||||
@ -90,42 +91,47 @@ struct GamesView: View {
|
||||
|
||||
struct GameView: View {
|
||||
@State var game: Game
|
||||
@Binding var startemu: Game?
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 16) {
|
||||
ZStack {
|
||||
if let iconData = game.icon,
|
||||
let icon = UIImage(data: iconData)
|
||||
{
|
||||
Image(uiImage: icon)
|
||||
.resizable()
|
||||
} else {
|
||||
Image(systemName: "gamecontroller.fill")
|
||||
.font(.system(size: 20))
|
||||
.foregroundColor(.gray)
|
||||
.background(Color.gray)
|
||||
Button(action: {
|
||||
startemu = game
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
ZStack {
|
||||
if let iconData = game.icon,
|
||||
let icon = UIImage(data: iconData)
|
||||
{
|
||||
Image(uiImage: icon)
|
||||
.resizable()
|
||||
} else {
|
||||
Image(systemName: "gamecontroller.fill")
|
||||
.font(.system(size: 20))
|
||||
.foregroundColor(.gray)
|
||||
.background(Color.gray)
|
||||
}
|
||||
}
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
|
||||
// Game Info
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(game.titleName)
|
||||
.font(.body)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text(game.developer)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.opacity(0.8)
|
||||
}
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
|
||||
// Game Info
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(game.titleName)
|
||||
.font(.body)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text(game.developer)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.opacity(0.8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
|
||||
fileprivate let logger: Logger = Logger(subsystem: "com.xitrix.melonx", category: "melonx")
|
||||
let logger: Logger = Logger(subsystem: "com.xitrix.melonx", category: "melonx")
|
||||
|
||||
/// The Android SDK number we are running against, or `nil` if not running on Android
|
||||
let androidSDK = ProcessInfo.processInfo.environment["android.os.Build.VERSION.SDK_INT"].flatMap({ Int($0) })
|
||||
|
@ -35,6 +35,11 @@ namespace Ryujinx.Common
|
||||
return "ios";
|
||||
}
|
||||
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
return "android";
|
||||
}
|
||||
|
||||
if (IsValid())
|
||||
{
|
||||
return BuildVersion;
|
||||
@ -51,7 +56,7 @@ namespace Ryujinx.Common
|
||||
#else
|
||||
public static string GetBaseApplicationDirectory()
|
||||
{
|
||||
if (IsFlatHubBuild() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
if (IsFlatHubBuild() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || PlatformInfo.IsBionic)
|
||||
{
|
||||
return AppDataManager.BaseDirPath;
|
||||
}
|
||||
|
@ -36,6 +36,12 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
Console.WriteLine($"[iOS] Required firmware library: {libName}");
|
||||
return libName;
|
||||
}
|
||||
else if (Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
string libName = $"lib{libraryName}.{version}.so";
|
||||
Console.WriteLine($"[Android] Required firmware library: {libName}");
|
||||
return libName;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Unsupported OS for FFmpeg: {RuntimeInformation.RuntimeIdentifier}");
|
||||
|
@ -25,7 +25,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request?
|
||||
};
|
||||
|
||||
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
||||
if (!Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
||||
}
|
||||
|
||||
GeneralServiceManager.Add(_generalServiceDetail);
|
||||
}
|
||||
@ -196,7 +199,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
||||
if (!Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
||||
}
|
||||
|
||||
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
||||
}
|
||||
|
@ -123,20 +123,23 @@ namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
string[] args = new string[argCount];
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 1");
|
||||
try
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 2");
|
||||
for (int i = 0; i < argCount; i++)
|
||||
{
|
||||
args[i] = Marshal.PtrToStringAnsi(pArgs[i]);
|
||||
|
||||
Console.WriteLine(args[i]);
|
||||
Logger.Info?.Print(LogClass.Application, args[i]);
|
||||
}
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 3");
|
||||
|
||||
Main(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
Logger.Info?.Print(LogClass.Application, $"Emulator start failed with reason: {e}");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -320,20 +323,24 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
Silk.NET.Core.Loader.SearchPathContainer.Platform = Silk.NET.Core.Loader.UnderlyingPlatform.MacOS;
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 4");
|
||||
Version = ReleaseInformation.GetVersion();
|
||||
|
||||
if (!OperatingSystem.IsIOS())
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 5");
|
||||
if (!OperatingSystem.IsIOS() && !PlatformInfo.IsBionic)
|
||||
{
|
||||
Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsLinux())
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 1");
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsLinux() || PlatformInfo.IsBionic)
|
||||
{
|
||||
AutoResetEvent invoked = new(false);
|
||||
|
||||
// MacOS must perform SDL polls from the main thread.
|
||||
SDL2Driver.MainThreadDispatcher = action =>
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 3");
|
||||
invoked.Reset();
|
||||
|
||||
WindowBase.QueueMainThreadAction(() =>
|
||||
@ -347,6 +354,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
};
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 2");
|
||||
var result = Parser.Default.ParseArguments<Options>(args)
|
||||
.WithParsed(options =>
|
||||
{
|
||||
@ -935,7 +943,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
if (gamepad == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")");
|
||||
Logger.Info?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")");
|
||||
|
||||
inputId = "0";
|
||||
|
||||
@ -1141,11 +1149,13 @@ namespace Ryujinx.Headless.SDL2
|
||||
static void Load(Options option)
|
||||
{
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 4");
|
||||
if (_virtualFileSystem == null)
|
||||
{
|
||||
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 5");
|
||||
if (_libHacHorizonManager == null)
|
||||
{
|
||||
_libHacHorizonManager = new LibHacHorizonManager();
|
||||
@ -1155,6 +1165,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
_libHacHorizonManager.InitializeSystemClients();
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 6");
|
||||
if (_contentManager == null)
|
||||
{
|
||||
_contentManager = new ContentManager(_virtualFileSystem);
|
||||
@ -1174,7 +1185,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
}
|
||||
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 7");
|
||||
GraphicsConfig.EnableShaderCache = true;
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
@ -1213,6 +1225,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 8");
|
||||
if (option.InputPath == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Please provide a file to load");
|
||||
@ -1220,6 +1233,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 9");
|
||||
if (option.InputPath == "MiiMaker") {
|
||||
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
@ -1265,6 +1279,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
||||
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 10");
|
||||
if (!option.DisableFileLog)
|
||||
{
|
||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||
@ -1274,6 +1289,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
));
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 11");
|
||||
// Setup graphics configuration
|
||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||
GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression;
|
||||
@ -1282,10 +1298,13 @@ namespace Ryujinx.Headless.SDL2
|
||||
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
|
||||
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 12");
|
||||
while (true)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 13");
|
||||
LoadApplication(option);
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Starting emu Test 14");
|
||||
if (_userChannelPersistence.PreviousIndex == -1 || !_userChannelPersistence.ShouldRestart)
|
||||
{
|
||||
break;
|
||||
@ -1377,7 +1396,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
AppleHV = true;
|
||||
}
|
||||
else if (OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
AppleHV = false;
|
||||
} else {
|
||||
|
@ -10,7 +10,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
return MemoryManagementWindows.Allocate((IntPtr)size);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
return MemoryManagementUnix.Allocate(size, forJit);
|
||||
}
|
||||
@ -26,7 +26,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
return MemoryManagementWindows.Reserve((IntPtr)size, viewCompatible);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
return MemoryManagementUnix.Reserve(size, forJit);
|
||||
}
|
||||
@ -42,7 +42,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
MemoryManagementWindows.Commit(address, (IntPtr)size);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
MemoryManagementUnix.Commit(address, size, forJit);
|
||||
}
|
||||
@ -58,7 +58,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
MemoryManagementWindows.Decommit(address, (IntPtr)size);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
MemoryManagementUnix.Decommit(address, size);
|
||||
}
|
||||
@ -74,7 +74,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
MemoryManagementUnix.MapView(sharedMemory, srcOffset, address, size);
|
||||
}
|
||||
@ -90,7 +90,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
MemoryManagementUnix.UnmapView(address, size);
|
||||
}
|
||||
@ -108,7 +108,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
result = MemoryManagementWindows.Reprotect(address, (IntPtr)size, permission, forView);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
result = MemoryManagementUnix.Reprotect(address, size, permission);
|
||||
}
|
||||
@ -129,7 +129,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
return MemoryManagementWindows.Free(address, (IntPtr)size);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
return MemoryManagementUnix.Free(address);
|
||||
}
|
||||
@ -145,7 +145,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
return MemoryManagementWindows.CreateSharedMemory((IntPtr)size, reserve);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
return MemoryManagementUnix.CreateSharedMemory(size, reserve);
|
||||
}
|
||||
@ -161,7 +161,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
MemoryManagementWindows.DestroySharedMemory(handle);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
MemoryManagementUnix.DestroySharedMemory(handle);
|
||||
}
|
||||
@ -177,7 +177,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
return MemoryManagementWindows.MapSharedMemory(handle);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
return MemoryManagementUnix.MapSharedMemory(handle, size);
|
||||
}
|
||||
@ -193,7 +193,7 @@ namespace Ryujinx.Memory
|
||||
{
|
||||
MemoryManagementWindows.UnmapSharedMemory(address);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
MemoryManagementUnix.UnmapSharedMemory(address, size);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -10,6 +11,7 @@ namespace Ryujinx.Memory
|
||||
[SupportedOSPlatform("linux")]
|
||||
[SupportedOSPlatform("macos")]
|
||||
[SupportedOSPlatform("ios")]
|
||||
[SupportedOSPlatform("android")]
|
||||
static class MemoryManagementUnix
|
||||
{
|
||||
private static readonly ConcurrentDictionary<IntPtr, ulong> _allocations = new();
|
||||
@ -173,6 +175,24 @@ namespace Ryujinx.Memory
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
byte[] memName = "Ryujinx-XXXXXX"u8.ToArray();
|
||||
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Creating Android SharedMemory of size:{size}");
|
||||
|
||||
fixed (byte* pMemName = memName)
|
||||
{
|
||||
fd = ASharedMemory_create((IntPtr)pMemName, (nuint)size);
|
||||
if (fd <= 0)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
}
|
||||
|
||||
// ASharedMemory_create handle ftruncate for us.
|
||||
return (IntPtr)fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] fileName = "/dev/shm/Ryujinx-XXXXXX"u8.ToArray();
|
||||
|
@ -7,6 +7,7 @@ namespace Ryujinx.Memory
|
||||
[SupportedOSPlatform("linux")]
|
||||
[SupportedOSPlatform("macos")]
|
||||
[SupportedOSPlatform("ios")]
|
||||
[SupportedOSPlatform("android")]
|
||||
public static partial class MemoryManagerUnixHelper
|
||||
{
|
||||
[Flags]
|
||||
@ -90,6 +91,9 @@ namespace Ryujinx.Memory
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
public static partial int shm_unlink(IntPtr name);
|
||||
|
||||
[DllImport("android")]
|
||||
internal static extern int ASharedMemory_create(IntPtr name, nuint size);
|
||||
|
||||
private static int MmapFlagsToSystemFlags(MmapFlags flags)
|
||||
{
|
||||
int result = 0;
|
||||
@ -111,7 +115,7 @@ namespace Ryujinx.Memory
|
||||
|
||||
if (flags.HasFlag(MmapFlags.MAP_ANONYMOUS))
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
if (OperatingSystem.IsLinux() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
result |= MAP_ANONYMOUS_LINUX_GENERIC;
|
||||
}
|
||||
@ -127,7 +131,7 @@ namespace Ryujinx.Memory
|
||||
|
||||
if (flags.HasFlag(MmapFlags.MAP_NORESERVE))
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
if (OperatingSystem.IsLinux() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
result |= MAP_NORESERVE_LINUX_GENERIC;
|
||||
}
|
||||
@ -143,7 +147,7 @@ namespace Ryujinx.Memory
|
||||
|
||||
if (flags.HasFlag(MmapFlags.MAP_UNLOCKED))
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
if (OperatingSystem.IsLinux() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
result |= MAP_UNLOCKED_LINUX_GENERIC;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user