diff --git a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj index bcf3e414d..7ea7eb394 100644 --- a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj +++ b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj @@ -58,51 +58,11 @@ }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ -/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ - 4E80AA0A2CD6FAA800029585 /* Exceptions for "MeloNX" folder in "Embed Libraries" phase from "MeloNX" target */ = { - isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; - attributesByRelativePath = { - "Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = (CodeSignOnCopy, ); - "Dependencies/Dynamic Libraries/libMoltenVK.dylib" = (CodeSignOnCopy, ); - "Dependencies/Dynamic Libraries/libavcodec.dylib" = (CodeSignOnCopy, ); - "Dependencies/Dynamic Libraries/libavutil.dylib" = (CodeSignOnCopy, ); - Dependencies/XCFrameworks/MoltenVK.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/SDL2.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libSPIRV.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libavcodec.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libavfilter.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libavformat.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libavutil.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libswresample.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libswscale.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - Dependencies/XCFrameworks/libteakra.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); - }; - buildPhase = 4E80AA092CD6FAA800029585 /* Embed Libraries */; - membershipExceptions = ( - "Dependencies/Dynamic Libraries/libavcodec.dylib", - "Dependencies/Dynamic Libraries/libavutil.dylib", - "Dependencies/Dynamic Libraries/libMoltenVK.dylib", - "Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib", - Dependencies/XCFrameworks/libavcodec.xcframework, - Dependencies/XCFrameworks/libavfilter.xcframework, - Dependencies/XCFrameworks/libavformat.xcframework, - Dependencies/XCFrameworks/libavutil.xcframework, - Dependencies/XCFrameworks/libSPIRV.xcframework, - Dependencies/XCFrameworks/libswresample.xcframework, - Dependencies/XCFrameworks/libswscale.xcframework, - Dependencies/XCFrameworks/libteakra.xcframework, - Dependencies/XCFrameworks/MoltenVK.xcframework, - Dependencies/XCFrameworks/SDL2.xcframework, - ); - }; -/* End PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ - /* Begin PBXFileSystemSynchronizedRootGroup section */ 4E80A98F2CD6F54500029585 /* MeloNX */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( 4E80AA1D2CD7015100029585 /* Exceptions for "MeloNX" folder in "MeloNX" target */, - 4E80AA0A2CD6FAA800029585 /* Exceptions for "MeloNX" folder in "Embed Libraries" phase from "MeloNX" target */, ); path = MeloNX; sourceTree = ""; @@ -485,7 +445,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = 4TD3JXVDW7; + DEVELOPMENT_TEAM = 95J8WZ4TN8; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MeloNX/Info.plist; @@ -565,9 +525,18 @@ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.benlawrence.MeloNX; + PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/Core/Headers/Ryujinx-Header.h"; @@ -585,7 +554,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = 4TD3JXVDW7; + DEVELOPMENT_TEAM = 95J8WZ4TN8; ENABLE_PREVIEWS = YES; GCC_OPTIMIZATION_LEVEL = 3; GENERATE_INFOPLIST_FILE = YES; @@ -666,9 +635,18 @@ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.benlawrence.MeloNX; + PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/Core/Headers/Ryujinx-Header.h"; diff --git a/src/MeloNX/MeloNX/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/src/MeloNX/MeloNX/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json index 230588010..635ee3d18 100644 --- a/src/MeloNX/MeloNX/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/src/MeloNX/MeloNX/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "nxgradientpng.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/src/MeloNX/MeloNX/Assets/Assets.xcassets/AppIcon.appiconset/nxgradientpng.png b/src/MeloNX/MeloNX/Assets/Assets.xcassets/AppIcon.appiconset/nxgradientpng.png new file mode 100644 index 000000000..421f7d484 Binary files /dev/null and b/src/MeloNX/MeloNX/Assets/Assets.xcassets/AppIcon.appiconset/nxgradientpng.png differ diff --git a/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift b/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift index fb9663123..83fa39b3b 100644 --- a/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift +++ b/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift @@ -11,8 +11,8 @@ import SDL2 import GameController struct Controller: Identifiable, Hashable { - let id: String - let name: String + var id: String + var name: String } struct iOSNav: View { @@ -45,11 +45,11 @@ class Ryujinx { var debuglogs: Bool var tracelogs: Bool var nintendoinput: Bool - var ryuLDN: Bool + var enableInternet: Bool var listinputids: Bool var fullscreen: Bool var memoryManagerMode: String - var vsync: String + var disableVSync: Bool var disableShaderCache: Bool var disableDockedMode: Bool var enableTextureRecompression: Bool @@ -60,14 +60,14 @@ class Ryujinx { inputids: [String] = [], debuglogs: Bool = false, tracelogs: Bool = false, - nintendoinput: Bool = true, - ryuLDN: Bool = false, listinputids: Bool = false, fullscreen: Bool = true, - memoryManagerMode: String = "HostMappedUnsafe", - vsync: String = "Switch", + memoryManagerMode: String = "HostMapped", + disableVSync: Bool = false, disableShaderCache: Bool = false, disableDockedMode: Bool = false, + nintendoinput: Bool = true, + enableInternet: Bool = false, enableTextureRecompression: Bool = true, additionalArgs: [String] = [], resscale: Float = 1.00 @@ -76,17 +76,17 @@ class Ryujinx { self.inputids = inputids self.debuglogs = debuglogs self.tracelogs = tracelogs - self.nintendoinput = nintendoinput - self.ryuLDN = ryuLDN self.listinputids = listinputids self.fullscreen = fullscreen - self.vsync = vsync + self.disableVSync = disableVSync self.disableShaderCache = disableShaderCache self.disableDockedMode = disableDockedMode self.enableTextureRecompression = enableTextureRecompression self.additionalArgs = additionalArgs self.memoryManagerMode = memoryManagerMode self.resscale = resscale + self.nintendoinput = nintendoinput + self.enableInternet = enableInternet } } @@ -157,15 +157,17 @@ class Ryujinx { args.append(contentsOf: ["--resolution-scale", String(config.resscale)]) } - // Adding default args directly into additionalArgs if config.nintendoinput { -// args.append("--correct-ons-controller") + // args.append("--correct-ons-controller") + } + if config.enableInternet { + args.append("--enable-internet-connection") } - if config.ryuLDN { - args.append("--lan-interface-id LdnRyu") + // Adding default args directly into additionalArgs + if config.disableVSync { + // args.append("--disable-vsync") } - if config.disableShaderCache { args.append("--disable-shader-cache") } @@ -202,8 +204,9 @@ class Ryujinx { } func getConnectedControllers() -> [Controller] { - - guard let jsonPtr = get_game_controllers() else { + var nill: String? + + guard let jsonPtr = nill else {//get_game_controllers() else { return [] } diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libSDL2.dylib b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libSDL2.dylib deleted file mode 100644 index a07334895..000000000 Binary files a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libSDL2.dylib and /dev/null differ diff --git a/src/MeloNX/MeloNX/Views/ContentView.swift b/src/MeloNX/MeloNX/Views/ContentView.swift index 74f7611e6..ff163bbf1 100644 --- a/src/MeloNX/MeloNX/Views/ContentView.swift +++ b/src/MeloNX/MeloNX/Views/ContentView.swift @@ -25,6 +25,7 @@ struct ContentView: View { @State private var config: Ryujinx.Configuration @State private var settings: [MoltenVKSettings] @State private var isVirtualControllerActive: Bool = false + @State var onscreencontroller: Controller = Controller(id: "", name: "") // MARK: - Initialization init() { @@ -84,16 +85,17 @@ struct ContentView: View { Section("Settings") { NavigationLink("Config") { SettingsView(config: $config, MoltenVKSettings: $settings) + .onAppear() { + virtualController?.disconnect() + } } } Section("Controller") { Button("Refresh", action: refreshControllersList) - + Divider() ForEach(controllersList, id: \.self) { controller in - if controller.name != "Apple Touch Controller" { - controllerRow(for: controller) - } + controllerRow(for: controller) } } } @@ -128,15 +130,11 @@ struct ContentView: View { virtualController = GCVirtualController(configuration: configuration) virtualController?.connect() - controllersList.removeAll(where: { $0.name == "Apple Touch Controller" }) } private func destroyVirtualController() { virtualController?.disconnect() virtualController = nil - - // Remove virtual controller from current controllers - controllersList.removeAll(where: { $0.name == "Apple Touch Controller" }) } // MARK: - Helper Methods @@ -152,33 +150,37 @@ struct ContentView: View { private func setupEmulation() { virtualController?.disconnect() - - controllerCallback = { - DispatchQueue.main.async { -// controllersList = Ryujinx.shared.getConnectedControllers() - currentControllers.removeAll(where: { $0.name == "Apple Touch Controller" }) - if controllersList.count == 2, - controllersList.contains(where: { $0.name == "Apple Touch Controller" }) { - currentControllers.append(controllersList[1]) + if controllersList.first(where: { $0 == onscreencontroller}) != nil { + controllerCallback = { + DispatchQueue.main.async { + controllersList = Ryujinx.shared.getConnectedControllers() + + print(currentControllers) + start(displayid: 1) } - + } + + + showVirtualController() + } else { + + DispatchQueue.main.async { print(currentControllers) start(displayid: 1) } } - - - showVirtualController() } private func refreshControllersList() { Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in -// controllersList = Ryujinx.shared.getConnectedControllers() - controllersList.removeAll(where: { $0.id == "0" }) - - controllersList.removeAll(where: { $0.name == "Apple Touch Controller" }) - - if let controller = controllersList.first, !controllersList.isEmpty { + controllersList = Ryujinx.shared.getConnectedControllers() + var controller = controllersList.first(where: { $0.name.hasPrefix("Apple")}) + self.onscreencontroller = (controller ?? Controller(id: "", name: "")) + if controllersList.count > 2 { + let controller = controllersList[2] + currentControllers.append(controller) + + } else if let controller = controllersList.first(where: { $0.id == onscreencontroller.id }), !controllersList.isEmpty { currentControllers.append(controller) } } diff --git a/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift index b2443a25b..ddd89965f 100644 --- a/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift +++ b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift @@ -12,29 +12,21 @@ struct SettingsView: View { @Binding var MoltenVKSettings: [MoltenVKSettings] var memoryManagerModes = [ - ("HostMappedUnsafe", "Host Unchecked (fastest, unstable / unsafe)"), ("HostMapped", "Host (fast)"), - ("SoftwarePageTable", "Software") - ] - - var vsyncModes = [ - ("Switch", "Switch"), - ("Unbound", "Unbound"), + ("HostMappedUnsafe", "Host Unchecked (fast, unstable / unsafe)"), + ("SoftwarePageTable", "Software (slow)"), ] @AppStorage("RyuDemoControls") var ryuDemo: Bool = false + @AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false var body: some View { ScrollView { VStack { - Section(header: Text("Graphics and Performance").bold()) { + Section(header: Title("Graphics and Performance")) { Toggle("Ryujinx Fullscreen", isOn: $config.fullscreen) - Picker("V-Sync", selection: $config.vsync) { - ForEach(vsyncModes, id: \.0) { key, displayName in - Text(displayName).tag(key) - } - } + Toggle("Disable V-Sync", isOn: $config.disableVSync) Toggle("Disable Shader Cache", isOn: $config.disableShaderCache) Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression) Toggle("Disable Docked Mode", isOn: $config.disableDockedMode) @@ -49,20 +41,18 @@ struct SettingsView: View { } } - Section(header: Text("Input Settings").bold()) { + Section(header: Title("Input Settings")) { Toggle("List Input IDs", isOn: $config.listinputids) Toggle("Nintendo Controller Layout", isOn: $config.nintendoinput) Toggle("Ryujinx Demo On-Screen Controller", isOn: $ryuDemo) // Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory) - Toggle("Disable Docked Mode", isOn: $config.disableDockedMode) } - Section(header: Text("Logging Settings").bold()) { + Section(header: Title("Logging Settings")) { Toggle("Enable Debug Logs", isOn: $config.debuglogs) Toggle("Enable Trace Logs", isOn: $config.tracelogs) } - - Section(header: Text("CPU Mode").bold()) { + Section(header: Title("CPU Mode")) { HStack { Spacer() Picker("Memory Manager Mode", selection: $config.memoryManagerMode) { @@ -74,11 +64,9 @@ struct SettingsView: View { } } - Section(header: Text("Network").bold()) { - Toggle("Enable Ryujinx LDN", isOn: $config.ryuLDN) - } - Section(header: Text("Additional Settings")) { + + Section(header: Title("Additional Settings")) { //TextField("Game Path", text: $config.gamepath) Text("PageSize \(String(Int(getpagesize())))") @@ -93,8 +81,8 @@ struct SettingsView: View { )) } } + .padding() } - .padding() .onAppear { if let configs = loadSettings() { self.config = configs @@ -176,3 +164,20 @@ extension NumberFormatter { return formatter } } + + +struct Title: View { + let string: String + + init(_ string: String) { + self.string = string + } + + var body: some View { + VStack { + Text(string) + .font(.title2) + Divider() + } + } +} diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index acd1582ec..ab329f835 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Backends.SDL2 // TODO: Add this to SDL2-CS // NOTE: We use a DllImport here because of marshaling issue for spec. #pragma warning disable SYSLIB1054 - [DllImport("SDL2")] + [DllImport("SDL2.framework/SDL2")] private static extern int SDL_GetDefaultAudioInfo(nint name, out SDL_AudioSpec spec, int isCapture); #pragma warning restore SYSLIB1054 diff --git a/src/Ryujinx.Common/Ryujinx.Common.csproj b/src/Ryujinx.Common/Ryujinx.Common.csproj index dee462fdb..5c56cd568 100644 --- a/src/Ryujinx.Common/Ryujinx.Common.csproj +++ b/src/Ryujinx.Common/Ryujinx.Common.csproj @@ -6,6 +6,7 @@ $(DefineConstants);$(ExtraDefineConstants) + diff --git a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs index 7530c012a..b4b6e6beb 100644 --- a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs +++ b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using System; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -133,6 +134,7 @@ namespace Ryujinx.Common private static (Assembly, string) ResolveManifestPath(string filename) { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries); if (segments.Length >= 2) diff --git a/src/Ryujinx.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs index 6664ed134..b71399910 100644 --- a/src/Ryujinx.Cpu/AddressSpace.cs +++ b/src/Ryujinx.Cpu/AddressSpace.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Cpu public AddressSpace(MemoryBlock backingMemory, MemoryBlock baseMemory, MemoryBlock mirrorMemory, ulong addressSpaceSize) { + _backingMemory = backingMemory; Base = baseMemory; diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 1f5523ed6..2d6cfcaca 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -42,6 +42,7 @@ using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.Gamepad using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; using Key = Ryujinx.Common.Configuration.Hid.Key; using System.Linq; +using System.Globalization; public class GamepadInfo { @@ -97,11 +98,11 @@ namespace Ryujinx.Headless.SDL2 static void Main(string[] args) { - Silk.NET.Core.Loader.SearchPathContainer.Platform = Silk.NET.Core.Loader.UnderlyingPlatform.MacOS; Version = "1"; // Make process DPI aware for proper window sizing on high-res screens. ForceDpiAware.Windows(); + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; Silk.NET.Core.Loader.SearchPathContainer.Platform = Silk.NET.Core.Loader.UnderlyingPlatform.MacOS; diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index 2479ec127..c12ee5eb3 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Headless.SDL2 private static readonly ConcurrentQueue _mainThreadActions = new(); - [LibraryImport("SDL2")] + [LibraryImport("SDL2.framework/SDL2")] // TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly private static partial nint SDL_LoadBMP_RW(nint src, int freesrc); diff --git a/src/Ryujinx.Memory/MachJITWorkaround.cs b/src/Ryujinx.Memory/MachJITWorkaround.cs new file mode 100644 index 000000000..cfb1e419c --- /dev/null +++ b/src/Ryujinx.Memory/MachJITWorkaround.cs @@ -0,0 +1,168 @@ +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Memory +{ + [SupportedOSPlatform("ios")] + static unsafe partial class MachJitWorkaround + { + [LibraryImport("libc")] + public static partial int mach_task_self(); + + [LibraryImport("libc")] + public static partial int mach_make_memory_entry_64(IntPtr target_task, IntPtr* size, IntPtr offset, int permission, IntPtr* object_handle, IntPtr parent_entry); + + [LibraryImport("libc")] + public static partial int mach_memory_entry_ownership(IntPtr mem_entry, IntPtr owner, int ledger_tag, int ledger_flags); + + [LibraryImport("libc")] + public static partial int vm_map(IntPtr target_task, IntPtr* address, IntPtr size, IntPtr mask, int flags, IntPtr obj, IntPtr offset, int copy, int cur_protection, int max_protection, int inheritance); + + [LibraryImport("libc")] + public static partial int vm_allocate(IntPtr target_task, IntPtr* address, IntPtr size, int flags); + + [LibraryImport("libc")] + public static partial int vm_deallocate(IntPtr target_task, IntPtr address, IntPtr size); + + [LibraryImport("libc")] + public static partial int vm_remap(IntPtr target_task, IntPtr* target_address, IntPtr size, IntPtr mask, int flags, IntPtr src_task, IntPtr src_address, int copy, int* cur_protection, int* max_protection, int inheritance); + + const int MAP_MEM_LEDGER_TAGGED = 0x002000; + const int MAP_MEM_NAMED_CREATE = 0x020000; + + const int VM_PROT_READ = 0x01; + const int VM_PROT_WRITE = 0x02; + const int VM_PROT_EXECUTE = 0x04; + + const int VM_LEDGER_TAG_DEFAULT = 0x00000001; + const int VM_LEDGER_FLAG_NO_FOOTPRINT = 0x00000001; + + const int VM_INHERIT_COPY = 1; + const int VM_INHERIT_DEFAULT = VM_INHERIT_COPY; + + const int VM_FLAGS_FIXED = 0x0000; + const int VM_FLAGS_ANYWHERE = 0x0001; + const int VM_FLAGS_OVERWRITE = 0x4000; + + const IntPtr TASK_NULL = 0; + + public static void ReallocateBlock(IntPtr address, int size) + { + IntPtr selfTask = mach_task_self(); + IntPtr memorySize = (IntPtr)size; + IntPtr memoryObjectPort = IntPtr.Zero; + + int err = mach_make_memory_entry_64(selfTask, &memorySize, 0, MAP_MEM_NAMED_CREATE | MAP_MEM_LEDGER_TAGGED | VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, &memoryObjectPort, 0); + + if (err != 0) + { + throw new InvalidOperationException($"Make memory entry failed: {err}"); + } + + try + { + if (memorySize != (IntPtr)size) + { + throw new InvalidOperationException($"Created with size {memorySize} instead of {size}."); + } + + err = mach_memory_entry_ownership(memoryObjectPort, TASK_NULL, VM_LEDGER_TAG_DEFAULT, VM_LEDGER_FLAG_NO_FOOTPRINT); + + if (err != 0) + { + throw new InvalidOperationException($"Failed to set ownership: {err}"); + } + + IntPtr mapAddress = address; + + err = vm_map( + selfTask, + &mapAddress, + memorySize, + /*mask=*/ 0, + /*flags=*/ VM_FLAGS_OVERWRITE, + memoryObjectPort, + /*offset=*/ 0, + /*copy=*/ 0, + VM_PROT_READ | VM_PROT_WRITE, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, + VM_INHERIT_COPY); + + if (err != 0) + { + throw new InvalidOperationException($"Failed to map: {err}"); + } + + if (address != mapAddress) + { + throw new InvalidOperationException($"Remap changed address"); + } + } + finally + { + //mach_port_deallocate(selfTask, memoryObjectPort); + } + + Console.WriteLine($"Reallocated an area... {address:x16}"); + } + + public static void ReallocateAreaWithOwnership(IntPtr address, int size) + { + int mapChunkSize = 128 * 1024 * 1024; + IntPtr endAddress = address + size; + IntPtr blockAddress = address; + while (blockAddress < endAddress) + { + int blockSize = Math.Min(mapChunkSize, (int)(endAddress - blockAddress)); + + ReallocateBlock(blockAddress, blockSize); + + blockAddress += blockSize; + } + } + + public static IntPtr AllocateSharedMemory(ulong size, bool reserve) + { + IntPtr address = 0; + + int err = vm_allocate(mach_task_self(), &address, (IntPtr)size, VM_FLAGS_ANYWHERE); + + if (err != 0) + { + throw new InvalidOperationException($"Failed to allocate shared memory: {err}"); + } + + return address; + } + + public static void DestroySharedMemory(IntPtr handle, ulong size) + { + vm_deallocate(mach_task_self(), handle, (IntPtr)size); + } + + public static IntPtr MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, ulong size) + { + IntPtr taskSelf = mach_task_self(); + IntPtr srcAddress = (IntPtr)((ulong)sharedMemory + srcOffset); + IntPtr dstAddress = location; + + int cur_protection = 0; + int max_protection = 0; + + int err = vm_remap(taskSelf, &dstAddress, (IntPtr)size, 0, VM_FLAGS_OVERWRITE, taskSelf, srcAddress, 0, &cur_protection, &max_protection, VM_INHERIT_DEFAULT); + + if (err != 0) + { + throw new InvalidOperationException($"Failed to allocate remap memory: {err}"); + } + + return dstAddress; + } + + public static void UnmapView(IntPtr location, ulong size) + { + vm_deallocate(mach_task_self(), location, (IntPtr)size); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Memory/MemoryBlock.cs b/src/Ryujinx.Memory/MemoryBlock.cs index c2ba51f82..a06b252a7 100644 --- a/src/Ryujinx.Memory/MemoryBlock.cs +++ b/src/Ryujinx.Memory/MemoryBlock.cs @@ -426,7 +426,7 @@ namespace Ryujinx.Memory return OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134); } - return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); + return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS(); } return true; diff --git a/src/Ryujinx.Memory/MemoryManagement.cs b/src/Ryujinx.Memory/MemoryManagement.cs index 276cc2a4c..a23fafb57 100644 --- a/src/Ryujinx.Memory/MemoryManagement.cs +++ b/src/Ryujinx.Memory/MemoryManagement.cs @@ -4,13 +4,13 @@ namespace Ryujinx.Memory { public static class MemoryManagement { - public static nint Allocate(ulong size, bool forJit) + public static IntPtr Allocate(ulong size, bool forJit) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Allocate((nint)size); + return MemoryManagementWindows.Allocate((IntPtr)size); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { return MemoryManagementUnix.Allocate(size, forJit); } @@ -20,13 +20,13 @@ namespace Ryujinx.Memory } } - public static nint Reserve(ulong size, bool forJit, bool viewCompatible) + public static IntPtr Reserve(ulong size, bool forJit, bool viewCompatible) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Reserve((nint)size, viewCompatible); + return MemoryManagementWindows.Reserve((IntPtr)size, viewCompatible); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { return MemoryManagementUnix.Reserve(size, forJit); } @@ -36,13 +36,13 @@ namespace Ryujinx.Memory } } - public static void Commit(nint address, ulong size, bool forJit) + public static void Commit(IntPtr address, ulong size, bool forJit) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.Commit(address, (nint)size); + MemoryManagementWindows.Commit(address, (IntPtr)size); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { MemoryManagementUnix.Commit(address, size, forJit); } @@ -52,13 +52,13 @@ namespace Ryujinx.Memory } } - public static void Decommit(nint address, ulong size) + public static void Decommit(IntPtr address, ulong size) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.Decommit(address, (nint)size); + MemoryManagementWindows.Decommit(address, (IntPtr)size); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { MemoryManagementUnix.Decommit(address, size); } @@ -68,13 +68,13 @@ namespace Ryujinx.Memory } } - public static void MapView(nint sharedMemory, ulong srcOffset, nint address, ulong size, MemoryBlock owner) + public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, MemoryBlock owner) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (nint)size, owner); + MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { MemoryManagementUnix.MapView(sharedMemory, srcOffset, address, size); } @@ -84,13 +84,13 @@ namespace Ryujinx.Memory } } - public static void UnmapView(nint sharedMemory, nint address, ulong size, MemoryBlock owner) + public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, MemoryBlock owner) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.UnmapView(sharedMemory, address, (nint)size, owner); + MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { MemoryManagementUnix.UnmapView(address, size); } @@ -100,15 +100,15 @@ namespace Ryujinx.Memory } } - public static void Reprotect(nint address, ulong size, MemoryPermission permission, bool forView, bool throwOnFail) + public static void Reprotect(IntPtr address, ulong size, MemoryPermission permission, bool forView, bool throwOnFail) { bool result; if (OperatingSystem.IsWindows()) { - result = MemoryManagementWindows.Reprotect(address, (nint)size, permission, forView); + result = MemoryManagementWindows.Reprotect(address, (IntPtr)size, permission, forView); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { result = MemoryManagementUnix.Reprotect(address, size, permission); } @@ -123,13 +123,13 @@ namespace Ryujinx.Memory } } - public static bool Free(nint address, ulong size) + public static bool Free(IntPtr address, ulong size) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Free(address, (nint)size); + return MemoryManagementWindows.Free(address, (IntPtr)size); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { return MemoryManagementUnix.Free(address); } @@ -139,13 +139,13 @@ namespace Ryujinx.Memory } } - public static nint CreateSharedMemory(ulong size, bool reserve) + public static IntPtr CreateSharedMemory(ulong size, bool reserve) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.CreateSharedMemory((nint)size, reserve); + return MemoryManagementWindows.CreateSharedMemory((IntPtr)size, reserve); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { return MemoryManagementUnix.CreateSharedMemory(size, reserve); } @@ -155,13 +155,13 @@ namespace Ryujinx.Memory } } - public static void DestroySharedMemory(nint handle) + public static void DestroySharedMemory(IntPtr handle) { if (OperatingSystem.IsWindows()) { MemoryManagementWindows.DestroySharedMemory(handle); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { MemoryManagementUnix.DestroySharedMemory(handle); } @@ -171,13 +171,13 @@ namespace Ryujinx.Memory } } - public static nint MapSharedMemory(nint handle, ulong size) + public static IntPtr MapSharedMemory(IntPtr handle, ulong size) { if (OperatingSystem.IsWindows()) { return MemoryManagementWindows.MapSharedMemory(handle); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { return MemoryManagementUnix.MapSharedMemory(handle, size); } @@ -187,13 +187,13 @@ namespace Ryujinx.Memory } } - public static void UnmapSharedMemory(nint address, ulong size) + public static void UnmapSharedMemory(IntPtr address, ulong size) { if (OperatingSystem.IsWindows()) { MemoryManagementWindows.UnmapSharedMemory(address); } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { MemoryManagementUnix.UnmapSharedMemory(address, size); } diff --git a/src/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs index 76a63a466..a33ed7967 100644 --- a/src/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/src/Ryujinx.Memory/MemoryManagementUnix.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.Versioning; using static Ryujinx.Memory.MemoryManagerUnixHelper; @@ -8,8 +9,13 @@ namespace Ryujinx.Memory { [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] + [SupportedOSPlatform("ios")] static class MemoryManagementUnix { + + + private static ConcurrentDictionary _sharedMemorySizes = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _allocations = new(); public static nint Allocate(ulong size, bool forJit) @@ -138,6 +144,14 @@ namespace Ryujinx.Memory { int fd; + if (OperatingSystem.IsIOS()) + { + IntPtr baseAddress = MachJitWorkaround.AllocateSharedMemory(size, reserve); + + _sharedMemorySizes.TryAdd(baseAddress, size); + + return baseAddress; + } if (OperatingSystem.IsMacOS()) { byte[] memName = "Ryujinx-XXXXXX"u8.ToArray(); @@ -183,26 +197,59 @@ namespace Ryujinx.Memory return fd; } - public static void DestroySharedMemory(nint handle) + public static void DestroySharedMemory(IntPtr handle) { - close(handle.ToInt32()); + if (OperatingSystem.IsIOS()) + { + if (_sharedMemorySizes.TryGetValue(handle, out ulong size)) + { + _sharedMemorySizes.Remove(handle, out _); + MachJitWorkaround.DestroySharedMemory(handle, size); + } + } + else + { + close(handle.ToInt32()); + } } - public static nint MapSharedMemory(nint handle, ulong size) + public static IntPtr MapSharedMemory(IntPtr handle, ulong size) { - return Mmap(nint.Zero, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_SHARED, handle.ToInt32(), 0); + if (OperatingSystem.IsIOS()) + { + // The base of the shared memory is already mapped - it's the handle. + // Views are remapped from it. + + return handle; + } + else + { + return Mmap(IntPtr.Zero, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_SHARED, handle.ToInt32(), 0); + } } - public static void UnmapSharedMemory(nint address, ulong size) + + public static void UnmapSharedMemory(IntPtr address, ulong size) { - munmap(address, size); + if (!OperatingSystem.IsIOS()) + { + munmap(address, size); + } } - public static void MapView(nint sharedMemory, ulong srcOffset, nint location, ulong size) + public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, ulong size) { - Mmap(location, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_FIXED | MmapFlags.MAP_SHARED, sharedMemory.ToInt32(), (long)srcOffset); + if (OperatingSystem.IsIOS()) + { + MachJitWorkaround.MapView(sharedMemory, srcOffset, location, size); + } + else + { + Mmap(location, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_FIXED | MmapFlags.MAP_SHARED, sharedMemory.ToInt32(), (long)srcOffset); + } } + public static void UnmapView(nint location, ulong size) { Mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0); diff --git a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs index e5a0b7a4d..6ed4d2387 100644 --- a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs +++ b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Memory { [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] + [SupportedOSPlatform("ios")] public static partial class MemoryManagerUnixHelper { [Flags] @@ -44,7 +45,7 @@ namespace Ryujinx.Memory O_SYNC = 256, } - public const nint MAP_FAILED = -1; + public const IntPtr MAP_FAILED = -1; private const int MAP_ANONYMOUS_LINUX_GENERIC = 0x20; private const int MAP_NORESERVE_LINUX_GENERIC = 0x4000; @@ -57,37 +58,37 @@ namespace Ryujinx.Memory public const int MADV_REMOVE = 9; [LibraryImport("libc", EntryPoint = "mmap", SetLastError = true)] - private static partial nint Internal_mmap(nint address, ulong length, MmapProts prot, int flags, int fd, long offset); + private static partial IntPtr Internal_mmap(IntPtr address, ulong length, MmapProts prot, int flags, int fd, long offset); [LibraryImport("libc", SetLastError = true)] - public static partial int mprotect(nint address, ulong length, MmapProts prot); + public static partial int mprotect(IntPtr address, ulong length, MmapProts prot); [LibraryImport("libc", SetLastError = true)] - public static partial int munmap(nint address, ulong length); + public static partial int munmap(IntPtr address, ulong length); [LibraryImport("libc", SetLastError = true)] - public static partial nint mremap(nint old_address, ulong old_size, ulong new_size, int flags, nint new_address); + public static partial IntPtr mremap(IntPtr old_address, ulong old_size, ulong new_size, int flags, IntPtr new_address); [LibraryImport("libc", SetLastError = true)] - public static partial int madvise(nint address, ulong size, int advice); + public static partial int madvise(IntPtr address, ulong size, int advice); [LibraryImport("libc", SetLastError = true)] - public static partial int mkstemp(nint template); + public static partial int mkstemp(IntPtr template); [LibraryImport("libc", SetLastError = true)] - public static partial int unlink(nint pathname); + public static partial int unlink(IntPtr pathname); [LibraryImport("libc", SetLastError = true)] - public static partial int ftruncate(int fildes, nint length); + public static partial int ftruncate(int fildes, IntPtr length); [LibraryImport("libc", SetLastError = true)] public static partial int close(int fd); [LibraryImport("libc", SetLastError = true)] - public static partial int shm_open(nint name, int oflag, uint mode); + public static partial int shm_open(IntPtr name, int oflag, uint mode); [LibraryImport("libc", SetLastError = true)] - public static partial int shm_unlink(nint name); + public static partial int shm_unlink(IntPtr name); private static int MmapFlagsToSystemFlags(MmapFlags flags) { @@ -114,7 +115,7 @@ namespace Ryujinx.Memory { result |= MAP_ANONYMOUS_LINUX_GENERIC; } - else if (OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { result |= MAP_ANONYMOUS_DARWIN; } @@ -130,7 +131,7 @@ namespace Ryujinx.Memory { result |= MAP_NORESERVE_LINUX_GENERIC; } - else if (OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { result |= MAP_NORESERVE_DARWIN; } @@ -146,7 +147,7 @@ namespace Ryujinx.Memory { result |= MAP_UNLOCKED_LINUX_GENERIC; } - else if (OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) { // FIXME: Doesn't exist on Darwin } @@ -156,7 +157,7 @@ namespace Ryujinx.Memory } } - if (flags.HasFlag(MmapFlags.MAP_JIT_DARWIN) && OperatingSystem.IsMacOSVersionAtLeast(10, 14)) + if (flags.HasFlag(MmapFlags.MAP_JIT_DARWIN) && (OperatingSystem.IsIOS() || OperatingSystem.IsMacOSVersionAtLeast(10, 14))) { result |= (int)MmapFlags.MAP_JIT_DARWIN; } @@ -164,7 +165,7 @@ namespace Ryujinx.Memory return result; } - public static nint Mmap(nint address, ulong length, MmapProts prot, MmapFlags flags, int fd, long offset) + public static IntPtr Mmap(IntPtr address, ulong length, MmapProts prot, MmapFlags flags, int fd, long offset) { return Internal_mmap(address, length, prot, MmapFlagsToSystemFlags(flags), fd, offset); } diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 4d8961335..da7c0c817 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -95,12 +95,12 @@ namespace Ryujinx.SDL2.Common SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE); - string gamepadDbPath = Path.Combine(AppDataManager.BaseDirPath, "SDL_GameControllerDB.txt"); + // string gamepadDbPath = Path.Combine(AppDataManager.BaseDirPath, "SDL_GameControllerDB.txt"); - if (File.Exists(gamepadDbPath)) - { - SDL_GameControllerAddMappingsFromFile(gamepadDbPath); - } + // if (File.Exists(gamepadDbPath)) + // { + // SDL_GameControllerAddMappingsFromFile(gamepadDbPath); + // } _registeredWindowHandlers = new ConcurrentDictionary>(); _worker = new Thread(EventWorker); diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index b57caa468..fafc03f69 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -32,6 +32,7 @@ namespace Ryujinx.Ava.Common.Locale private void Load() { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; var localeLanguageCode = !string.IsNullOrEmpty(ConfigurationState.Instance.UI.LanguageCode.Value) ? ConfigurationState.Instance.UI.LanguageCode.Value : CultureInfo.CurrentCulture.Name.Replace('-', '_');