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.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using ARMeilleure.Translation.Cache;
|
using ARMeilleure.Translation.Cache;
|
||||||
|
using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -112,10 +113,16 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
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));
|
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
||||||
|
|
||||||
|
if (PlatformInfo.IsBionic)
|
||||||
|
{
|
||||||
|
config.StructAddressOffset = 16; // si_addr
|
||||||
|
config.StructWriteOffset = 8; // si_code
|
||||||
|
}
|
||||||
|
|
||||||
if (customSignalHandlerFactory != null)
|
if (customSignalHandlerFactory != null)
|
||||||
{
|
{
|
||||||
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
namespace ARMeilleure.Signal
|
||||||
{
|
{
|
||||||
@ -20,9 +21,25 @@ namespace ARMeilleure.Signal
|
|||||||
public IntPtr sa_restorer;
|
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 SIGSEGV = 11;
|
||||||
private const int SIGBUS = 10;
|
private const int SIGBUS = 10;
|
||||||
private const int SA_SIGINFO = 0x00000004;
|
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)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
||||||
@ -35,7 +52,26 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
public static SigAction GetSegfaultExceptionHandler()
|
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)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
@ -47,15 +83,36 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
public static SigAction RegisterExceptionHandler(IntPtr action)
|
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_handler = action,
|
||||||
sa_flags = SA_SIGINFO,
|
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);
|
||||||
|
|
||||||
|
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)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
@ -71,13 +128,36 @@ namespace ARMeilleure.Signal
|
|||||||
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
|
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool RestoreExceptionHandler(SigAction oldAction)
|
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.IsMacOS() ? 1u : 0u) << 2;
|
||||||
osPlatform |= (OperatingSystem.IsWindows() ? 1u : 0u) << 3;
|
osPlatform |= (OperatingSystem.IsWindows() ? 1u : 0u) << 3;
|
||||||
osPlatform |= (OperatingSystem.IsIOS() ? 1u : 0u) << 4;
|
osPlatform |= (OperatingSystem.IsIOS() ? 1u : 0u) << 4;
|
||||||
|
osPlatform |= (Ryujinx.Common.PlatformInfo.IsBionic ? 1u : 0u) << 5;
|
||||||
#pragma warning restore IDE0055
|
#pragma warning restore IDE0055
|
||||||
|
|
||||||
return osPlatform;
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ import Darwin
|
|||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
|
private var isRunning = false
|
||||||
private var basePath: URL!
|
private var basePath: URL!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ public extension Ryujinx {
|
|||||||
|
|
||||||
let version = fetchFirmwareVersion()
|
let version = fetchFirmwareVersion()
|
||||||
if !version.isEmpty {
|
if !version.isEmpty {
|
||||||
self.firmwareversion = version
|
firmwareversion = version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ public extension Ryujinx {
|
|||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
let romsDirectory = basePath.appendingPathComponent("roms")
|
let romsDirectory = basePath.appendingPathComponent("roms")
|
||||||
|
|
||||||
if (!fileManager.fileExists(atPath: romsDirectory.path)) {
|
if !fileManager.fileExists(atPath: romsDirectory.path) {
|
||||||
do {
|
do {
|
||||||
try fileManager.createDirectory(at: romsDirectory, withIntermediateDirectories: true, attributes: nil)
|
try fileManager.createDirectory(at: romsDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||||
} catch {
|
} catch {
|
||||||
@ -88,6 +89,237 @@ public extension Ryujinx {
|
|||||||
logger.error("Error loading games from roms folder: \(error)")
|
logger.error("Error loading games from roms folder: \(error)")
|
||||||
self.games = []
|
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)
|
let f = unsafeBitCast(sym, to: initializeFunc.self)
|
||||||
return f(fileDescriptor, fileURLCandidateCString)
|
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 {
|
//public struct GameInfo {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MeloNXModel
|
import MeloNXModel
|
||||||
|
import OSLog
|
||||||
|
|
||||||
enum ContentTab: String, Hashable {
|
enum ContentTab: String, Hashable {
|
||||||
case games, home, settings
|
case games, home, settings
|
||||||
@ -10,11 +11,30 @@ struct ContentView: View {
|
|||||||
@State var viewModel = ViewModel()
|
@State var viewModel = ViewModel()
|
||||||
@State var appearance = ""
|
@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 {
|
var body: some View {
|
||||||
|
if game != nil {
|
||||||
|
if isLoading {
|
||||||
|
ZStack {
|
||||||
|
emulationView
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EmulationView()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
TabView(selection: $tab) {
|
TabView(selection: $tab) {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
GamesView()
|
GamesView(startemu: $game)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tabItem { Label("Games", systemImage: "house.fill") }
|
.tabItem { Label("Games", systemImage: "house.fill") }
|
||||||
@ -30,15 +50,76 @@ struct ContentView: View {
|
|||||||
.environment(viewModel)
|
.environment(viewModel)
|
||||||
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
|
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
initializeSDL()
|
initialize()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helper Methods
|
// 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.
|
// 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()
|
// setMoltenVKSettings()
|
||||||
let path = URL.documentsDirectory
|
let path = URL.documentsDirectory
|
||||||
Ryujinx.shared.initialize(basePath: path)
|
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" : {
|
"Firmware" : {
|
||||||
|
|
||||||
@ -151,6 +154,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Loading..." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Mii Maker" : {
|
"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
|
import SwiftUI
|
||||||
|
|
||||||
struct GamesView: View {
|
struct GamesView: View {
|
||||||
|
@Binding var startemu: Game?
|
||||||
@State var firmwareversion = "0"
|
@State var firmwareversion = "0"
|
||||||
@State var searchQuery: String = ""
|
@State var searchQuery: String = ""
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ struct GamesView: View {
|
|||||||
List {
|
List {
|
||||||
// Section {
|
// Section {
|
||||||
ForEach(games) { game in
|
ForEach(games) { game in
|
||||||
GameView(game: game)
|
GameView(game: game, startemu: $startemu)
|
||||||
}
|
}
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
@ -90,8 +91,12 @@ struct GamesView: View {
|
|||||||
|
|
||||||
struct GameView: View {
|
struct GameView: View {
|
||||||
@State var game: Game
|
@State var game: Game
|
||||||
|
@Binding var startemu: Game?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
Button(action: {
|
||||||
|
startemu = game
|
||||||
|
}) {
|
||||||
HStack(spacing: 16) {
|
HStack(spacing: 16) {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let iconData = game.icon,
|
if let iconData = game.icon,
|
||||||
@ -129,3 +134,4 @@ struct GameView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ import Foundation
|
|||||||
import OSLog
|
import OSLog
|
||||||
import SwiftUI
|
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
|
/// 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) })
|
let androidSDK = ProcessInfo.processInfo.environment["android.os.Build.VERSION.SDK_INT"].flatMap({ Int($0) })
|
||||||
|
@ -35,6 +35,11 @@ namespace Ryujinx.Common
|
|||||||
return "ios";
|
return "ios";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PlatformInfo.IsBionic)
|
||||||
|
{
|
||||||
|
return "android";
|
||||||
|
}
|
||||||
|
|
||||||
if (IsValid())
|
if (IsValid())
|
||||||
{
|
{
|
||||||
return BuildVersion;
|
return BuildVersion;
|
||||||
@ -51,7 +56,7 @@ namespace Ryujinx.Common
|
|||||||
#else
|
#else
|
||||||
public static string GetBaseApplicationDirectory()
|
public static string GetBaseApplicationDirectory()
|
||||||
{
|
{
|
||||||
if (IsFlatHubBuild() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
if (IsFlatHubBuild() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || PlatformInfo.IsBionic)
|
||||||
{
|
{
|
||||||
return AppDataManager.BaseDirPath;
|
return AppDataManager.BaseDirPath;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,12 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
|||||||
Console.WriteLine($"[iOS] Required firmware library: {libName}");
|
Console.WriteLine($"[iOS] Required firmware library: {libName}");
|
||||||
return 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
|
else
|
||||||
{
|
{
|
||||||
throw new NotImplementedException($"Unsupported OS for FFmpeg: {RuntimeInformation.RuntimeIdentifier}");
|
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?
|
IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!Ryujinx.Common.PlatformInfo.IsBionic)
|
||||||
|
{
|
||||||
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
||||||
|
}
|
||||||
|
|
||||||
GeneralServiceManager.Add(_generalServiceDetail);
|
GeneralServiceManager.Add(_generalServiceDetail);
|
||||||
}
|
}
|
||||||
@ -195,8 +198,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
if (isDisposing)
|
if (isDisposing)
|
||||||
|
{
|
||||||
|
if (!Ryujinx.Common.PlatformInfo.IsBionic)
|
||||||
{
|
{
|
||||||
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
||||||
|
}
|
||||||
|
|
||||||
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
||||||
}
|
}
|
||||||
|
@ -123,20 +123,23 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
{
|
{
|
||||||
string[] args = new string[argCount];
|
string[] args = new string[argCount];
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 1");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 2");
|
||||||
for (int i = 0; i < argCount; i++)
|
for (int i = 0; i < argCount; i++)
|
||||||
{
|
{
|
||||||
args[i] = Marshal.PtrToStringAnsi(pArgs[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);
|
Main(args);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Console.WriteLine(e.ToString());
|
Logger.Info?.Print(LogClass.Application, $"Emulator start failed with reason: {e}");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,20 +323,24 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
Silk.NET.Core.Loader.SearchPathContainer.Platform = Silk.NET.Core.Loader.UnderlyingPlatform.MacOS;
|
Silk.NET.Core.Loader.SearchPathContainer.Platform = Silk.NET.Core.Loader.UnderlyingPlatform.MacOS;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Start Emu Test 4");
|
||||||
Version = ReleaseInformation.GetVersion();
|
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)";
|
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);
|
AutoResetEvent invoked = new(false);
|
||||||
|
|
||||||
// MacOS must perform SDL polls from the main thread.
|
// MacOS must perform SDL polls from the main thread.
|
||||||
SDL2Driver.MainThreadDispatcher = action =>
|
SDL2Driver.MainThreadDispatcher = action =>
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 3");
|
||||||
invoked.Reset();
|
invoked.Reset();
|
||||||
|
|
||||||
WindowBase.QueueMainThreadAction(() =>
|
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)
|
var result = Parser.Default.ParseArguments<Options>(args)
|
||||||
.WithParsed(options =>
|
.WithParsed(options =>
|
||||||
{
|
{
|
||||||
@ -935,7 +943,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
if (gamepad == null)
|
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";
|
inputId = "0";
|
||||||
|
|
||||||
@ -1141,11 +1149,13 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
static void Load(Options option)
|
static void Load(Options option)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 4");
|
||||||
if (_virtualFileSystem == null)
|
if (_virtualFileSystem == null)
|
||||||
{
|
{
|
||||||
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 5");
|
||||||
if (_libHacHorizonManager == null)
|
if (_libHacHorizonManager == null)
|
||||||
{
|
{
|
||||||
_libHacHorizonManager = new LibHacHorizonManager();
|
_libHacHorizonManager = new LibHacHorizonManager();
|
||||||
@ -1155,6 +1165,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_libHacHorizonManager.InitializeSystemClients();
|
_libHacHorizonManager.InitializeSystemClients();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 6");
|
||||||
if (_contentManager == null)
|
if (_contentManager == null)
|
||||||
{
|
{
|
||||||
_contentManager = new ContentManager(_virtualFileSystem);
|
_contentManager = new ContentManager(_virtualFileSystem);
|
||||||
@ -1175,6 +1186,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 7");
|
||||||
GraphicsConfig.EnableShaderCache = true;
|
GraphicsConfig.EnableShaderCache = true;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||||
@ -1213,6 +1225,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 8");
|
||||||
if (option.InputPath == null)
|
if (option.InputPath == null)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, "Please provide a file to load");
|
Logger.Error?.Print(LogClass.Application, "Please provide a file to load");
|
||||||
@ -1220,6 +1233,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 9");
|
||||||
if (option.InputPath == "MiiMaker") {
|
if (option.InputPath == "MiiMaker") {
|
||||||
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
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.Guest, !option.LoggingDisableGuest);
|
||||||
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 10");
|
||||||
if (!option.DisableFileLog)
|
if (!option.DisableFileLog)
|
||||||
{
|
{
|
||||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||||
@ -1274,6 +1289,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 11");
|
||||||
// Setup graphics configuration
|
// Setup graphics configuration
|
||||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||||
GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression;
|
GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression;
|
||||||
@ -1282,10 +1298,13 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
|
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
|
||||||
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
|
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 12");
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 13");
|
||||||
LoadApplication(option);
|
LoadApplication(option);
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Starting emu Test 14");
|
||||||
if (_userChannelPersistence.PreviousIndex == -1 || !_userChannelPersistence.ShouldRestart)
|
if (_userChannelPersistence.PreviousIndex == -1 || !_userChannelPersistence.ShouldRestart)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@ -1377,7 +1396,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
{
|
{
|
||||||
AppleHV = true;
|
AppleHV = true;
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsIOS())
|
else if (OperatingSystem.IsIOS() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||||
{
|
{
|
||||||
AppleHV = false;
|
AppleHV = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,7 +10,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
return MemoryManagementWindows.Allocate((IntPtr)size);
|
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);
|
return MemoryManagementUnix.Allocate(size, forJit);
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
return MemoryManagementWindows.Reserve((IntPtr)size, viewCompatible);
|
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);
|
return MemoryManagementUnix.Reserve(size, forJit);
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
MemoryManagementWindows.Commit(address, (IntPtr)size);
|
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);
|
MemoryManagementUnix.Commit(address, size, forJit);
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
MemoryManagementWindows.Decommit(address, (IntPtr)size);
|
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);
|
MemoryManagementUnix.Decommit(address, size);
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner);
|
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);
|
MemoryManagementUnix.MapView(sharedMemory, srcOffset, address, size);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner);
|
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);
|
MemoryManagementUnix.UnmapView(address, size);
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
result = MemoryManagementWindows.Reprotect(address, (IntPtr)size, permission, forView);
|
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);
|
result = MemoryManagementUnix.Reprotect(address, size, permission);
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
return MemoryManagementWindows.Free(address, (IntPtr)size);
|
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);
|
return MemoryManagementUnix.Free(address);
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
return MemoryManagementWindows.CreateSharedMemory((IntPtr)size, reserve);
|
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);
|
return MemoryManagementUnix.CreateSharedMemory(size, reserve);
|
||||||
}
|
}
|
||||||
@ -161,7 +161,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
MemoryManagementWindows.DestroySharedMemory(handle);
|
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);
|
MemoryManagementUnix.DestroySharedMemory(handle);
|
||||||
}
|
}
|
||||||
@ -177,7 +177,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
return MemoryManagementWindows.MapSharedMemory(handle);
|
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);
|
return MemoryManagementUnix.MapSharedMemory(handle, size);
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
MemoryManagementWindows.UnmapSharedMemory(address);
|
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);
|
MemoryManagementUnix.UnmapSharedMemory(address, size);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Logging;
|
||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -10,6 +11,7 @@ namespace Ryujinx.Memory
|
|||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
[SupportedOSPlatform("ios")]
|
[SupportedOSPlatform("ios")]
|
||||||
|
[SupportedOSPlatform("android")]
|
||||||
static class MemoryManagementUnix
|
static class MemoryManagementUnix
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<IntPtr, ulong> _allocations = new();
|
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
|
else
|
||||||
{
|
{
|
||||||
byte[] fileName = "/dev/shm/Ryujinx-XXXXXX"u8.ToArray();
|
byte[] fileName = "/dev/shm/Ryujinx-XXXXXX"u8.ToArray();
|
||||||
|
@ -7,6 +7,7 @@ namespace Ryujinx.Memory
|
|||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
[SupportedOSPlatform("ios")]
|
[SupportedOSPlatform("ios")]
|
||||||
|
[SupportedOSPlatform("android")]
|
||||||
public static partial class MemoryManagerUnixHelper
|
public static partial class MemoryManagerUnixHelper
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
@ -90,6 +91,9 @@ namespace Ryujinx.Memory
|
|||||||
[LibraryImport("libc", SetLastError = true)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
public static partial int shm_unlink(IntPtr name);
|
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)
|
private static int MmapFlagsToSystemFlags(MmapFlags flags)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
@ -111,7 +115,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (flags.HasFlag(MmapFlags.MAP_ANONYMOUS))
|
if (flags.HasFlag(MmapFlags.MAP_ANONYMOUS))
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsLinux())
|
if (OperatingSystem.IsLinux() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||||
{
|
{
|
||||||
result |= MAP_ANONYMOUS_LINUX_GENERIC;
|
result |= MAP_ANONYMOUS_LINUX_GENERIC;
|
||||||
}
|
}
|
||||||
@ -127,7 +131,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (flags.HasFlag(MmapFlags.MAP_NORESERVE))
|
if (flags.HasFlag(MmapFlags.MAP_NORESERVE))
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsLinux())
|
if (OperatingSystem.IsLinux() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||||
{
|
{
|
||||||
result |= MAP_NORESERVE_LINUX_GENERIC;
|
result |= MAP_NORESERVE_LINUX_GENERIC;
|
||||||
}
|
}
|
||||||
@ -143,7 +147,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (flags.HasFlag(MmapFlags.MAP_UNLOCKED))
|
if (flags.HasFlag(MmapFlags.MAP_UNLOCKED))
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsLinux())
|
if (OperatingSystem.IsLinux() || Ryujinx.Common.PlatformInfo.IsBionic)
|
||||||
{
|
{
|
||||||
result |= MAP_UNLOCKED_LINUX_GENERIC;
|
result |= MAP_UNLOCKED_LINUX_GENERIC;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user