1
0
forked from MeloNX/MeloNX

WIP: Android support

This commit is contained in:
Daniil Vinogradov 2025-03-05 02:00:16 +01:00
parent 8b37abec1e
commit 89777c18f1
22 changed files with 737 additions and 100 deletions

View File

@ -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,10 +113,16 @@ 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)
{
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);

View File

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace ARMeilleure.Signal
{
@ -20,9 +21,25 @@ namespace ARMeilleure.Signal
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,15 +83,36 @@ namespace ARMeilleure.Signal
public static SigAction RegisterExceptionHandler(IntPtr action)
{
SigAction sig = new()
int result;
SigAction old;
if (Ryujinx.Common.PlatformInfo.IsBionic)
{
SigActionBionic sig = new()
{
sa_handler = action,
sa_flags = SA_SIGINFO,
sa_flags = SA_SIGINFO | SA_ONSTACK,
};
sigemptyset(ref sig.sa_mask);
int result = sigaction(SIGSEGV, ref sig, out SigAction old);
result = sigaction(SIGSEGV, ref sig, 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
{
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)
{
@ -71,13 +128,36 @@ namespace ARMeilleure.Signal
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
}
}
}
return old;
}
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;
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ import Darwin
private init() {}
private var isRunning = false
private var basePath: URL!
}
@ -44,7 +45,7 @@ public extension Ryujinx {
let version = fetchFirmwareVersion()
if !version.isEmpty {
self.firmwareversion = version
firmwareversion = version
}
}
@ -52,7 +53,7 @@ public extension Ryujinx {
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
}
}
}

View File

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

View File

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

View File

@ -1,5 +1,6 @@
import SwiftUI
import MeloNXModel
import OSLog
enum ContentTab: String, Hashable {
case games, home, settings
@ -10,11 +11,30 @@ struct ContentView: View {
@State var viewModel = ViewModel()
@State var appearance = ""
@State private var config: Ryujinx.Configuration
@State private var game: Game?
@State private var isLoading = true
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()
GamesView(startemu: $game)
}
}
.tabItem { Label("Games", systemImage: "house.fill") }
@ -30,15 +50,76 @@ struct ContentView: View {
.environment(viewModel)
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
.onAppear {
initializeSDL()
initialize()
}
}
}
// 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)")
}
}
}

View File

@ -56,6 +56,9 @@
}
}
}
},
"EmulationView" : {
},
"Firmware" : {
@ -151,6 +154,9 @@
}
}
}
},
"Loading..." : {
},
"Mii Maker" : {

View File

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

View File

@ -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,8 +91,12 @@ struct GamesView: View {
struct GameView: View {
@State var game: Game
@Binding var startemu: Game?
var body: some View {
Button(action: {
startemu = game
}) {
HStack(spacing: 16) {
ZStack {
if let iconData = game.icon,
@ -129,3 +134,4 @@ struct GameView: View {
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -25,7 +25,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request?
};
if (!Ryujinx.Common.PlatformInfo.IsBionic)
{
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
}
GeneralServiceManager.Add(_generalServiceDetail);
}
@ -195,8 +198,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
if (!Ryujinx.Common.PlatformInfo.IsBionic)
{
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
}
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
}

View File

@ -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);
@ -1175,6 +1186,7 @@ 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 {

View File

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

View File

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

View File

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