diff --git a/src/ARMeilleure/Signal/NativeSignalHandler.cs b/src/ARMeilleure/Signal/NativeSignalHandler.cs index 40860a5d7..989935608 100644 --- a/src/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/src/ARMeilleure/Signal/NativeSignalHandler.cs @@ -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) { diff --git a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs b/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs index 433aab636..e024d1006 100644 --- a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs +++ b/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs @@ -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; + } } } } diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 5ed27927a..d288f6ce3 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -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; diff --git a/src/MeloNX-Skip/melonx-native/Android/app/src/main/jniLibs/arm64-v8a/libRyujinx.Headless.SDL2.so b/src/MeloNX-Skip/melonx-native/Android/app/src/main/jniLibs/arm64-v8a/libRyujinx.Headless.SDL2.so index 247b6e341..6f790fbc7 100644 Binary files a/src/MeloNX-Skip/melonx-native/Android/app/src/main/jniLibs/arm64-v8a/libRyujinx.Headless.SDL2.so and b/src/MeloNX-Skip/melonx-native/Android/app/src/main/jniLibs/arm64-v8a/libRyujinx.Headless.SDL2.so differ diff --git a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/AspectRatio.swift b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/AspectRatio.swift new file mode 100644 index 000000000..08c94782d --- /dev/null +++ b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/AspectRatio.swift @@ -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)" + } + } +} diff --git a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/Language.swift b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/Language.swift new file mode 100644 index 000000000..ff3732f25 --- /dev/null +++ b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/Language.swift @@ -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" + } + } +} diff --git a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/Region.swift b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/Region.swift new file mode 100644 index 000000000..249f03912 --- /dev/null +++ b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Models/Enums/Region.swift @@ -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" + } + } +} + diff --git a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Ryujinx.swift b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Ryujinx.swift index f66735639..7342bcbe2 100644 --- a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Ryujinx.swift +++ b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/Ryujinx.swift @@ -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 + } } } diff --git a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/RyujinxError.swift b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/RyujinxError.swift new file mode 100644 index 000000000..12b56a730 --- /dev/null +++ b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/RyujinxError.swift @@ -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 +} diff --git a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/RyujinxLib.swift b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/RyujinxLib.swift index ff42b5c35..66bddae2c 100644 --- a/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/RyujinxLib.swift +++ b/src/MeloNX-Skip/melonx-native/Sources/MeloNXModel/RyujinxLib.swift @@ -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?>) -> (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 { diff --git a/src/MeloNX-Skip/melonx-native/Sources/melonx/ContentView.swift b/src/MeloNX-Skip/melonx-native/Sources/melonx/ContentView.swift index b6c18dc99..09b6973d8 100644 --- a/src/MeloNX-Skip/melonx-native/Sources/melonx/ContentView.swift +++ b/src/MeloNX-Skip/melonx-native/Sources/melonx/ContentView.swift @@ -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)") + } + } } diff --git a/src/MeloNX-Skip/melonx-native/Sources/melonx/Resources/Localizable.xcstrings b/src/MeloNX-Skip/melonx-native/Sources/melonx/Resources/Localizable.xcstrings index 6a2ed625a..bca82a15d 100644 --- a/src/MeloNX-Skip/melonx-native/Sources/melonx/Resources/Localizable.xcstrings +++ b/src/MeloNX-Skip/melonx-native/Sources/melonx/Resources/Localizable.xcstrings @@ -56,6 +56,9 @@ } } } + }, + "EmulationView" : { + }, "Firmware" : { @@ -151,6 +154,9 @@ } } } + }, + "Loading..." : { + }, "Mii Maker" : { diff --git a/src/MeloNX-Skip/melonx-native/Sources/melonx/Screens/EmulationView.swift b/src/MeloNX-Skip/melonx-native/Sources/melonx/Screens/EmulationView.swift new file mode 100644 index 000000000..1bf0ef078 --- /dev/null +++ b/src/MeloNX-Skip/melonx-native/Sources/melonx/Screens/EmulationView.swift @@ -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") + } + } +} diff --git a/src/MeloNX-Skip/melonx-native/Sources/melonx/Screens/GamesView.swift b/src/MeloNX-Skip/melonx-native/Sources/melonx/Screens/GamesView.swift index 7b1263201..519284bf8 100644 --- a/src/MeloNX-Skip/melonx-native/Sources/melonx/Screens/GamesView.swift +++ b/src/MeloNX-Skip/melonx-native/Sources/melonx/Screens/GamesView.swift @@ -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) } } } diff --git a/src/MeloNX-Skip/melonx-native/Sources/melonx/melonxApp.swift b/src/MeloNX-Skip/melonx-native/Sources/melonx/melonxApp.swift index 9f72dcb20..67535a0b3 100644 --- a/src/MeloNX-Skip/melonx-native/Sources/melonx/melonxApp.swift +++ b/src/MeloNX-Skip/melonx-native/Sources/melonx/melonxApp.swift @@ -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) }) diff --git a/src/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs index bf68cbbc8..a7a75ec67 100644 --- a/src/Ryujinx.Common/ReleaseInformation.cs +++ b/src/Ryujinx.Common/ReleaseInformation.cs @@ -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; } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs index 2e963cdda..cb7657741 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs @@ -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}"); diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index 581a2906b..697f872b7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -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); } diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index e8c5dbdbd..e5ae4f1d2 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -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(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 { diff --git a/src/Ryujinx.Memory/MemoryManagement.cs b/src/Ryujinx.Memory/MemoryManagement.cs index a23fafb57..780a293ea 100644 --- a/src/Ryujinx.Memory/MemoryManagement.cs +++ b/src/Ryujinx.Memory/MemoryManagement.cs @@ -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); } diff --git a/src/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs index 948bf8023..9bf4902c8 100644 --- a/src/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/src/Ryujinx.Memory/MemoryManagementUnix.cs @@ -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 _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(); diff --git a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs index 6ed4d2387..ef43de047 100644 --- a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs +++ b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs @@ -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; }