aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/nightly_pr_comment.yml3
-rw-r--r--ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs19
-rw-r--r--ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs2
-rw-r--r--ARMeilleure/CodeGen/X86/X86Optimizer.cs42
-rw-r--r--ARMeilleure/Common/Counter.cs99
-rw-r--r--ARMeilleure/Common/EntryTable.cs196
-rw-r--r--ARMeilleure/Decoders/Block.cs3
-rw-r--r--ARMeilleure/Decoders/Optimizations/TailCallRemover.cs5
-rw-r--r--ARMeilleure/Instructions/InstEmitFlowHelper.cs6
-rw-r--r--ARMeilleure/Instructions/InstEmitMemory.cs19
-rw-r--r--ARMeilleure/Instructions/NativeInterface.cs19
-rw-r--r--ARMeilleure/IntermediateRepresentation/Operand.cs8
-rw-r--r--ARMeilleure/IntermediateRepresentation/OperandHelper.cs6
-rw-r--r--ARMeilleure/Memory/IMemoryManager.cs2
-rw-r--r--ARMeilleure/Translation/ArmEmitterContext.cs19
-rw-r--r--ARMeilleure/Translation/ControlFlowGraph.cs4
-rw-r--r--ARMeilleure/Translation/Delegates.cs2
-rw-r--r--ARMeilleure/Translation/EmitterContext.cs20
-rw-r--r--ARMeilleure/Translation/PTC/Ptc.cs171
-rw-r--r--ARMeilleure/Translation/PTC/PtcFormatter.cs14
-rw-r--r--ARMeilleure/Translation/PTC/PtcJumpTable.cs1
-rw-r--r--ARMeilleure/Translation/PTC/PtcProfiler.cs188
-rw-r--r--ARMeilleure/Translation/SsaConstruction.cs10
-rw-r--r--ARMeilleure/Translation/TranslatedFunction.cs20
-rw-r--r--ARMeilleure/Translation/Translator.cs139
-rw-r--r--Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj13
-rw-r--r--Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs16
-rw-r--r--Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs184
-rw-r--r--Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs223
-rw-r--r--Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs9
-rw-r--r--Ryujinx.Common/Logging/LogClass.cs1
-rw-r--r--Ryujinx.Cpu/MemoryHelper.cs20
-rw-r--r--Ryujinx.Graphics.GAL/Capabilities.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Methods.cs13
-rw-r--r--Ryujinx.Graphics.Gpu/GraphicsConfig.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs9
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs5
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureManager.cs12
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs5
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs28
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs7
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs17
-rw-r--r--Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs64
-rw-r--r--Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj2
-rw-r--r--Ryujinx.Graphics.OpenGL/Framebuffer.cs25
-rw-r--r--Ryujinx.Graphics.OpenGL/HwCapabilities.cs1
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureView.cs38
-rw-r--r--Ryujinx.Graphics.OpenGL/Pipeline.cs6
-rw-r--r--Ryujinx.Graphics.OpenGL/Renderer.cs1
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs4
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs9
-rw-r--r--Ryujinx.Graphics.Shader/IGpuAccessor.cs6
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs2
-rw-r--r--Ryujinx.Graphics.Shader/ShaderProgramInfo.cs5
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs4
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs1
-rw-r--r--Ryujinx.Graphics.Shader/Translation/EmitterContext.cs26
-rw-r--r--Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs6
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs2
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Rewriter.cs4
-rw-r--r--Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs11
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Translator.cs3
-rw-r--r--Ryujinx.HLE/FileSystem/Content/ContentManager.cs62
-rw-r--r--Ryujinx.HLE/FileSystem/Content/LocationEntry.cs4
-rw-r--r--Ryujinx.HLE/HLEConfiguration.cs179
-rw-r--r--Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs1
-rw-r--r--Ryujinx.HLE/HOS/ApplicationLoader.cs73
-rw-r--r--Ryujinx.HLE/HOS/Font/SharedFontManager.cs2
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs28
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs14
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcMessage.cs52
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs24
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs4
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs57
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs172
-rw-r--r--Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs87
-rw-r--r--Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs36
-rw-r--r--Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs73
-rw-r--r--Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs22
-rw-r--r--Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs24
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs70
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs46
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs62
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs30
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs56
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs30
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs42
-rw-r--r--Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs8
-rw-r--r--Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs8
-rw-r--r--Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Hid.cs55
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs27
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs42
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs420
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs51
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs106
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/Boolean32.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs (renamed from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/HidVector.cs)2
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs (renamed from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadJoyHoldType.cs)4
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/CommonEntriesHeader.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPadEntry.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs23
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/KeyboardState.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MousePosition.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MouseState.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/BatterCharge.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/DeviceType.cs26
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs23
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadColorDescription.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadConnectionState.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadState.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadStatesHeader.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSystemProperties.cs22
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisLayoutsIndex.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisState.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs8
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs26
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs149
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs24
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs15
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs29
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs138
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs21
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs15
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs29
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs44
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs15
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs61
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs8
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs18
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs76
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs24
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs66
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs15
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/IpcService.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Mm/IRequest.cs18
-rw-r--r--Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs68
-rw-r--r--Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs56
-rw-r--r--Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs18
-rw-r--r--Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs18
-rw-r--r--Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs30
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs5
-rw-r--r--Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs28
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs38
-rw-r--r--Ryujinx.HLE/HOS/Services/ServiceConfiguration.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs8
-rw-r--r--Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs43
-rw-r--r--Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs26
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs96
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs210
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs50
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs116
-rw-r--r--Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs18
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs40
-rw-r--r--Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs30
-rw-r--r--Ryujinx.HLE/Switch.cs106
-rw-r--r--Ryujinx.HLE/Utilities/StringUtils.cs10
-rw-r--r--Ryujinx.HLE/Utilities/StructReader.cs26
-rw-r--r--Ryujinx.HLE/Utilities/StructWriter.cs8
-rw-r--r--Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj5
-rw-r--r--Ryujinx.Input.SDL2/SDL2GamepadDriver.cs3
-rw-r--r--Ryujinx.Input/HLE/NpadController.cs6
-rw-r--r--Ryujinx.Input/HLE/NpadManager.cs74
-rw-r--r--Ryujinx.Input/Motion/CemuHook/Client.cs14
-rw-r--r--Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj15
-rw-r--r--Ryujinx.SDL2.Common/SDL2Driver.cs (renamed from Ryujinx.Input.SDL2/SDL2Driver.cs)6
-rw-r--r--Ryujinx.sln16
-rw-r--r--Ryujinx/Config.json2
-rw-r--r--Ryujinx/Configuration/AudioBackend.cs (renamed from Ryujinx.Common/Configuration/AudioBackend.cs)3
-rw-r--r--Ryujinx/Configuration/ConfigurationFileFormat.cs (renamed from Ryujinx.Common/Configuration/ConfigurationFileFormat.cs)11
-rw-r--r--Ryujinx/Configuration/ConfigurationState.cs (renamed from Ryujinx.Common/Configuration/ConfigurationState.cs)13
-rw-r--r--Ryujinx/Configuration/LoggerModule.cs (renamed from Ryujinx.Common/Configuration/LoggerModule.cs)1
-rw-r--r--Ryujinx/Configuration/System/Language.cs (renamed from Ryujinx.Common/Configuration/System/Language.cs)0
-rw-r--r--Ryujinx/Configuration/System/Region.cs (renamed from Ryujinx.Common/Configuration/System/Region.cs)0
-rw-r--r--Ryujinx/Configuration/Ui/ColumnSort.cs (renamed from Ryujinx.Common/Configuration/Ui/ColumnSort.cs)0
-rw-r--r--Ryujinx/Configuration/Ui/GuiColumns.cs (renamed from Ryujinx.Common/Configuration/Ui/GuiColumns.cs)0
-rw-r--r--Ryujinx/Input/GTK3/GTK3MappingHelper.cs4
-rw-r--r--Ryujinx/Modules/DiscordIntegrationModule.cs158
-rw-r--r--Ryujinx/Program.cs11
-rw-r--r--Ryujinx/Ryujinx.csproj7
-rw-r--r--Ryujinx/Ui/Applet/GtkHostUiHandler.cs4
-rw-r--r--Ryujinx/Ui/GLRenderer.cs600
-rw-r--r--Ryujinx/Ui/GLWidget.cs118
-rw-r--r--Ryujinx/Ui/MainWindow.cs270
-rw-r--r--Ryujinx/Ui/MainWindow.glade10
-rw-r--r--Ryujinx/Ui/RendererWidgetBase.cs591
-rw-r--r--Ryujinx/Ui/SPBOpenGLContext.cs2
-rw-r--r--Ryujinx/Ui/VKRenderer.cs80
-rw-r--r--Ryujinx/Ui/Widgets/GameTableContextMenu.cs2
-rw-r--r--Ryujinx/Ui/Widgets/GtkDialog.cs29
-rw-r--r--Ryujinx/Ui/Widgets/GtkInputDialog.cs37
-rw-r--r--Ryujinx/Ui/Windows/AvatarWindow.cs289
-rw-r--r--Ryujinx/Ui/Windows/ControllerWindow.cs12
-rw-r--r--Ryujinx/Ui/Windows/SettingsWindow.cs12
-rw-r--r--Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs255
-rw-r--r--Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs327
269 files changed, 6524 insertions, 3010 deletions
diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml
index c6f7528c..7145b7b6 100644
--- a/.github/workflows/nightly_pr_comment.yml
+++ b/.github/workflows/nightly_pr_comment.yml
@@ -14,13 +14,12 @@ jobs:
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
- const pull_user_id = ${{github.event.sender.id}};
const issue_number = await (async () => {
const pulls = await github.pulls.list({owner, repo});
for await (const {data} of github.paginate.iterator(pulls)) {
for (const pull of data) {
- if (pull.head.sha === pull_head_sha && pull.user.id === pull_user_id) {
+ if (pull.head.sha === pull_head_sha) {
return pull.number;
}
}
diff --git a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs
index aa10aea0..2f68c43f 100644
--- a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs
+++ b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs
@@ -83,9 +83,10 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
int intFreeRegisters = regMasks.IntAvailableRegisters;
int vecFreeRegisters = regMasks.VecAvailableRegisters;
- BlockInfo[] blockInfo = new BlockInfo[cfg.Blocks.Count];
+ var blockInfo = new BlockInfo[cfg.Blocks.Count];
- List<LocalInfo> locInfo = new List<LocalInfo>();
+ var locInfo = new List<LocalInfo>();
+ var locVisited = new HashSet<Operand>();
for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--)
{
@@ -109,7 +110,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
if (source.Kind == OperandKind.LocalVariable)
{
- locInfo[source.AsInt32() - 1].SetBlockIndex(block.Index);
+ locInfo[source.GetLocalNumber() - 1].SetBlockIndex(block.Index);
}
else if (source.Kind == OperandKind.Memory)
{
@@ -117,12 +118,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
if (memOp.BaseAddress != null)
{
- locInfo[memOp.BaseAddress.AsInt32() - 1].SetBlockIndex(block.Index);
+ locInfo[memOp.BaseAddress.GetLocalNumber() - 1].SetBlockIndex(block.Index);
}
if (memOp.Index != null)
{
- locInfo[memOp.Index.AsInt32() - 1].SetBlockIndex(block.Index);
+ locInfo[memOp.Index.GetLocalNumber() - 1].SetBlockIndex(block.Index);
}
}
}
@@ -135,9 +136,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
LocalInfo info;
- if (dest.Value != 0)
+ if (!locVisited.Add(dest))
{
- info = locInfo[dest.AsInt32() - 1];
+ info = locInfo[dest.GetLocalNumber() - 1];
}
else
{
@@ -198,7 +199,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
void AllocateRegister(Operand source, MemoryOperand memOp, int srcIndex)
{
- LocalInfo info = locInfo[source.AsInt32() - 1];
+ LocalInfo info = locInfo[source.GetLocalNumber() - 1];
info.UseCount++;
@@ -317,7 +318,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
continue;
}
- LocalInfo info = locInfo[dest.AsInt32() - 1];
+ LocalInfo info = locInfo[dest.GetLocalNumber() - 1];
if (info.UseCount == 0 && !info.PreAllocated)
{
diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs
index cd36bdc0..88adeeb0 100644
--- a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs
+++ b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs
@@ -976,7 +976,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
if (operand.Kind == OperandKind.LocalVariable)
{
- return operand.AsInt32();
+ return operand.GetLocalNumber();
}
else if (operand.Kind == OperandKind.Register)
{
diff --git a/ARMeilleure/CodeGen/X86/X86Optimizer.cs b/ARMeilleure/CodeGen/X86/X86Optimizer.cs
index 643b515f..fa8b54e8 100644
--- a/ARMeilleure/CodeGen/X86/X86Optimizer.cs
+++ b/ARMeilleure/CodeGen/X86/X86Optimizer.cs
@@ -1,6 +1,7 @@
using ARMeilleure.CodeGen.Optimizations;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
+using System.Collections.Generic;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
using static ARMeilleure.IntermediateRepresentation.OperationHelper;
@@ -11,8 +12,28 @@ namespace ARMeilleure.CodeGen.X86
{
public static void RunPass(ControlFlowGraph cfg)
{
+ var constants = new Dictionary<ulong, Operand>();
+
+ Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
+ {
+ if (!constants.TryGetValue(source.Value, out var constant))
+ {
+ constant = Local(source.Type);
+
+ Operation copyOp = Operation(Instruction.Copy, constant, source);
+
+ block.Operations.AddBefore(operation, copyOp);
+
+ constants.Add(source.Value, constant);
+ }
+
+ return constant;
+ }
+
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{
+ constants.Clear();
+
Node nextNode;
for (Node node = block.Operations.First; node != null; node = nextNode)
@@ -33,24 +54,12 @@ namespace ARMeilleure.CodeGen.X86
if (src1.Kind == OperandKind.Constant && (src1.Relocatable || CodeGenCommon.IsLongConst(src1)))
{
- Operand temp = Local(src1.Type);
-
- Operation copyOp = Operation(Instruction.Copy, temp, src1);
-
- block.Operations.AddBefore(operation, copyOp);
-
- operation.SetSource(0, temp);
+ operation.SetSource(0, GetConstantCopy(block, operation, src1));
}
if (src2.Kind == OperandKind.Constant && (src2.Relocatable || CodeGenCommon.IsLongConst(src2)))
{
- Operand temp = Local(src2.Type);
-
- Operation copyOp = Operation(Instruction.Copy, temp, src2);
-
- block.Operations.AddBefore(operation, copyOp);
-
- operation.SetSource(1, temp);
+ operation.SetSource(1, GetConstantCopy(block, operation, src2));
}
}
@@ -111,6 +120,11 @@ namespace ARMeilleure.CodeGen.X86
return null;
}
+ if (imm == 0 && scale == Multiplier.x1 && indexOp != null)
+ {
+ imm = GetConstOp(ref indexOp);
+ }
+
return MemoryOp(type, baseOp, indexOp, scale, imm);
}
diff --git a/ARMeilleure/Common/Counter.cs b/ARMeilleure/Common/Counter.cs
new file mode 100644
index 00000000..defb5aba
--- /dev/null
+++ b/ARMeilleure/Common/Counter.cs
@@ -0,0 +1,99 @@
+using System;
+
+namespace ARMeilleure.Common
+{
+ /// <summary>
+ /// Represents a numeric counter which can be used for instrumentation of compiled code.
+ /// </summary>
+ /// <typeparam name="T">Type of the counter</typeparam>
+ class Counter<T> : IDisposable where T : unmanaged
+ {
+ private bool _disposed;
+ private readonly int _index;
+ private readonly EntryTable<T> _countTable;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Counter{T}"/> class from the specified
+ /// <see cref="EntryTable{T}"/> instance and index.
+ /// </summary>
+ /// <param name="countTable"><see cref="EntryTable{T}"/> instance</param>
+ /// <param name="index">Index in the <see cref="EntryTable{T}"/></param>
+ /// <exception cref="ArgumentNullException"><paramref name="countTable"/> is <see langword="null"/></exception>
+ /// <exception cref="ArgumentException"><typeparamref name="T"/> is unsupported</exception>
+ public Counter(EntryTable<T> countTable)
+ {
+ if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) &&
+ typeof(T) != typeof(short) && typeof(T) != typeof(ushort) &&
+ typeof(T) != typeof(int) && typeof(T) != typeof(uint) &&
+ typeof(T) != typeof(long) && typeof(T) != typeof(ulong) &&
+ typeof(T) != typeof(nint) && typeof(T) != typeof(nuint) &&
+ typeof(T) != typeof(float) && typeof(T) != typeof(double))
+ {
+ throw new ArgumentException("Counter does not support the specified type.");
+ }
+
+ _countTable = countTable ?? throw new ArgumentNullException(nameof(countTable));
+ _index = countTable.Allocate();
+ }
+
+ /// <summary>
+ /// Gets a reference to the value of the counter.
+ /// </summary>
+ /// <exception cref="ObjectDisposedException"><see cref="Counter{T}"/> instance was disposed</exception>
+ /// <remarks>
+ /// This can refer to freed memory if the owning <see cref="EntryTable{TEntry}"/> is disposed.
+ /// </remarks>
+ public ref T Value
+ {
+ get
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+
+ return ref _countTable.GetValue(_index);
+ }
+ }
+
+ /// <summary>
+ /// Releases all resources used by the <see cref="Counter{T}"/> instance.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases all unmanaged and optionally managed resources used by the <see cref="Counter{T}"/> instance.
+ /// </summary>
+ /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ try
+ {
+ // The index into the EntryTable is essentially an unmanaged resource since we allocate and free the
+ // resource ourselves.
+ _countTable.Free(_index);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Can happen because _countTable may be disposed before the Counter instance.
+ }
+
+ _disposed = true;
+ }
+ }
+
+ /// <summary>
+ /// Frees resources used by the <see cref="Counter{T}"/> instance.
+ /// </summary>
+ ~Counter()
+ {
+ Dispose(false);
+ }
+ }
+}
diff --git a/ARMeilleure/Common/EntryTable.cs b/ARMeilleure/Common/EntryTable.cs
new file mode 100644
index 00000000..a0ed7c8e
--- /dev/null
+++ b/ARMeilleure/Common/EntryTable.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.InteropServices;
+
+namespace ARMeilleure.Common
+{
+ /// <summary>
+ /// Represents an expandable table of the type <typeparamref name="TEntry"/>, whose entries will remain at the same
+ /// address through out the table's lifetime.
+ /// </summary>
+ /// <typeparam name="TEntry">Type of the entry in the table</typeparam>
+ class EntryTable<TEntry> : IDisposable where TEntry : unmanaged
+ {
+ private bool _disposed;
+ private int _freeHint;
+ private readonly int _pageCapacity; // Number of entries per page.
+ private readonly int _pageLogCapacity;
+ private readonly Dictionary<int, IntPtr> _pages;
+ private readonly BitMap _allocated;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EntryTable{TEntry}"/> class with the desired page size in
+ /// bytes.
+ /// </summary>
+ /// <param name="pageSize">Desired page size in bytes</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="pageSize"/> is less than 0</exception>
+ /// <exception cref="ArgumentException"><typeparamref name="TEntry"/>'s size is zero</exception>
+ /// <remarks>
+ /// The actual page size may be smaller or larger depending on the size of <typeparamref name="TEntry"/>.
+ /// </remarks>
+ public unsafe EntryTable(int pageSize = 4096)
+ {
+ if (pageSize < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(pageSize), "Page size cannot be negative.");
+ }
+
+ if (sizeof(TEntry) == 0)
+ {
+ throw new ArgumentException("Size of TEntry cannot be zero.");
+ }
+
+ _allocated = new BitMap();
+ _pages = new Dictionary<int, IntPtr>();
+ _pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry)));
+ _pageCapacity = 1 << _pageLogCapacity;
+ }
+
+ /// <summary>
+ /// Allocates an entry in the <see cref="EntryTable{TEntry}"/>.
+ /// </summary>
+ /// <returns>Index of entry allocated in the table</returns>
+ /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
+ public int Allocate()
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+
+ lock (_allocated)
+ {
+ if (_allocated.IsSet(_freeHint))
+ {
+ _freeHint = _allocated.FindFirstUnset();
+ }
+
+ int index = _freeHint++;
+ var page = GetPage(index);
+
+ _allocated.Set(index);
+
+ GetValue(page, index) = default;
+
+ return index;
+ }
+ }
+
+ /// <summary>
+ /// Frees the entry at the specified <paramref name="index"/>.
+ /// </summary>
+ /// <param name="index">Index of entry to free</param>
+ /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
+ public void Free(int index)
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+
+ lock (_allocated)
+ {
+ if (_allocated.IsSet(index))
+ {
+ _allocated.Clear(index);
+
+ _freeHint = index;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a reference to the entry at the specified allocated <paramref name="index"/>.
+ /// </summary>
+ /// <param name="index">Index of the entry</param>
+ /// <returns>Reference to the entry at the specified <paramref name="index"/></returns>
+ /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
+ /// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception>
+ public ref TEntry GetValue(int index)
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+
+ lock (_allocated)
+ {
+ if (!_allocated.IsSet(index))
+ {
+ throw new ArgumentException("Entry at the specified index was not allocated", nameof(index));
+ }
+
+ var page = GetPage(index);
+
+ return ref GetValue(page, index);
+ }
+ }
+
+ /// <summary>
+ /// Gets a reference to the entry at using the specified <paramref name="index"/> from the specified
+ /// <paramref name="page"/>.
+ /// </summary>
+ /// <param name="page">Page to use</param>
+ /// <param name="index">Index to use</param>
+ /// <returns>Reference to the entry</returns>
+ private ref TEntry GetValue(Span<TEntry> page, int index)
+ {
+ return ref page[index & (_pageCapacity - 1)];
+ }
+
+ /// <summary>
+ /// Gets the page for the specified <see cref="index"/>.
+ /// </summary>
+ /// <param name="index">Index to use</param>
+ /// <returns>Page for the specified <see cref="index"/></returns>
+ private unsafe Span<TEntry> GetPage(int index)
+ {
+ var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity);
+
+ if (!_pages.TryGetValue(pageIndex, out IntPtr page))
+ {
+ page = Marshal.AllocHGlobal(sizeof(TEntry) * _pageCapacity);
+
+ _pages.Add(pageIndex, page);
+ }
+
+ return new Span<TEntry>((void*)page, _pageCapacity);
+ }
+
+ /// <summary>
+ /// Releases all resources used by the <see cref="EntryTable{TEntry}"/> instance.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases all unmanaged and optionally managed resources used by the <see cref="EntryTable{TEntry}{T}"/>
+ /// instance.
+ /// </summary>
+ /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ foreach (var page in _pages.Values)
+ {
+ Marshal.FreeHGlobal(page);
+ }
+
+ _disposed = true;
+ }
+ }
+
+ /// <summary>
+ /// Frees resources used by the <see cref="EntryTable{TEntry}"/> instance.
+ /// </summary>
+ ~EntryTable()
+ {
+ Dispose(false);
+ }
+ }
+}
diff --git a/ARMeilleure/Decoders/Block.cs b/ARMeilleure/Decoders/Block.cs
index 9d380bca..f296d299 100644
--- a/ARMeilleure/Decoders/Block.cs
+++ b/ARMeilleure/Decoders/Block.cs
@@ -11,8 +11,7 @@ namespace ARMeilleure.Decoders
public Block Next { get; set; }
public Block Branch { get; set; }
- public bool TailCall { get; set; }
- public bool Exit { get; set; }
+ public bool Exit { get; set; }
public List<OpCode> OpCodes { get; }
diff --git a/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs b/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs
index e64f9a54..17c17812 100644
--- a/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs
+++ b/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs
@@ -58,15 +58,14 @@ namespace ARMeilleure.Decoders.Optimizations
return blocks.ToArray(); // Nothing to do here.
}
- // Mark branches outside of contiguous region as exit blocks.
+ // Mark branches whose target is outside of the contiguous region as an exit block.
for (int i = startBlockIndex; i <= endBlockIndex; i++)
{
Block block = blocks[i];
if (block.Branch != null && (block.Branch.Address > endBlock.EndAddress || block.Branch.EndAddress < startBlock.Address))
{
- block.Branch.Exit = true;
- block.Branch.TailCall = true;
+ block.Branch.Exit = true;
}
}
diff --git a/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/ARMeilleure/Instructions/InstEmitFlowHelper.cs
index 296e20a5..f995ffa1 100644
--- a/ARMeilleure/Instructions/InstEmitFlowHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitFlowHelper.cs
@@ -200,7 +200,7 @@ namespace ARMeilleure.Instructions
}
}
- public static void EmitTailContinue(ArmEmitterContext context, Operand address, bool allowRejit)
+ public static void EmitTailContinue(ArmEmitterContext context, Operand address)
{
// Left option here as it may be useful if we need to return to managed rather than tail call in future.
// (eg. for debug)
@@ -218,9 +218,7 @@ namespace ARMeilleure.Instructions
{
context.StoreToContext();
- Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(allowRejit
- ? nameof(NativeInterface.GetFunctionAddress)
- : nameof(NativeInterface.GetFunctionAddressWithoutRejit)), address);
+ Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address);
EmitNativeCall(context, fallbackAddr, isJump: true);
}
diff --git a/ARMeilleure/Instructions/InstEmitMemory.cs b/ARMeilleure/Instructions/InstEmitMemory.cs
index 1d5953fb..87564fdc 100644
--- a/ARMeilleure/Instructions/InstEmitMemory.cs
+++ b/ARMeilleure/Instructions/InstEmitMemory.cs
@@ -87,8 +87,7 @@ namespace ARMeilleure.Instructions
}
Operand address = GetAddress(context);
-
- Operand address2 = context.Add(address, Const(1L << op.Size));
+ Operand address2 = GetAddress(context, 1L << op.Size);
EmitLoad(op.Rt, address);
EmitLoad(op.Rt2, address2);
@@ -112,8 +111,7 @@ namespace ARMeilleure.Instructions
OpCodeMemPair op = (OpCodeMemPair)context.CurrOp;
Operand address = GetAddress(context);
-
- Operand address2 = context.Add(address, Const(1L << op.Size));
+ Operand address2 = GetAddress(context, 1L << op.Size);
InstEmitMemoryHelper.EmitStore(context, address, op.Rt, op.Size);
InstEmitMemoryHelper.EmitStore(context, address2, op.Rt2, op.Size);
@@ -121,7 +119,7 @@ namespace ARMeilleure.Instructions
EmitWBackIfNeeded(context, address);
}
- private static Operand GetAddress(ArmEmitterContext context)
+ private static Operand GetAddress(ArmEmitterContext context, long addend = 0)
{
Operand address = null;
@@ -134,7 +132,11 @@ namespace ARMeilleure.Instructions
// Pre-indexing.
if (!op.PostIdx)
{
- address = context.Add(address, Const(op.Immediate));
+ address = context.Add(address, Const(op.Immediate + addend));
+ }
+ else if (addend != 0)
+ {
+ address = context.Add(address, Const(addend));
}
break;
@@ -153,6 +155,11 @@ namespace ARMeilleure.Instructions
address = context.Add(n, m);
+ if (addend != 0)
+ {
+ address = context.Add(address, Const(addend));
+ }
+
break;
}
}
diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs
index b8b7ff0e..fa17d334 100644
--- a/ARMeilleure/Instructions/NativeInterface.cs
+++ b/ARMeilleure/Instructions/NativeInterface.cs
@@ -220,6 +220,11 @@ namespace ARMeilleure.Instructions
}
#endregion
+ public static void EnqueueForRejit(ulong address)
+ {
+ Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
+ }
+
public static void SignalMemoryTracking(ulong address, ulong size, bool write)
{
GetMemoryManager().SignalMemoryTracking(address, size, write);
@@ -232,24 +237,14 @@ namespace ARMeilleure.Instructions
public static ulong GetFunctionAddress(ulong address)
{
- return GetFunctionAddressWithHint(address, true);
- }
-
- public static ulong GetFunctionAddressWithoutRejit(ulong address)
- {
- return GetFunctionAddressWithHint(address, false);
- }
-
- private static ulong GetFunctionAddressWithHint(ulong address, bool hintRejit)
- {
- TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode, hintRejit);
+ TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
return (ulong)function.FuncPtr.ToInt64();
}
public static ulong GetIndirectFunctionAddress(ulong address, ulong entryAddress)
{
- TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode, hintRejit: true);
+ TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
ulong ptr = (ulong)function.FuncPtr.ToInt64();
diff --git a/ARMeilleure/IntermediateRepresentation/Operand.cs b/ARMeilleure/IntermediateRepresentation/Operand.cs
index 7b486c55..ec023939 100644
--- a/ARMeilleure/IntermediateRepresentation/Operand.cs
+++ b/ARMeilleure/IntermediateRepresentation/Operand.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
@@ -91,6 +92,13 @@ namespace ARMeilleure.IntermediateRepresentation
return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
}
+ public int GetLocalNumber()
+ {
+ Debug.Assert(Kind == OperandKind.LocalVariable);
+
+ return (int)Value;
+ }
+
public byte AsByte()
{
return (byte)Value;
diff --git a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs b/ARMeilleure/IntermediateRepresentation/OperandHelper.cs
index 26d66478..1b748f6a 100644
--- a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs
+++ b/ARMeilleure/IntermediateRepresentation/OperandHelper.cs
@@ -1,4 +1,5 @@
using ARMeilleure.Common;
+using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
{
@@ -34,6 +35,11 @@ namespace ARMeilleure.IntermediateRepresentation
return Operand().With(value);
}
+ public static unsafe Operand Const<T>(ref T reference, int? index = null)
+ {
+ return Operand().With((long)Unsafe.AsPointer(ref reference), index != null, index);
+ }
+
public static Operand ConstF(float value)
{
return Operand().With(value);
diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/ARMeilleure/Memory/IMemoryManager.cs
index 33153903..cacfc4ac 100644
--- a/ARMeilleure/Memory/IMemoryManager.cs
+++ b/ARMeilleure/Memory/IMemoryManager.cs
@@ -12,6 +12,8 @@ namespace ARMeilleure.Memory
T ReadTracked<T>(ulong va) where T : unmanaged;
void Write<T>(ulong va, T value) where T : unmanaged;
+ ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false);
+
ref T GetRef<T>(ulong va) where T : unmanaged;
bool IsMapped(ulong va);
diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/ARMeilleure/Translation/ArmEmitterContext.cs
index 8f153192..ad44b0cf 100644
--- a/ARMeilleure/Translation/ArmEmitterContext.cs
+++ b/ARMeilleure/Translation/ArmEmitterContext.cs
@@ -1,3 +1,4 @@
+using ARMeilleure.Common;
using ARMeilleure.Decoders;
using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
@@ -41,18 +42,26 @@ namespace ARMeilleure.Translation
public IMemoryManager Memory { get; }
public JumpTable JumpTable { get; }
+ public EntryTable<uint> CountTable { get; }
public ulong EntryAddress { get; }
public bool HighCq { get; }
public Aarch32Mode Mode { get; }
- public ArmEmitterContext(IMemoryManager memory, JumpTable jumpTable, ulong entryAddress, bool highCq, Aarch32Mode mode)
+ public ArmEmitterContext(
+ IMemoryManager memory,
+ JumpTable jumpTable,
+ EntryTable<uint> countTable,
+ ulong entryAddress,
+ bool highCq,
+ Aarch32Mode mode)
{
- Memory = memory;
- JumpTable = jumpTable;
+ Memory = memory;
+ JumpTable = jumpTable;
+ CountTable = countTable;
EntryAddress = entryAddress;
- HighCq = highCq;
- Mode = mode;
+ HighCq = highCq;
+ Mode = mode;
_labels = new Dictionary<ulong, Operand>();
}
diff --git a/ARMeilleure/Translation/ControlFlowGraph.cs b/ARMeilleure/Translation/ControlFlowGraph.cs
index ee1a245e..4c76d5dd 100644
--- a/ARMeilleure/Translation/ControlFlowGraph.cs
+++ b/ARMeilleure/Translation/ControlFlowGraph.cs
@@ -10,15 +10,17 @@ namespace ARMeilleure.Translation
private BasicBlock[] _postOrderBlocks;
private int[] _postOrderMap;
+ public int LocalsCount { get; }
public BasicBlock Entry { get; }
public IntrusiveList<BasicBlock> Blocks { get; }
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
public int[] PostOrderMap => _postOrderMap;
- public ControlFlowGraph(BasicBlock entry, IntrusiveList<BasicBlock> blocks)
+ public ControlFlowGraph(BasicBlock entry, IntrusiveList<BasicBlock> blocks, int localsCount)
{
Entry = entry;
Blocks = blocks;
+ LocalsCount = localsCount;
Update(removeUnreachableBlocks: true);
}
diff --git a/ARMeilleure/Translation/Delegates.cs b/ARMeilleure/Translation/Delegates.cs
index 5ad71872..a561d265 100644
--- a/ARMeilleure/Translation/Delegates.cs
+++ b/ARMeilleure/Translation/Delegates.cs
@@ -103,6 +103,7 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
+ SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
@@ -113,7 +114,6 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
- SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddressWithoutRejit)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetIndirectFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs
index 5c608b3d..cc2205ce 100644
--- a/ARMeilleure/Translation/EmitterContext.cs
+++ b/ARMeilleure/Translation/EmitterContext.cs
@@ -12,6 +12,8 @@ namespace ARMeilleure.Translation
{
class EmitterContext
{
+ private int _localsCount;
+
private readonly Dictionary<Operand, BasicBlock> _irLabels;
private readonly IntrusiveList<BasicBlock> _irBlocks;
@@ -23,6 +25,8 @@ namespace ARMeilleure.Translation
public EmitterContext()
{
+ _localsCount = 0;
+
_irLabels = new Dictionary<Operand, BasicBlock>();
_irBlocks = new IntrusiveList<BasicBlock>();
@@ -30,6 +34,15 @@ namespace ARMeilleure.Translation
_nextBlockFreq = BasicBlockFrequency.Default;
}
+ public Operand AllocateLocal(OperandType type)
+ {
+ Operand local = Local(type);
+
+ local.NumberLocal(++_localsCount);
+
+ return local;
+ }
+
public Operand Add(Operand op1, Operand op2)
{
return Add(Instruction.Add, Local(op1.Type), op1, op2);
@@ -223,9 +236,10 @@ namespace ARMeilleure.Translation
public Operand Copy(Operand dest, Operand op1)
{
- if (dest.Kind != OperandKind.Register)
+ if (dest.Kind != OperandKind.Register &&
+ (dest.Kind != OperandKind.LocalVariable || dest.GetLocalNumber() == 0))
{
- throw new ArgumentException($"Invalid dest operand kind \"{dest.Kind}\".");
+ throw new ArgumentException($"Destination operand must be a Register or a numbered LocalVariable.");
}
return Add(Instruction.Copy, dest, op1);
@@ -670,7 +684,7 @@ namespace ARMeilleure.Translation
public ControlFlowGraph GetControlFlowGraph()
{
- return new ControlFlowGraph(_irBlocks.First, _irBlocks);
+ return new ControlFlowGraph(_irBlocks.First, _irBlocks, _localsCount);
}
}
}
diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs
index 32e0e7e8..1bb8659c 100644
--- a/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/ARMeilleure/Translation/PTC/Ptc.cs
@@ -1,6 +1,7 @@
using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.CodeGen.X86;
+using ARMeilleure.Common;
using ARMeilleure.Memory;
using ARMeilleure.Translation.Cache;
using Ryujinx.Common;
@@ -27,7 +28,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
- private const uint InternalVersion = 2169; //! To be incremented manually for each change to the ARMeilleure project.
+ private const uint InternalVersion = 2285; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
@@ -38,6 +39,7 @@ namespace ARMeilleure.Translation.PTC
internal const int PageTablePointerIndex = -1; // Must be a negative value.
internal const int JumpPointerIndex = -2; // Must be a negative value.
internal const int DynamicPointerIndex = -3; // Must be a negative value.
+ internal const int CountTableIndex = -4; // Must be a negative value.
private const byte FillingByte = 0x00;
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
@@ -48,8 +50,6 @@ namespace ARMeilleure.Translation.PTC
private static MemoryStream _relocsStream;
private static MemoryStream _unwindInfosStream;
- private static BinaryWriter _infosWriter;
-
private static readonly ulong _outerHeaderMagic;
private static readonly ulong _innerHeaderMagic;
@@ -151,14 +151,10 @@ namespace ARMeilleure.Translation.PTC
_codesList = new List<byte[]>();
_relocsStream = new MemoryStream();
_unwindInfosStream = new MemoryStream();
-
- _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
}
private static void DisposeCarriers()
{
- _infosWriter.Dispose();
-
_infosStream.Dispose();
_codesList.Clear();
_relocsStream.Dispose();
@@ -538,68 +534,89 @@ namespace ARMeilleure.Translation.PTC
}
}
- internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
+ internal static void LoadTranslations(
+ ConcurrentDictionary<ulong, TranslatedFunction> funcs,
+ IMemoryManager memory,
+ JumpTable jumpTable,
+ EntryTable<uint> countTable)
{
if (AreCarriersEmpty())
{
return;
}
+ long infosStreamLength = _infosStream.Length;
+ long relocsStreamLength = _relocsStream.Length;
+ long unwindInfosStreamLength = _unwindInfosStream.Length;
+
_infosStream.Seek(0L, SeekOrigin.Begin);
_relocsStream.Seek(0L, SeekOrigin.Begin);
_unwindInfosStream.Seek(0L, SeekOrigin.Begin);
- using (BinaryReader infosReader = new(_infosStream, EncodingCache.UTF8NoBOM, true))
using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true))
using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
{
for (int index = 0; index < GetEntriesCount(); index++)
{
- InfoEntry infoEntry = ReadInfo(infosReader);
+ InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
if (infoEntry.Stubbed)
{
SkipCode(index, infoEntry.CodeLength);
SkipReloc(infoEntry.RelocEntriesCount);
SkipUnwindInfo(unwindInfosReader);
+
+ continue;
}
- else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.HighCq)
+
+ bool isEntryChanged = infoEntry.Hash != ComputeHash(memory, infoEntry.Address, infoEntry.GuestSize);
+
+ if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
{
- byte[] code = ReadCode(index, infoEntry.CodeLength);
+ infoEntry.Stubbed = true;
+ infoEntry.CodeLength = 0;
+ UpdateInfo(infoEntry);
- if (infoEntry.RelocEntriesCount != 0)
+ StubCode(index);
+ StubReloc(infoEntry.RelocEntriesCount);
+ StubUnwindInfo(unwindInfosReader);
+
+ if (isEntryChanged)
{
- RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
+ PtcJumpTable.Clean(infoEntry.Address);
- PatchCode(code.AsSpan(), relocEntries, memory.PageTablePointer, jumpTable);
+ Logger.Info?.Print(LogClass.Ptc, $"Invalidated translated function (address: 0x{infoEntry.Address:X16})");
}
- UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
+ continue;
+ }
- TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
+ byte[] code = ReadCode(index, infoEntry.CodeLength);
- bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func);
+ Counter<uint> callCounter = null;
- Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
- }
- else
+ if (infoEntry.RelocEntriesCount != 0)
{
- infoEntry.Stubbed = true;
- infoEntry.CodeLength = 0;
- UpdateInfo(infoEntry);
+ RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
- StubCode(index);
- StubReloc(infoEntry.RelocEntriesCount);
- StubUnwindInfo(unwindInfosReader);
+ PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter);
}
+
+ UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
+
+ TranslatedFunction func = FastTranslate(code, callCounter, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
+
+ bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func);
+
+ Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
}
}
- if (_infosStream.Position < _infosStream.Length ||
- _relocsStream.Position < _relocsStream.Length ||
- _unwindInfosStream.Position < _unwindInfosStream.Length)
+ if (_infosStream.Length != infosStreamLength || _infosStream.Position != infosStreamLength ||
+ _relocsStream.Length != relocsStreamLength || _relocsStream.Position != relocsStreamLength ||
+ _unwindInfosStream.Length != unwindInfosStreamLength || _unwindInfosStream.Position != unwindInfosStreamLength)
{
- throw new Exception("Could not reach the end of one or more memory streams.");
+ throw new Exception("The length of a memory stream has changed, or its position has not reached or has exceeded its end.");
}
jumpTable.Initialize(PtcJumpTable, funcs);
@@ -615,20 +632,6 @@ namespace ARMeilleure.Translation.PTC
return _codesList.Count;
}
- private static InfoEntry ReadInfo(BinaryReader infosReader)
- {
- InfoEntry infoEntry = new InfoEntry();
-
- infoEntry.Address = infosReader.ReadUInt64();
- infoEntry.GuestSize = infosReader.ReadUInt64();
- infoEntry.HighCq = infosReader.ReadBoolean();
- infoEntry.Stubbed = infosReader.ReadBoolean();
- infoEntry.CodeLength = infosReader.ReadInt32();
- infoEntry.RelocEntriesCount = infosReader.ReadInt32();
-
- return infoEntry;
- }
-
[Conditional("DEBUG")]
private static void SkipCode(int index, int codeLength)
{
@@ -670,8 +673,16 @@ namespace ARMeilleure.Translation.PTC
return relocEntries;
}
- private static void PatchCode(Span<byte> code, RelocEntry[] relocEntries, IntPtr pageTablePointer, JumpTable jumpTable)
+ private static void PatchCode(
+ Span<byte> code,
+ RelocEntry[] relocEntries,
+ IntPtr pageTablePointer,
+ JumpTable jumpTable,
+ EntryTable<uint> countTable,
+ out Counter<uint> callCounter)
{
+ callCounter = null;
+
foreach (RelocEntry relocEntry in relocEntries)
{
ulong imm;
@@ -688,6 +699,12 @@ namespace ARMeilleure.Translation.PTC
{
imm = (ulong)jumpTable.DynamicPointer.ToInt64();
}
+ else if (relocEntry.Index == CountTableIndex)
+ {
+ callCounter = new Counter<uint>(countTable);
+
+ unsafe { imm = (ulong)Unsafe.AsPointer(ref callCounter.Value); }
+ }
else if (Delegates.TryGetDelegateFuncPtrByIndex(relocEntry.Index, out IntPtr funcPtr))
{
imm = (ulong)funcPtr.ToInt64();
@@ -722,7 +739,12 @@ namespace ARMeilleure.Translation.PTC
return new UnwindInfo(pushEntries, prologueSize);
}
- private static TranslatedFunction FastTranslate(byte[] code, ulong guestSize, UnwindInfo unwindInfo, bool highCq)
+ private static TranslatedFunction FastTranslate(
+ byte[] code,
+ Counter<uint> callCounter,
+ ulong guestSize,
+ UnwindInfo unwindInfo,
+ bool highCq)
{
CompiledFunction cFunc = new CompiledFunction(code, unwindInfo);
@@ -730,22 +752,16 @@ namespace ARMeilleure.Translation.PTC
GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer<GuestFunction>(codePtr);
- TranslatedFunction tFunc = new TranslatedFunction(gFunc, guestSize, highCq);
+ TranslatedFunction tFunc = new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
return tFunc;
}
private static void UpdateInfo(InfoEntry infoEntry)
{
- _infosStream.Seek(-InfoEntry.Stride, SeekOrigin.Current);
-
- // WriteInfo.
- _infosWriter.Write((ulong)infoEntry.Address);
- _infosWriter.Write((ulong)infoEntry.GuestSize);
- _infosWriter.Write((bool)infoEntry.HighCq);
- _infosWriter.Write((bool)infoEntry.Stubbed);
- _infosWriter.Write((int)infoEntry.CodeLength);
- _infosWriter.Write((int)infoEntry.RelocEntriesCount);
+ _infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
+
+ SerializeStructure(_infosStream, infoEntry);
}
private static void StubCode(int index)
@@ -771,7 +787,11 @@ namespace ARMeilleure.Translation.PTC
}
}
- internal static void MakeAndSaveTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
+ internal static void MakeAndSaveTranslations(
+ ConcurrentDictionary<ulong, TranslatedFunction> funcs,
+ IMemoryManager memory,
+ JumpTable jumpTable,
+ EntryTable<uint> countTable)
{
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs);
@@ -813,7 +833,7 @@ namespace ARMeilleure.Translation.PTC
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
- TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.mode, item.highCq);
+ TranslatedFunction func = Translator.Translate(memory, jumpTable, countTable, address, item.funcProfile.Mode, item.funcProfile.HighCq);
bool isAddressUnique = funcs.TryAdd(address, func);
@@ -888,17 +908,26 @@ namespace ARMeilleure.Translation.PTC
while (!endEvent.WaitOne(refreshRate));
}
- internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo)
+ internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
+ {
+ return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
+ }
+
+ internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, Hash128 hash, bool highCq, PtcInfo ptcInfo)
{
lock (_lock)
{
- // WriteInfo.
- _infosWriter.Write((ulong)address); // InfoEntry.Address
- _infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize
- _infosWriter.Write((bool)highCq); // InfoEntry.HighCq
- _infosWriter.Write((bool)false); // InfoEntry.Stubbed
- _infosWriter.Write((int)ptcInfo.Code.Length); // InfoEntry.CodeLength
- _infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
+ InfoEntry infoEntry = new InfoEntry();
+
+ infoEntry.Address = address;
+ infoEntry.GuestSize = guestSize;
+ infoEntry.Hash = hash;
+ infoEntry.HighCq = highCq;
+ infoEntry.Stubbed = false;
+ infoEntry.CodeLength = ptcInfo.Code.Length;
+ infoEntry.RelocEntriesCount = ptcInfo.RelocEntriesCount;
+
+ SerializeStructure(_infosStream, infoEntry);
WriteCode(ptcInfo.Code.AsSpan());
@@ -915,7 +944,7 @@ namespace ARMeilleure.Translation.PTC
_codesList.Add(code.ToArray());
}
- private static bool GetEndianness()
+ internal static bool GetEndianness()
{
return BitConverter.IsLittleEndian;
}
@@ -1001,12 +1030,12 @@ namespace ARMeilleure.Translation.PTC
}
}
+ [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 42*/)]
private struct InfoEntry
{
- public const int Stride = 26; // Bytes.
-
public ulong Address;
public ulong GuestSize;
+ public Hash128 Hash;
public bool HighCq;
public bool Stubbed;
public int CodeLength;
@@ -1058,4 +1087,4 @@ namespace ARMeilleure.Translation.PTC
}
}
}
-} \ No newline at end of file
+}
diff --git a/ARMeilleure/Translation/PTC/PtcFormatter.cs b/ARMeilleure/Translation/PTC/PtcFormatter.cs
index 753c01c8..2f7a9c21 100644
--- a/ARMeilleure/Translation/PTC/PtcFormatter.cs
+++ b/ARMeilleure/Translation/PTC/PtcFormatter.cs
@@ -50,7 +50,12 @@ namespace ARMeilleure.Translation.PTC
T structure = default(T);
Span<T> spanT = MemoryMarshal.CreateSpan(ref structure, 1);
- stream.Read(MemoryMarshal.AsBytes(spanT));
+ int bytesCount = stream.Read(MemoryMarshal.AsBytes(spanT));
+
+ if (bytesCount != Unsafe.SizeOf<T>())
+ {
+ throw new EndOfStreamException();
+ }
return structure;
}
@@ -130,7 +135,12 @@ namespace ARMeilleure.Translation.PTC
T[] item = new T[itemLength];
- stream.Read(MemoryMarshal.AsBytes(item.AsSpan()));
+ int bytesCount = stream.Read(MemoryMarshal.AsBytes(item.AsSpan()));
+
+ if (bytesCount != itemLength)
+ {
+ throw new EndOfStreamException();
+ }
list.Add(item);
}
diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
index 40a30329..67719623 100644
--- a/ARMeilleure/Translation/PTC/PtcJumpTable.cs
+++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
@@ -129,7 +129,6 @@ namespace ARMeilleure.Translation.PTC
}
}
- // For future use.
public void Clean(ulong guestAddress)
{
if (Owners.TryGetValue(guestAddress, out List<int> entries))
diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs
index d7b2b0f8..e31ff230 100644
--- a/ARMeilleure/Translation/PTC/PtcProfiler.cs
+++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs
@@ -1,13 +1,15 @@
using ARMeilleure.State;
+using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
+using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Security.Cryptography;
using System.Threading;
using static ARMeilleure.Translation.PTC.PtcFormatter;
@@ -16,9 +18,9 @@ namespace ARMeilleure.Translation.PTC
{
public static class PtcProfiler
{
- private const string HeaderMagic = "Phd";
+ private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
- private const uint InternalVersion = 1713; //! Not to be incremented manually for each change to the ARMeilleure project.
+ private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project.
private const int SaveInterval = 30; // Seconds.
@@ -26,13 +28,15 @@ namespace ARMeilleure.Translation.PTC
private static readonly System.Timers.Timer _timer;
+ private static readonly ulong _outerHeaderMagic;
+
private static readonly ManualResetEvent _waitEvent;
private static readonly object _lock;
private static bool _disposed;
- private static byte[] _lastHash;
+ private static Hash128 _lastHash;
internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
@@ -46,6 +50,8 @@ namespace ARMeilleure.Translation.PTC
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
_timer.Elapsed += PreSave;
+ _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
+
_waitEvent = new ManualResetEvent(true);
_lock = new object();
@@ -90,17 +96,15 @@ namespace ARMeilleure.Translation.PTC
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
}
- internal static ConcurrentQueue<(ulong address, ExecutionMode mode, bool highCq)> GetProfiledFuncsToTranslate(ConcurrentDictionary<ulong, TranslatedFunction> funcs)
+ internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(ConcurrentDictionary<ulong, TranslatedFunction> funcs)
{
- var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, ExecutionMode mode, bool highCq)>();
+ var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
foreach (var profiledFunc in ProfiledFuncs)
{
- ulong address = profiledFunc.Key;
-
- if (!funcs.ContainsKey(address))
+ if (!funcs.ContainsKey(profiledFunc.Key))
{
- profiledFuncsToTranslate.Enqueue((address, profiledFunc.Value.Mode, profiledFunc.Value.HighCq));
+ profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value));
}
}
@@ -115,7 +119,7 @@ namespace ARMeilleure.Translation.PTC
internal static void PreLoad()
{
- _lastHash = Array.Empty<byte>();
+ _lastHash = default;
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
@@ -141,96 +145,82 @@ namespace ARMeilleure.Translation.PTC
private static bool Load(string fileName, bool isBackup)
{
- using (FileStream compressedStream = new FileStream(fileName, FileMode.Open))
- using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
- using (MemoryStream stream = new MemoryStream())
- using (MD5 md5 = MD5.Create())
+ using (FileStream compressedStream = new(fileName, FileMode.Open))
+ using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
{
- try
- {
- deflateStream.CopyTo(stream);
- }
- catch
+ OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
+
+ if (!outerHeader.IsHeaderValid())
{
InvalidateCompressedStream(compressedStream);
return false;
}
- int hashSize = md5.HashSize / 8;
-
- stream.Seek(0L, SeekOrigin.Begin);
-
- byte[] currentHash = new byte[hashSize];
- stream.Read(currentHash, 0, hashSize);
-
- byte[] expectedHash = md5.ComputeHash(stream);
-
- if (!CompareHash(currentHash, expectedHash))
+ if (outerHeader.Magic != _outerHeaderMagic)
{
InvalidateCompressedStream(compressedStream);
return false;
}
- stream.Seek((long)hashSize, SeekOrigin.Begin);
-
- Header header = ReadHeader(stream);
-
- if (header.Magic != HeaderMagic)
+ if (outerHeader.InfoFileVersion != InternalVersion)
{
InvalidateCompressedStream(compressedStream);
return false;
}
- if (header.InfoFileVersion != InternalVersion)
+ if (outerHeader.Endianness != Ptc.GetEndianness())
{
InvalidateCompressedStream(compressedStream);
return false;
}
- try
+ using (MemoryStream stream = new MemoryStream())
{
- ProfiledFuncs = Deserialize(stream);
- }
- catch
- {
- ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
+ Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
- InvalidateCompressedStream(compressedStream);
+ try
+ {
+ deflateStream.CopyTo(stream);
+ }
+ catch
+ {
+ InvalidateCompressedStream(compressedStream);
- return false;
- }
+ return false;
+ }
- _lastHash = expectedHash;
- }
+ Debug.Assert(stream.Position == stream.Length);
- long fileSize = new FileInfo(fileName).Length;
+ stream.Seek(0L, SeekOrigin.Begin);
- Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
+ Hash128 expectedHash = DeserializeStructure<Hash128>(stream);
- return true;
- }
+ Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
- private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
- {
- return currentHash.SequenceEqual(expectedHash);
- }
+ if (actualHash != expectedHash)
+ {
+ InvalidateCompressedStream(compressedStream);
- private static Header ReadHeader(Stream stream)
- {
- using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
- {
- Header header = new Header();
+ return false;
+ }
- header.Magic = headerReader.ReadString();
+ ProfiledFuncs = Deserialize(stream);
- header.InfoFileVersion = headerReader.ReadUInt32();
+ Debug.Assert(stream.Position == stream.Length);
- return header;
+ _lastHash = actualHash;
+ }
}
+
+ long fileSize = new FileInfo(fileName).Length;
+
+ Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
+
+ return true;
}
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream)
@@ -238,6 +228,11 @@ namespace ARMeilleure.Translation.PTC
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
}
+ private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
+ {
+ return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
+ }
+
private static void InvalidateCompressedStream(FileStream compressedStream)
{
compressedStream.SetLength(0L);
@@ -266,14 +261,20 @@ namespace ARMeilleure.Translation.PTC
{
int profiledFuncsCount;
+ OuterHeader outerHeader = new OuterHeader();
+
+ outerHeader.Magic = _outerHeaderMagic;
+
+ outerHeader.InfoFileVersion = InternalVersion;
+ outerHeader.Endianness = Ptc.GetEndianness();
+
+ outerHeader.SetHeaderHash();
+
using (MemoryStream stream = new MemoryStream())
- using (MD5 md5 = MD5.Create())
{
- int hashSize = md5.HashSize / 8;
+ Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
- stream.Seek((long)hashSize, SeekOrigin.Begin);
-
- WriteHeader(stream);
+ stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
lock (_lock)
{
@@ -282,22 +283,26 @@ namespace ARMeilleure.Translation.PTC
profiledFuncsCount = ProfiledFuncs.Count;
}
- stream.Seek((long)hashSize, SeekOrigin.Begin);
- byte[] hash = md5.ComputeHash(stream);
+ Debug.Assert(stream.Position == stream.Length);
+
+ stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
+ Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
stream.Seek(0L, SeekOrigin.Begin);
- stream.Write(hash, 0, hashSize);
+ SerializeStructure(stream, hash);
- if (CompareHash(hash, _lastHash))
+ if (hash == _lastHash)
{
return;
}
- using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
- using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
+ using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
+ using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
{
try
{
+ SerializeStructure(compressedStream, outerHeader);
+
stream.WriteTo(deflateStream);
_lastHash = hash;
@@ -306,7 +311,7 @@ namespace ARMeilleure.Translation.PTC
{
compressedStream.Position = 0L;
- _lastHash = Array.Empty<byte>();
+ _lastHash = default;
}
if (compressedStream.Position < compressedStream.Length)
@@ -324,26 +329,35 @@ namespace ARMeilleure.Translation.PTC
}
}
- private static void WriteHeader(Stream stream)
- {
- using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
- {
- headerWriter.Write((string)HeaderMagic); // Header.Magic
-
- headerWriter.Write((uint)InternalVersion); // Header.InfoFileVersion
- }
- }
-
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
{
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
}
- private struct Header
+ [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)]
+ private struct OuterHeader
{
- public string Magic;
+ public ulong Magic;
public uint InfoFileVersion;
+
+ public bool Endianness;
+
+ public Hash128 HeaderHash;
+
+ public void SetHeaderHash()
+ {
+ Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
+
+ HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>()));
+ }
+
+ public bool IsHeaderValid()
+ {
+ Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
+
+ return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash;
+ }
}
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
diff --git a/ARMeilleure/Translation/SsaConstruction.cs b/ARMeilleure/Translation/SsaConstruction.cs
index 1c6e83c9..76cb9a44 100644
--- a/ARMeilleure/Translation/SsaConstruction.cs
+++ b/ARMeilleure/Translation/SsaConstruction.cs
@@ -45,7 +45,7 @@ namespace ARMeilleure.Translation
public static void Construct(ControlFlowGraph cfg)
{
var globalDefs = new DefMap[cfg.Blocks.Count];
- var localDefs = new Operand[RegisterConsts.TotalCount];
+ var localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
var dfPhiBlocks = new Queue<BasicBlock>();
@@ -264,6 +264,12 @@ namespace ARMeilleure.Translation
return true;
}
+ else if (operand is { Kind: OperandKind.LocalVariable } && operand.GetLocalNumber() > 0)
+ {
+ result = RegisterConsts.TotalCount + operand.GetLocalNumber() - 1;
+
+ return true;
+ }
result = -1;
@@ -274,7 +280,7 @@ namespace ARMeilleure.Translation
{
if (!TryGetId(operand, out int key))
{
- Debug.Fail("OperandKind must be Register.");
+ Debug.Fail("OperandKind must be Register or a numbered LocalVariable.");
}
return key;
diff --git a/ARMeilleure/Translation/TranslatedFunction.cs b/ARMeilleure/Translation/TranslatedFunction.cs
index 8b075928..04dd769c 100644
--- a/ARMeilleure/Translation/TranslatedFunction.cs
+++ b/ARMeilleure/Translation/TranslatedFunction.cs
@@ -1,24 +1,22 @@
+using ARMeilleure.Common;
using System;
using System.Runtime.InteropServices;
-using System.Threading;
namespace ARMeilleure.Translation
{
class TranslatedFunction
{
- private const int MinCallsForRejit = 100;
-
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
- private int _callCount;
-
+ public Counter<uint> CallCounter { get; }
public ulong GuestSize { get; }
public bool HighCq { get; }
public IntPtr FuncPtr { get; }
- public TranslatedFunction(GuestFunction func, ulong guestSize, bool highCq)
+ public TranslatedFunction(GuestFunction func, Counter<uint> callCounter, ulong guestSize, bool highCq)
{
_func = func;
+ CallCounter = callCounter;
GuestSize = guestSize;
HighCq = highCq;
FuncPtr = Marshal.GetFunctionPointerForDelegate(func);
@@ -28,15 +26,5 @@ namespace ARMeilleure.Translation
{
return _func(context.NativeContextPtr);
}
-
- public bool ShouldRejit()
- {
- return !HighCq && Interlocked.Increment(ref _callCount) == MinCallsForRejit;
- }
-
- public void ResetCallCount()
- {
- Interlocked.Exchange(ref _callCount, 0);
- }
}
} \ No newline at end of file
diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs
index 73a321fd..81af0681 100644
--- a/ARMeilleure/Translation/Translator.cs
+++ b/ARMeilleure/Translation/Translator.cs
@@ -1,3 +1,4 @@
+using ARMeilleure.Common;
using ARMeilleure.Decoders;
using ARMeilleure.Diagnostics;
using ARMeilleure.Instructions;
@@ -6,11 +7,11 @@ using ARMeilleure.Memory;
using ARMeilleure.State;
using ARMeilleure.Translation.Cache;
using ARMeilleure.Translation.PTC;
+using Ryujinx.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Linq;
using System.Runtime;
using System.Threading;
@@ -22,23 +23,27 @@ namespace ARMeilleure.Translation
{
public class Translator
{
+ private const int CountTableCapacity = 4 * 1024 * 1024;
+
private readonly IJitMemoryAllocator _allocator;
private readonly IMemoryManager _memory;
private readonly ConcurrentDictionary<ulong, TranslatedFunction> _funcs;
- private readonly ConcurrentQueue<KeyValuePair<ulong, IntPtr>> _oldFuncs;
+ private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
+ private readonly ConcurrentDictionary<ulong, object> _backgroundSet;
private readonly ConcurrentStack<RejitRequest> _backgroundStack;
private readonly AutoResetEvent _backgroundTranslatorEvent;
private readonly ReaderWriterLock _backgroundTranslatorLock;
private JumpTable _jumpTable;
internal JumpTable JumpTable => _jumpTable;
+ internal EntryTable<uint> CountTable { get; }
private volatile int _threadCount;
// FIXME: Remove this once the init logic of the emulator will be redone.
- public static ManualResetEvent IsReadyForTranslation = new ManualResetEvent(false);
+ public static readonly ManualResetEvent IsReadyForTranslation = new(false);
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory)
{
@@ -46,12 +51,15 @@ namespace ARMeilleure.Translation
_memory = memory;
_funcs = new ConcurrentDictionary<ulong, TranslatedFunction>();
- _oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, IntPtr>>();
+ _oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
+ _backgroundSet = new ConcurrentDictionary<ulong, object>();
_backgroundStack = new ConcurrentStack<RejitRequest>();
_backgroundTranslatorEvent = new AutoResetEvent(false);
_backgroundTranslatorLock = new ReaderWriterLock();
+ CountTable = new EntryTable<uint>();
+
JitCache.Initialize(allocator);
DirectCallStubs.InitializeStubs();
@@ -63,9 +71,16 @@ namespace ARMeilleure.Translation
{
_backgroundTranslatorLock.AcquireReaderLock(Timeout.Infinite);
- if (_backgroundStack.TryPop(out RejitRequest request))
+ if (_backgroundStack.TryPop(out RejitRequest request) &&
+ _backgroundSet.TryRemove(request.Address, out _))
{
- TranslatedFunction func = Translate(_memory, _jumpTable, request.Address, request.Mode, highCq: true);
+ TranslatedFunction func = Translate(
+ _memory,
+ _jumpTable,
+ CountTable,
+ request.Address,
+ request.Mode,
+ highCq: true);
_funcs.AddOrUpdate(request.Address, func, (key, oldFunc) =>
{
@@ -89,7 +104,8 @@ namespace ARMeilleure.Translation
}
}
- _backgroundTranslatorEvent.Set(); // Wake up any other background translator threads, to encourage them to exit.
+ // Wake up any other background translator threads, to encourage them to exit.
+ _backgroundTranslatorEvent.Set();
}
public void Execute(State.ExecutionContext context, ulong address)
@@ -104,18 +120,21 @@ namespace ARMeilleure.Translation
if (Ptc.State == PtcState.Enabled)
{
Debug.Assert(_funcs.Count == 0);
- Ptc.LoadTranslations(_funcs, _memory, _jumpTable);
- Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable);
+ Ptc.LoadTranslations(_funcs, _memory, _jumpTable, CountTable);
+ Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable, CountTable);
}
PtcProfiler.Start();
Ptc.Disable();
- // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core+ht etc).
- // All threads are normal priority except from the last, which just fills as much of the last core as the os lets it with a low priority.
- // If we only have one rejit thread, it should be normal priority as highCq code is performance critical.
- // TODO: Use physical cores rather than logical. This only really makes sense for processors with hyperthreading. Requires OS specific code.
+ // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
+ // etc). All threads are normal priority except from the last, which just fills as much of the last core
+ // as the os lets it with a low priority. If we only have one rejit thread, it should be normal priority
+ // as highCq code is performance critical.
+ //
+ // TODO: Use physical cores rather than logical. This only really makes sense for processors with
+ // hyperthreading. Requires OS specific code.
int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3);
int threadCount = Math.Min(4, unboundedThreadCount);
@@ -156,6 +175,8 @@ namespace ARMeilleure.Translation
_jumpTable.Dispose();
_jumpTable = null;
+ CountTable.Dispose();
+
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
}
}
@@ -173,11 +194,11 @@ namespace ARMeilleure.Translation
return nextAddr;
}
- internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode, bool hintRejit = false)
+ internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
{
if (!_funcs.TryGetValue(address, out TranslatedFunction func))
{
- func = Translate(_memory, _jumpTable, address, mode, highCq: false);
+ func = Translate(_memory, _jumpTable, CountTable, address, mode, highCq: false);
TranslatedFunction getFunc = _funcs.GetOrAdd(address, func);
@@ -193,18 +214,18 @@ namespace ARMeilleure.Translation
}
}
- if (hintRejit && func.ShouldRejit())
- {
- _backgroundStack.Push(new RejitRequest(address, mode));
- _backgroundTranslatorEvent.Set();
- }
-
return func;
}
- internal static TranslatedFunction Translate(IMemoryManager memory, JumpTable jumpTable, ulong address, ExecutionMode mode, bool highCq)
+ internal static TranslatedFunction Translate(
+ IMemoryManager memory,
+ JumpTable jumpTable,
+ EntryTable<uint> countTable,
+ ulong address,
+ ExecutionMode mode,
+ bool highCq)
{
- ArmEmitterContext context = new ArmEmitterContext(memory, jumpTable, address, highCq, Aarch32Mode.User);
+ var context = new ArmEmitterContext(memory, jumpTable, countTable, address, highCq, Aarch32Mode.User);
Logger.StartPass(PassName.Decoding);
@@ -216,6 +237,13 @@ namespace ARMeilleure.Translation
Logger.StartPass(PassName.Translation);
+ Counter<uint> counter = null;
+
+ if (!context.HighCq)
+ {
+ EmitRejitCheck(context, out counter);
+ }
+
EmitSynchronization(context);
if (blocks[0].Address != address)
@@ -255,10 +283,12 @@ namespace ARMeilleure.Translation
ResetPool(highCq ? 1 : 0);
- Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, highCq, ptcInfo);
+ Hash128 hash = Ptc.ComputeHash(memory, address, funcSize);
+
+ Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, hash, highCq, ptcInfo);
}
- return new TranslatedFunction(func, funcSize, highCq);
+ return new TranslatedFunction(func, counter, funcSize, highCq);
}
internal static void PreparePool(int groupId = 0)
@@ -320,7 +350,7 @@ namespace ARMeilleure.Translation
if (block.Exit)
{
- InstEmitFlowHelper.EmitTailContinue(context, Const(block.Address), block.TailCall);
+ InstEmitFlowHelper.EmitTailContinue(context, Const(block.Address));
}
else
{
@@ -368,29 +398,43 @@ namespace ARMeilleure.Translation
return context.GetControlFlowGraph();
}
- internal static void EmitSynchronization(EmitterContext context)
+ internal static void EmitRejitCheck(ArmEmitterContext context, out Counter<uint> counter)
{
- long countOffs = NativeContext.GetCounterOffset();
+ const int MinsCallForRejit = 100;
- Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));
+ counter = new Counter<uint>(context.CountTable);
- Operand count = context.Load(OperandType.I32, countAddr);
+ Operand lblEnd = Label();
+
+ Operand address = Const(ref counter.Value, Ptc.CountTableIndex);
+ Operand curCount = context.Load(OperandType.I32, address);
+ Operand count = context.Add(curCount, Const(1));
+ context.Store(address, count);
+ context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold);
+
+ context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
+
+ context.MarkLabel(lblEnd);
+ }
+
+ internal static void EmitSynchronization(EmitterContext context)
+ {
+ long countOffs = NativeContext.GetCounterOffset();
Operand lblNonZero = Label();
- Operand lblExit = Label();
+ Operand lblExit = Label();
+ Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));
+ Operand count = context.Load(OperandType.I32, countAddr);
context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold);
Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
-
context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);
context.Return(Const(0L));
context.MarkLabel(lblNonZero);
-
count = context.Subtract(count, Const(1));
-
context.Store(countAddr, count);
context.MarkLabel(lblExit);
@@ -404,9 +448,18 @@ namespace ARMeilleure.Translation
// TODO: Completely remove functions overlapping the specified range from the cache.
}
+ internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode)
+ {
+ if (_backgroundSet.TryAdd(guestAddress, null))
+ {
+ _backgroundStack.Push(new RejitRequest(guestAddress, mode));
+ _backgroundTranslatorEvent.Set();
+ }
+ }
+
private void EnqueueForDeletion(ulong guestAddress, TranslatedFunction func)
{
- _oldFuncs.Enqueue(new KeyValuePair<ulong, IntPtr>(guestAddress, func.FuncPtr));
+ _oldFuncs.Enqueue(new(guestAddress, func));
}
private void ClearJitCache()
@@ -414,16 +467,20 @@ namespace ARMeilleure.Translation
// Ensure no attempt will be made to compile new functions due to rejit.
ClearRejitQueue(allowRequeue: false);
- foreach (var kv in _funcs)
+ foreach (var func in _funcs.Values)
{
- JitCache.Unmap(kv.Value.FuncPtr);
+ JitCache.Unmap(func.FuncPtr);
+
+ func.CallCounter?.Dispose();
}
_funcs.Clear();
while (_oldFuncs.TryDequeue(out var kv))
{
- JitCache.Unmap(kv.Value);
+ JitCache.Unmap(kv.Value.FuncPtr);
+
+ kv.Value.CallCounter?.Dispose();
}
}
@@ -435,10 +492,12 @@ namespace ARMeilleure.Translation
{
while (_backgroundStack.TryPop(out var request))
{
- if (_funcs.TryGetValue(request.Address, out var func))
+ if (_funcs.TryGetValue(request.Address, out var func) && func.CallCounter != null)
{
- func.ResetCallCount();
+ Volatile.Write(ref func.CallCounter.Value, 0);
}
+
+ _backgroundSet.TryRemove(request.Address, out _);
}
}
else
diff --git a/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj
new file mode 100644
index 00000000..6619a500
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
+ <ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs b/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs
new file mode 100644
index 00000000..71ef414a
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.Audio.Backends.SDL2
+{
+ class SDL2AudioBuffer
+ {
+ public readonly ulong DriverIdentifier;
+ public readonly ulong SampleCount;
+ public ulong SamplePlayed;
+
+ public SDL2AudioBuffer(ulong driverIdentifier, ulong sampleCount)
+ {
+ DriverIdentifier = driverIdentifier;
+ SampleCount = sampleCount;
+ SamplePlayed = 0;
+ }
+ }
+}
diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs
new file mode 100644
index 00000000..07131d1d
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs
@@ -0,0 +1,184 @@
+using Ryujinx.Audio.Backends.Common;
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Memory;
+using Ryujinx.SDL2.Common;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
+using static SDL2.SDL;
+
+namespace Ryujinx.Audio.Backends.SDL2
+{
+ public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver
+ {
+ private object _lock = new object();
+
+ private ManualResetEvent _updateRequiredEvent;
+ private List<SDL2HardwareDeviceSession> _sessions;
+
+ public SDL2HardwareDeviceDriver()
+ {
+ _updateRequiredEvent = new ManualResetEvent(false);
+ _sessions = new List<SDL2HardwareDeviceSession>();
+
+ SDL2Driver.Instance.Initialize();
+ }
+
+ public static bool IsSupported => IsSupportedInternal();
+
+ private static bool IsSupportedInternal()
+ {
+ uint device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, Constants.TargetSampleCount, null);
+
+ if (device != 0)
+ {
+ SDL_CloseAudioDevice(device);
+ }
+
+ return device != 0;
+ }
+
+ public ManualResetEvent GetUpdateRequiredEvent()
+ {
+ return _updateRequiredEvent;
+ }
+
+ public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
+ {
+ if (channelCount == 0)
+ {
+ channelCount = 2;
+ }
+
+ if (sampleRate == 0)
+ {
+ sampleRate = Constants.TargetSampleRate;
+ }
+
+ if (direction != Direction.Output)
+ {
+ throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
+ }
+
+ lock (_lock)
+ {
+ SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
+
+ _sessions.Add(session);
+
+ return session;
+ }
+ }
+
+ internal void Unregister(SDL2HardwareDeviceSession session)
+ {
+ lock (_lock)
+ {
+ _sessions.Remove(session);
+ }
+ }
+
+ private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
+ {
+ return new SDL_AudioSpec
+ {
+ channels = (byte)requestedChannelCount,
+ format = GetSDL2Format(requestedSampleFormat),
+ freq = (int)requestedSampleRate,
+ samples = (ushort)sampleCount
+ };
+ }
+
+ internal static ushort GetSDL2Format(SampleFormat format)
+ {
+ return format switch
+ {
+ SampleFormat.PcmInt8 => AUDIO_S8,
+ SampleFormat.PcmInt16 => AUDIO_S16,
+ SampleFormat.PcmInt32 => AUDIO_S32,
+ SampleFormat.PcmFloat => AUDIO_F32,
+ _ => throw new ArgumentException($"Unsupported sample format {format}"),
+ };
+ }
+
+ // TODO: Fix this in SDL2-CS.
+ [DllImport("SDL2", EntryPoint = "SDL_OpenAudioDevice", CallingConvention = CallingConvention.Cdecl)]
+ private static extern uint SDL_OpenAudioDevice_Workaround(
+ IntPtr name,
+ int iscapture,
+ ref SDL_AudioSpec desired,
+ out SDL_AudioSpec obtained,
+ uint allowed_changes
+ );
+
+ internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount, SDL_AudioCallback callback)
+ {
+ SDL_AudioSpec desired = GetSDL2Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount, sampleCount);
+
+ desired.callback = callback;
+
+ uint device = SDL_OpenAudioDevice_Workaround(IntPtr.Zero, 0, ref desired, out SDL_AudioSpec got, 0);
+
+ if (device == 0)
+ {
+ return 0;
+ }
+
+ bool isValid = got.format == desired.format && got.freq == desired.freq && got.channels == desired.channels;
+
+ if (!isValid)
+ {
+ SDL_CloseAudioDevice(device);
+
+ return 0;
+ }
+
+ return device;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ while (_sessions.Count > 0)
+ {
+ SDL2HardwareDeviceSession session = _sessions[_sessions.Count - 1];
+
+ session.Dispose();
+ }
+
+ SDL2Driver.Instance.Dispose();
+ }
+ }
+
+ public bool SupportsSampleRate(uint sampleRate)
+ {
+ return true;
+ }
+
+ public bool SupportsSampleFormat(SampleFormat sampleFormat)
+ {
+ return sampleFormat != SampleFormat.PcmInt24;
+ }
+
+ public bool SupportsChannelCount(uint channelCount)
+ {
+ return true;
+ }
+
+ public bool SupportsDirection(Direction direction)
+ {
+ // TODO: add direction input when supported.
+ return direction == Direction.Output;
+ }
+ }
+}
diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs
new file mode 100644
index 00000000..344dd9b6
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs
@@ -0,0 +1,223 @@
+using Ryujinx.Audio.Backends.Common;
+using Ryujinx.Audio.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.Memory;
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using static SDL2.SDL;
+
+namespace Ryujinx.Audio.Backends.SDL2
+{
+ class SDL2HardwareDeviceSession : HardwareDeviceSessionOutputBase
+ {
+ private SDL2HardwareDeviceDriver _driver;
+ private ConcurrentQueue<SDL2AudioBuffer> _queuedBuffers;
+ private DynamicRingBuffer _ringBuffer;
+ private ulong _playedSampleCount;
+ private ManualResetEvent _updateRequiredEvent;
+ private uint _outputStream;
+ private SDL_AudioCallback _callbackDelegate;
+ private int _bytesPerFrame;
+ private uint _sampleCount;
+ private bool _started;
+ private float _volume;
+ private ushort _nativeSampleFormat;
+
+ public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
+ {
+ _driver = driver;
+ _updateRequiredEvent = _driver.GetUpdateRequiredEvent();
+ _queuedBuffers = new ConcurrentQueue<SDL2AudioBuffer>();
+ _ringBuffer = new DynamicRingBuffer();
+ _callbackDelegate = Update;
+ _bytesPerFrame = BackendHelper.GetSampleSize(RequestedSampleFormat) * (int)RequestedChannelCount;
+ _nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
+ _sampleCount = uint.MaxValue;
+ _started = false;
+ _volume = 1.0f;
+ }
+
+ private void EnsureAudioStreamSetup(AudioBuffer buffer)
+ {
+ bool needAudioSetup = _outputStream == 0 || ((uint)GetSampleCount(buffer) % _sampleCount) != 0;
+
+ if (needAudioSetup)
+ {
+ _sampleCount = Math.Max(Constants.TargetSampleCount, (uint)GetSampleCount(buffer));
+
+ uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
+
+ if (newOutputStream == 0)
+ {
+ // No stream in place, this is unexpected.
+ throw new InvalidOperationException($"OpenStream failed with error: \"{SDL_GetError()}\"");
+ }
+ else
+ {
+ if (_outputStream != 0)
+ {
+ SDL_CloseAudioDevice(_outputStream);
+ }
+
+ _outputStream = newOutputStream;
+
+ SDL_PauseAudioDevice(_outputStream, _started ? 0 : 1);
+
+ Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}");
+ }
+ }
+ }
+
+ // TODO: Add this variant with pointer to SDL2-CS.
+ [DllImport("SDL2", EntryPoint = "SDL_MixAudioFormat", CallingConvention = CallingConvention.Cdecl)]
+ private static extern unsafe uint SDL_MixAudioFormat(IntPtr dst, IntPtr src, ushort format, uint len, int volume);
+
+ private unsafe void Update(IntPtr userdata, IntPtr stream, int streamLength)
+ {
+ Span<byte> streamSpan = new Span<byte>((void*)stream, streamLength);
+
+ int maxFrameCount = (int)GetSampleCount(streamLength);
+ int bufferedFrames = _ringBuffer.Length / _bytesPerFrame;
+
+ int frameCount = Math.Min(bufferedFrames, maxFrameCount);
+
+ if (frameCount == 0)
+ {
+ // SDL2 left the responsability to the user to clear the buffer.
+ streamSpan.Fill(0);
+
+ return;
+ }
+
+ byte[] samples = new byte[frameCount * _bytesPerFrame];
+
+ _ringBuffer.Read(samples, 0, samples.Length);
+
+ samples.AsSpan().CopyTo(streamSpan);
+ streamSpan.Slice(samples.Length).Fill(0);
+
+ // Apply volume to written data
+ SDL_MixAudioFormat(stream, stream, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
+
+ ulong sampleCount = GetSampleCount(samples.Length);
+
+ ulong availaibleSampleCount = sampleCount;
+
+ bool needUpdate = false;
+
+ while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
+ {
+ ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed);
+ ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
+
+ ulong currentSamplePlayed = Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount);
+ availaibleSampleCount -= playedAudioBufferSampleCount;
+
+ if (currentSamplePlayed == driverBuffer.SampleCount)
+ {
+ _queuedBuffers.TryDequeue(out _);
+
+ needUpdate = true;
+ }
+
+ Interlocked.Add(ref _playedSampleCount, playedAudioBufferSampleCount);
+ }
+
+ // Notify the output if needed.
+ if (needUpdate)
+ {
+ _updateRequiredEvent.Set();
+ }
+ }
+
+ public override ulong GetPlayedSampleCount()
+ {
+ return Interlocked.Read(ref _playedSampleCount);
+ }
+
+ public override float GetVolume()
+ {
+ return _volume;
+ }
+
+ public override void PrepareToClose() { }
+
+ public override void QueueBuffer(AudioBuffer buffer)
+ {
+ EnsureAudioStreamSetup(buffer);
+
+ SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer));
+
+ _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
+
+ _queuedBuffers.Enqueue(driverBuffer);
+ }
+
+ public override void SetVolume(float volume)
+ {
+ _volume = volume;
+ }
+
+ public override void Start()
+ {
+ if (!_started)
+ {
+ if (_outputStream != 0)
+ {
+ SDL_PauseAudioDevice(_outputStream, 0);
+ }
+
+ _started = true;
+ }
+ }
+
+ public override void Stop()
+ {
+ if (_started)
+ {
+ if (_outputStream != 0)
+ {
+ SDL_PauseAudioDevice(_outputStream, 1);
+ }
+
+ _started = false;
+ }
+ }
+
+ public override void UnregisterBuffer(AudioBuffer buffer) { }
+
+ public override bool WasBufferFullyConsumed(AudioBuffer buffer)
+ {
+ if (!_queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
+ {
+ return true;
+ }
+
+ return driverBuffer.DriverIdentifier != buffer.DataPointer;
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ PrepareToClose();
+ Stop();
+
+ if (_outputStream != 0)
+ {
+ SDL_CloseAudioDevice(_outputStream);
+ }
+
+ _driver.Unregister(this);
+ }
+ }
+
+ public override void Dispose()
+ {
+ Dispose(true);
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs b/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs
index 1e000e4c..f1f0039c 100644
--- a/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs
+++ b/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs
@@ -18,6 +18,7 @@
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using Ryujinx.Memory;
+using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Backends.Common
{
@@ -52,7 +53,13 @@ namespace Ryujinx.Audio.Backends.Common
protected ulong GetSampleCount(AudioBuffer buffer)
{
- return (ulong)BackendHelper.GetSampleCount(RequestedSampleFormat, (int)RequestedChannelCount, (int)buffer.DataSize);
+ return GetSampleCount((int)buffer.DataSize);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected ulong GetSampleCount(int dataSize)
+ {
+ return (ulong)BackendHelper.GetSampleCount(RequestedSampleFormat, (int)RequestedChannelCount, dataSize);
}
public abstract void Dispose();
diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs
index 1597805c..28b344cd 100644
--- a/Ryujinx.Common/Logging/LogClass.cs
+++ b/Ryujinx.Common/Logging/LogClass.cs
@@ -9,6 +9,7 @@ namespace Ryujinx.Common.Logging
Cpu,
Font,
Emulation,
+ FFmpeg,
Gpu,
Hid,
Host1x,
diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs
index 7c400e91..6194d5b2 100644
--- a/Ryujinx.Cpu/MemoryHelper.cs
+++ b/Ryujinx.Cpu/MemoryHelper.cs
@@ -8,28 +8,28 @@ namespace Ryujinx.Cpu
{
public static class MemoryHelper
{
- public static void FillWithZeros(IVirtualMemoryManager memory, long position, int size)
+ public static void FillWithZeros(IVirtualMemoryManager memory, ulong position, int size)
{
int size8 = size & ~(8 - 1);
for (int offs = 0; offs < size8; offs += 8)
{
- memory.Write<long>((ulong)(position + offs), 0);
+ memory.Write<long>(position + (ulong)offs, 0);
}
for (int offs = size8; offs < (size - size8); offs++)
{
- memory.Write<byte>((ulong)(position + offs), 0);
+ memory.Write<byte>(position + (ulong)offs, 0);
}
}
- public unsafe static T Read<T>(IVirtualMemoryManager memory, long position) where T : struct
+ public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : struct
{
long size = Marshal.SizeOf<T>();
byte[] data = new byte[size];
- memory.Read((ulong)position, data);
+ memory.Read(position, data);
fixed (byte* ptr = data)
{
@@ -37,7 +37,7 @@ namespace Ryujinx.Cpu
}
}
- public unsafe static long Write<T>(IVirtualMemoryManager memory, long position, T value) where T : struct
+ public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : struct
{
long size = Marshal.SizeOf<T>();
@@ -48,18 +48,18 @@ namespace Ryujinx.Cpu
Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
}
- memory.Write((ulong)position, data);
+ memory.Write(position, data);
- return size;
+ return (ulong)size;
}
- public static string ReadAsciiString(IVirtualMemoryManager memory, long position, long maxSize = -1)
+ public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
{
using (MemoryStream ms = new MemoryStream())
{
for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
{
- byte value = memory.Read<byte>((ulong)(position + offs));
+ byte value = memory.Read<byte>(position + (ulong)offs);
if (value == 0)
{
diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs
index d496de67..e388f0e5 100644
--- a/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -5,6 +5,7 @@ namespace Ryujinx.Graphics.GAL
public bool SupportsAstcCompression { get; }
public bool SupportsImageLoadFormatted { get; }
public bool SupportsNonConstantTextureOffset { get; }
+ public bool SupportsMismatchingViewFormat { get; }
public bool SupportsViewportSwizzle { get; }
public int MaximumComputeSharedMemorySize { get; }
@@ -15,6 +16,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsAstcCompression,
bool supportsImageLoadFormatted,
bool supportsNonConstantTextureOffset,
+ bool supportsMismatchingViewFormat,
bool supportsViewportSwizzle,
int maximumComputeSharedMemorySize,
float maximumSupportedAnisotropy,
@@ -23,6 +25,7 @@ namespace Ryujinx.Graphics.GAL
SupportsAstcCompression = supportsAstcCompression;
SupportsImageLoadFormatted = supportsImageLoadFormatted;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
+ SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
SupportsViewportSwizzle = supportsViewportSwizzle;
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
MaximumSupportedAnisotropy = maximumSupportedAnisotropy;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
index 033311b2..ae9bdb0d 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
@@ -39,6 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
private bool _isAnyVbInstanced;
private bool _vsUsesInstanceId;
+ private byte _vsClipDistancesWritten;
private bool _forceShaderUpdate;
@@ -993,7 +994,15 @@ namespace Ryujinx.Graphics.Gpu.Engine
ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses);
- _vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
+ byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
+
+ _vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
+ _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0;
+
+ if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
+ {
+ UpdateUserClipState(state);
+ }
int storageBufferBindingsCount = 0;
int uniformBufferBindingsCount = 0;
@@ -1098,7 +1107,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="state">Current GPU state</param>
private void UpdateUserClipState(GpuState state)
{
- int clipMask = state.Get<int>(MethodOffset.ClipDistanceEnable);
+ int clipMask = state.Get<int>(MethodOffset.ClipDistanceEnable) & _vsClipDistancesWritten;
for (int i = 0; i < Constants.TotalClipDistances; ++i)
{
diff --git a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs
index af980e77..7ef102e2 100644
--- a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs
+++ b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu
/// <summary>
/// Max Anisotropy. Values range from 0 - 16. Set to -1 to let the game decide.
/// </summary>
- public static float MaxAnisotropy;
+ public static float MaxAnisotropy = -1;
/// <summary>
/// Base directory used to write shader code dumps.
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 6d2510f1..4d7e31c5 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -1014,6 +1014,15 @@ namespace Ryujinx.Graphics.Gpu.Image
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
+ if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
+ {
+ // AMD and Intel have a bug where the view format is always ignored;
+ // they use the parent format instead.
+ // Create a copy dependency to avoid this issue.
+
+ result = TextureViewCompatibility.CopyOnly;
+ }
+
return (Info.SamplesInX == info.SamplesInX &&
Info.SamplesInY == info.SamplesInY) ? result : TextureViewCompatibility.Incompatible;
}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index 7fea7ebe..ae610a76 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -398,10 +398,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="state">The current GPU state</param>
/// <param name="stageIndex">The stage number where the texture is bound</param>
/// <param name="handle">The texture handle</param>
+ /// <param name="cbufSlot">The texture handle's constant buffer slot</param>
/// <returns>The texture descriptor for the specified texture</returns>
- public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle)
+ public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle, int cbufSlot)
{
- int packedId = ReadPackedId(stageIndex, handle, state.Get<int>(MethodOffset.TextureBufferIndex));
+ int packedId = ReadPackedId(stageIndex, handle, cbufSlot < 0 ? state.Get<int>(MethodOffset.TextureBufferIndex) : cbufSlot);
int textureId = UnpackTextureId(packedId);
var poolState = state.Get<PoolState>(MethodOffset.TexturePoolState);
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 17bb553f..e08e55ee 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -340,10 +340,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="handle">Shader "fake" handle of the texture</param>
+ /// <param name="cbufSlot">Shader constant buffer slot of the texture</param>
/// <returns>The texture descriptor</returns>
- public TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle)
+ public TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle, int cbufSlot)
{
- return _cpBindingsManager.GetTextureDescriptor(state, 0, handle);
+ return _cpBindingsManager.GetTextureDescriptor(state, 0, handle, cbufSlot);
}
/// <summary>
@@ -352,10 +353,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="state">Current GPU state</param>
/// <param name="stageIndex">Index of the shader stage where the texture is bound</param>
/// <param name="handle">Shader "fake" handle of the texture</param>
+ /// <param name="cbufSlot">Shader constant buffer slot of the texture</param>
/// <returns>The texture descriptor</returns>
- public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle)
+ public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle, int cbufSlot)
{
- return _gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle);
+ return _gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle, cbufSlot);
}
/// <summary>
@@ -1297,4 +1299,4 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
index 1ec4ab74..f6caddef 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
@@ -417,7 +417,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
{
foreach (int textureHandle in context.TextureHandlesForCache)
{
- GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle)).ToCache();
+ GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle, -1)).ToCache();
textureDescriptor.Handle = (uint)textureHandle;
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
index f592919f..b538e2de 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
@@ -75,7 +75,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
programInfo.SBuffers.Count,
programInfo.Textures.Count,
programInfo.Images.Count,
- programInfo.UsesInstanceId);
+ programInfo.UsesInstanceId,
+ programInfo.ClipDistancesWritten);
CBuffers = programInfo.CBuffers.ToArray();
SBuffers = programInfo.SBuffers.ToArray();
Textures = programInfo.Textures.ToArray();
@@ -88,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
/// <returns>A new <see cref="ShaderProgramInfo"/> from this instance</returns>
internal ShaderProgramInfo ToShaderProgramInfo()
{
- return new ShaderProgramInfo(CBuffers, SBuffers, Textures, Images, Header.UsesInstanceId);
+ return new ShaderProgramInfo(CBuffers, SBuffers, Textures, Images, Header.UsesInstanceId, Header.ClipDistancesWritten);
}
/// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs
index 9b1af8fb..7f27124f 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs
@@ -42,9 +42,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
public bool InUse;
/// <summary>
+ /// Mask of clip distances that are written to on the shader.
+ /// </summary>
+ public byte ClipDistancesWritten;
+
+ /// <summary>
/// Reserved / unused.
/// </summary>
- public short Reserved;
+ public byte Reserved;
/// <summary>
/// Create a new host shader cache entry header.
@@ -54,14 +59,21 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
/// <param name="texturesCount">Count of texture descriptors</param>
/// <param name="imagesCount">Count of image descriptors</param>
/// <param name="usesInstanceId">Set to true if the shader uses instance id</param>
- public HostShaderCacheEntryHeader(int cBuffersCount, int sBuffersCount, int texturesCount, int imagesCount, bool usesInstanceId) : this()
+ public HostShaderCacheEntryHeader(
+ int cBuffersCount,
+ int sBuffersCount,
+ int texturesCount,
+ int imagesCount,
+ bool usesInstanceId,
+ byte clipDistancesWritten) : this()
{
- CBuffersCount = cBuffersCount;
- SBuffersCount = sBuffersCount;
- TexturesCount = texturesCount;
- ImagesCount = imagesCount;
- UsesInstanceId = usesInstanceId;
- InUse = true;
+ CBuffersCount = cBuffersCount;
+ SBuffersCount = sBuffersCount;
+ TexturesCount = texturesCount;
+ ImagesCount = imagesCount;
+ UsesInstanceId = usesInstanceId;
+ ClipDistancesWritten = clipDistancesWritten;
+ InUse = true;
}
}
}
diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs
index f714d97b..a7bd4edb 100644
--- a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs
@@ -140,8 +140,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Gets the texture descriptor for a given texture on the pool.
/// </summary>
/// <param name="handle">Index of the texture (this is the word offset of the handle in the constant buffer)</param>
+ /// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
/// <returns>Texture descriptor</returns>
- public override Image.ITextureDescriptor GetTextureDescriptor(int handle)
+ public override Image.ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot)
{
if (!_textureDescriptors.TryGetValue(handle, out GuestTextureDescriptor textureDescriptor))
{
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index c0ad481e..f5373bd6 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -184,16 +184,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Gets the texture descriptor for a given texture on the pool.
/// </summary>
/// <param name="handle">Index of the texture (this is the word offset of the handle in the constant buffer)</param>
+ /// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
/// <returns>Texture descriptor</returns>
- public override Image.ITextureDescriptor GetTextureDescriptor(int handle)
+ public override Image.ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot)
{
if (_compute)
{
- return _context.Methods.TextureManager.GetComputeTextureDescriptor(_state, handle);
+ return _context.Methods.TextureManager.GetComputeTextureDescriptor(_state, handle, cbufSlot);
}
else
{
- return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(_state, _stageIndex, handle);
+ return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(_state, _stageIndex, handle, cbufSlot);
}
}
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index bfd46c82..f6dcf052 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Version of the codegen (to be changed when codegen or guest format change).
/// </summary>
- private const ulong ShaderCodeGenVersion = 2200;
+ private const ulong ShaderCodeGenVersion = 2261;
// Progress reporting helpers
private volatile int _shaderCount;
diff --git a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs
index dc0e392b..904a0fd4 100644
--- a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
public abstract T MemoryRead<T>(ulong address) where T : unmanaged;
- public abstract ITextureDescriptor GetTextureDescriptor(int handle);
+ public abstract ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot);
/// <summary>
/// Queries texture format information, for shaders using image load or store.
@@ -18,10 +18,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// If the format of the texture is a compressed, depth or unsupported format, then a default value is returned.
/// </remarks>
/// <param name="handle">Texture handle</param>
+ /// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
/// <returns>Color format of the non-compressed texture</returns>
- public TextureFormat QueryTextureFormat(int handle)
+ public TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1)
{
- var descriptor = GetTextureDescriptor(handle);
+ var descriptor = GetTextureDescriptor(handle, cbufSlot);
if (!FormatTable.TryGetTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb(), out FormatInfo formatInfo))
{
@@ -78,20 +79,22 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Queries texture target information.
/// </summary>
/// <param name="handle">Texture handle</param>
+ /// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
/// <returns>True if the texture is a buffer texture, false otherwise</returns>
- public bool QueryIsTextureBuffer(int handle)
+ public bool QueryIsTextureBuffer(int handle, int cbufSlot = -1)
{
- return GetTextureDescriptor(handle).UnpackTextureTarget() == TextureTarget.TextureBuffer;
+ return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget() == TextureTarget.TextureBuffer;
}
/// <summary>
/// Queries texture target information.
/// </summary>
/// <param name="handle">Texture handle</param>
+ /// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
/// <returns>True if the texture is a rectangle texture, false otherwise</returns>
- public bool QueryIsTextureRectangle(int handle)
+ public bool QueryIsTextureRectangle(int handle, int cbufSlot = -1)
{
- var descriptor = GetTextureDescriptor(handle);
+ var descriptor = GetTextureDescriptor(handle, cbufSlot);
TextureTarget target = descriptor.UnpackTextureTarget();
diff --git a/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs b/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs
index b4f9206b..c402c574 100644
--- a/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs
+++ b/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs
@@ -1,33 +1,80 @@
using FFmpeg.AutoGen;
+using Ryujinx.Common.Logging;
using System;
+using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Nvdec.H264
{
unsafe class FFmpegContext : IDisposable
{
+ private readonly av_log_set_callback_callback _logFunc;
private readonly AVCodec* _codec;
+ private AVPacket* _packet;
private AVCodecContext* _context;
public FFmpegContext()
{
+ _logFunc = Log;
+
+ // Redirect log output
+ ffmpeg.av_log_set_level(ffmpeg.AV_LOG_MAX_OFFSET);
+ ffmpeg.av_log_set_callback(_logFunc);
+
_codec = ffmpeg.avcodec_find_decoder(AVCodecID.AV_CODEC_ID_H264);
_context = ffmpeg.avcodec_alloc_context3(_codec);
ffmpeg.avcodec_open2(_context, _codec, null);
+
+ _packet = ffmpeg.av_packet_alloc();
}
- public int DecodeFrame(Surface output, ReadOnlySpan<byte> bitstream)
+ private void Log(void* p0, int level, string format, byte* vl)
{
- AVPacket packet;
+ if (level > ffmpeg.av_log_get_level())
+ {
+ return;
+ }
+
+ int lineSize = 1024;
+ byte* lineBuffer = stackalloc byte[lineSize];
+ int printPrefix = 1;
+
+ ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix);
+
+ string line = Marshal.PtrToStringAnsi((IntPtr)lineBuffer).Trim();
- ffmpeg.av_init_packet(&packet);
+ switch (level)
+ {
+ case ffmpeg.AV_LOG_PANIC:
+ case ffmpeg.AV_LOG_FATAL:
+ case ffmpeg.AV_LOG_ERROR:
+ Logger.Error?.Print(LogClass.FFmpeg, line);
+ break;
+ case ffmpeg.AV_LOG_WARNING:
+ Logger.Warning?.Print(LogClass.FFmpeg, line);
+ break;
+ case ffmpeg.AV_LOG_INFO:
+ Logger.Info?.Print(LogClass.FFmpeg, line);
+ break;
+ case ffmpeg.AV_LOG_VERBOSE:
+ case ffmpeg.AV_LOG_DEBUG:
+ case ffmpeg.AV_LOG_TRACE:
+ Logger.Debug?.Print(LogClass.FFmpeg, line);
+ break;
+ }
+ }
+
+ public int DecodeFrame(Surface output, ReadOnlySpan<byte> bitstream)
+ {
+ // Ensure the packet is clean before proceeding
+ ffmpeg.av_packet_unref(_packet);
fixed (byte* ptr = bitstream)
{
- packet.data = ptr;
- packet.size = bitstream.Length;
+ _packet->data = ptr;
+ _packet->size = bitstream.Length;
- int rc = ffmpeg.avcodec_send_packet(_context, &packet);
+ int rc = ffmpeg.avcodec_send_packet(_context, _packet);
if (rc != 0)
{
@@ -40,6 +87,11 @@ namespace Ryujinx.Graphics.Nvdec.H264
public void Dispose()
{
+ fixed (AVPacket** ppPacket = &_packet)
+ {
+ ffmpeg.av_packet_free(ppPacket);
+ }
+
ffmpeg.avcodec_close(_context);
fixed (AVCodecContext** ppContext = &_context)
diff --git a/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj b/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj
index 2d54173c..fdcdae06 100644
--- a/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj
+++ b/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj
@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="FFmpeg.AutoGen" Version="4.3.0" />
+ <PackageReference Include="FFmpeg.AutoGen" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs
index e6b70376..76b321b7 100644
--- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs
+++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs
@@ -40,15 +40,7 @@ namespace Ryujinx.Graphics.OpenGL
FramebufferAttachment attachment = FramebufferAttachment.ColorAttachment0 + index;
- if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Amd ||
- HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows)
- {
- GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.GetIncompatibleFormatViewHandle() ?? 0, 0);
- }
- else
- {
- GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.Handle ?? 0, 0);
- }
+ GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.Handle ?? 0, 0);
_colors[index] = color;
}
@@ -92,21 +84,6 @@ namespace Ryujinx.Graphics.OpenGL
}
}
- public void SignalModified()
- {
- if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Amd ||
- HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows)
- {
- for (int i = 0; i < 8; i++)
- {
- if (_colors[i] != null)
- {
- _colors[i].SignalModified();
- }
- }
- }
- }
-
public void SetDualSourceBlend(bool enable)
{
bool oldEnable = _dualSourceBlend;
diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
index 08b0e6af..c994113d 100644
--- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
+++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.OpenGL
public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value;
public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia;
public static bool RequiresSyncFlush => _gpuVendor.Value == GpuVendor.Amd || _gpuVendor.Value == GpuVendor.IntelWindows || _gpuVendor.Value == GpuVendor.IntelUnix;
+ public static bool SupportsMismatchingViewFormat => _gpuVendor.Value != GpuVendor.Amd && _gpuVendor.Value != GpuVendor.IntelWindows;
public static int MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value;
public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value;
diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
index e8c3e3be..8e8d3c78 100644
--- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
@@ -10,8 +10,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
private readonly TextureStorage _parent;
- private TextureView _incompatibleFormatView;
-
public ITextureInfo Storage => _parent;
public int FirstLayer { get; private set; }
@@ -102,35 +100,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
return _parent.CreateView(info, firstLayer, firstLevel);
}
- public int GetIncompatibleFormatViewHandle()
- {
- // AMD and Intel have a bug where the view format is always ignored;
- // they use the parent format instead.
- // As a workaround we create a new texture with the correct
- // format, and then do a copy after the draw.
- if (_parent.Info.Format != Format)
- {
- if (_incompatibleFormatView == null)
- {
- _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info, ScaleFactor);
- }
-
- _renderer.TextureCopy.CopyUnscaled(_parent, _incompatibleFormatView, FirstLayer, 0, FirstLevel, 0);
-
- return _incompatibleFormatView.Handle;
- }
-
- return Handle;
- }
-
- public void SignalModified()
- {
- if (_incompatibleFormatView != null)
- {
- _renderer.TextureCopy.CopyUnscaled(_incompatibleFormatView, _parent, 0, FirstLayer, 0, FirstLevel);
- }
- }
-
public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
{
TextureView destinationView = (TextureView)destination;
@@ -634,13 +603,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
private void DisposeHandles()
{
- if (_incompatibleFormatView != null)
- {
- _incompatibleFormatView.Dispose();
-
- _incompatibleFormatView = null;
- }
-
if (Handle != 0)
{
GL.DeleteTexture(Handle);
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index 28478a72..fcb0fdf1 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -110,8 +110,6 @@ namespace Ryujinx.Graphics.OpenGL
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
RestoreComponentMask(index);
-
- _framebuffer.SignalModified();
}
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
@@ -154,8 +152,6 @@ namespace Ryujinx.Graphics.OpenGL
{
GL.DepthMask(_depthMask);
}
-
- _framebuffer.SignalModified();
}
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@@ -1224,8 +1220,6 @@ namespace Ryujinx.Graphics.OpenGL
private void PostDraw()
{
- _framebuffer?.SignalModified();
-
if (_tfEnabled)
{
for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs
index 0382ba86..7416cdd7 100644
--- a/Ryujinx.Graphics.OpenGL/Renderer.cs
+++ b/Ryujinx.Graphics.OpenGL/Renderer.cs
@@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.OpenGL
HwCapabilities.SupportsAstcCompression,
HwCapabilities.SupportsImageLoadFormatted,
HwCapabilities.SupportsNonConstantTextureOffset,
+ HwCapabilities.SupportsMismatchingViewFormat,
HwCapabilities.SupportsViewportSwizzle,
HwCapabilities.MaximumComputeSharedMemorySize,
HwCapabilities.MaximumSupportedAnisotropy,
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
index 622ac646..0ea7f151 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
@@ -61,8 +61,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
switch (memRegion)
{
- case Instruction.MrShared: args += LoadShared (context, operation); break;
- case Instruction.MrStorage: args += LoadStorage(context, operation); break;
+ case Instruction.MrShared: args += LoadShared(context, operation); break;
+ case Instruction.MrStorage: args += LoadStorage(context, operation, forAtomic: true); break;
default: throw new InvalidOperationException($"Invalid memory region \"{memRegion}\".");
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index fc5b06b1..273aaef8 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return $"{arrayName}[{offsetExpr}]";
}
- public static string LoadStorage(CodeGenContext context, AstOperation operation)
+ public static string LoadStorage(CodeGenContext context, AstOperation operation, bool forAtomic = false)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
@@ -213,6 +213,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
+ if (forAtomic)
+ {
+ SetStorageWriteFlag(context, src1, context.Config.Stage);
+ }
+
return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
}
@@ -485,7 +490,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
flags |= TextureUsageFlags.ResScaleUnsupported;
}
- }
+ }
else
{
// Resolution scaling cannot be applied to this texture right now.
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index fe034c57..1e05cdcd 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -44,12 +44,12 @@
return 0xffff;
}
- bool QueryIsTextureBuffer(int handle)
+ bool QueryIsTextureBuffer(int handle, int cbufSlot = -1)
{
return false;
}
- bool QueryIsTextureRectangle(int handle)
+ bool QueryIsTextureRectangle(int handle, int cbufSlot = -1)
{
return false;
}
@@ -74,7 +74,7 @@
return true;
}
- TextureFormat QueryTextureFormat(int handle)
+ TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1)
{
return TextureFormat.R8G8B8A8Unorm;
}
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
index 81d5c7af..7afdbf4e 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
@@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand dest = Attribute(op.AttributeOffset + index * 4);
+ context.FlagAttributeWritten(dest.Value);
+
context.Copy(dest, Register(rd));
}
}
diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
index 2324fac2..9329442f 100644
--- a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
@@ -11,13 +11,15 @@ namespace Ryujinx.Graphics.Shader
public ReadOnlyCollection<TextureDescriptor> Images { get; }
public bool UsesInstanceId { get; }
+ public byte ClipDistancesWritten { get; }
public ShaderProgramInfo(
BufferDescriptor[] cBuffers,
BufferDescriptor[] sBuffers,
TextureDescriptor[] textures,
TextureDescriptor[] images,
- bool usesInstanceId)
+ bool usesInstanceId,
+ byte clipDistancesWritten)
{
CBuffers = Array.AsReadOnly(cBuffers);
SBuffers = Array.AsReadOnly(sBuffers);
@@ -25,6 +27,7 @@ namespace Ryujinx.Graphics.Shader
Images = Array.AsReadOnly(images);
UsesInstanceId = usesInstanceId;
+ ClipDistancesWritten = clipDistancesWritten;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
index 2667be1d..c4d8b85f 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
@@ -291,10 +291,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
Info.IAttributes.Add(attrIndex);
}
- else if (operand.Type == OperandType.Attribute && operand.Value == AttributeConsts.InstanceId)
- {
- Info.UsesInstanceId = true;
- }
else if (operand.Type == OperandType.ConstantBuffer)
{
Info.CBuffers.Add(operand.GetCbufSlot());
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
index 16a27f51..d1619bfa 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
@@ -12,7 +12,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public HashSet<int> IAttributes { get; }
public HashSet<int> OAttributes { get; }
- public bool UsesInstanceId { get; set; }
public bool UsesCbIndexing { get; set; }
public HelperFunctionsMask HelperFunctionsMask { get; set; }
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
index a4c21c1d..9b220177 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
@@ -55,7 +55,11 @@ namespace Ryujinx.Graphics.Shader.Translation
public void FlagAttributeRead(int attribute)
{
- if (Config.Stage == ShaderStage.Fragment)
+ if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId)
+ {
+ Config.SetUsedFeature(FeatureFlags.InstanceId);
+ }
+ else if (Config.Stage == ShaderStage.Fragment)
{
switch (attribute)
{
@@ -67,6 +71,26 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
+ public void FlagAttributeWritten(int attribute)
+ {
+ if (Config.Stage == ShaderStage.Vertex)
+ {
+ switch (attribute)
+ {
+ case AttributeConsts.ClipDistance0:
+ case AttributeConsts.ClipDistance1:
+ case AttributeConsts.ClipDistance2:
+ case AttributeConsts.ClipDistance3:
+ case AttributeConsts.ClipDistance4:
+ case AttributeConsts.ClipDistance5:
+ case AttributeConsts.ClipDistance6:
+ case AttributeConsts.ClipDistance7:
+ Config.SetClipDistanceWritten((attribute - AttributeConsts.ClipDistance0) / 4);
+ break;
+ }
+ }
+ }
+
public void MarkLabel(Operand label)
{
Add(Instruction.MarkLabel, label);
diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
index d2b53f84..1b712896 100644
--- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
+++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
@@ -12,9 +12,11 @@ namespace Ryujinx.Graphics.Shader.Translation
None = 0,
// Affected by resolution scaling.
- FragCoordXY = 1 << 1,
IntegerSampling = 1 << 0,
+ FragCoordXY = 1 << 1,
+
+ Bindless = 1 << 2,
- Bindless = 1 << 2,
+ InstanceId = 1 << 3
}
}
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
index f462cedb..f91a00eb 100644
--- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
@@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (src0.Type == OperandType.ConstantBuffer)
{
texOp.SetHandle(src0.GetCbufOffset(), src0.GetCbufSlot());
- texOp.Format = config.GetTextureFormat(texOp.Handle);
+ texOp.Format = config.GetTextureFormat(texOp.Handle, texOp.CbufSlot);
}
}
}
diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
index a928f935..5427c013 100644
--- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
@@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.Shader.Translation
bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QuerySupportsNonConstantTextureOffset();
- bool isRect = config.GpuAccessor.QueryIsTextureRectangle(texOp.Handle);
+ bool isRect = config.GpuAccessor.QueryIsTextureRectangle(texOp.Handle, texOp.CbufSlot);
if (!(hasInvalidOffset || isRect))
{
@@ -433,7 +433,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
TextureOperation texOp = (TextureOperation)node.Value;
- TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle);
+ TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle, texOp.CbufSlot);
int maxPositive = format switch
{
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index b1fd6470..077ce70d 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Shader.Translation
public int Size { get; private set; }
+ public byte ClipDistancesWritten { get; private set; }
+
public FeatureFlags UsedFeatures { get; private set; }
public HashSet<int> TextureHandlesForCache { get; }
@@ -89,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return count + 1;
}
- public TextureFormat GetTextureFormat(int handle)
+ public TextureFormat GetTextureFormat(int handle, int cbufSlot = -1)
{
// When the formatted load extension is supported, we don't need to
// specify a format, we can just declare it without a format and the GPU will handle it.
@@ -98,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return TextureFormat.Unknown;
}
- var format = GpuAccessor.QueryTextureFormat(handle);
+ var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot);
if (format == TextureFormat.Unknown)
{
@@ -115,6 +117,11 @@ namespace Ryujinx.Graphics.Shader.Translation
Size += size;
}
+ public void SetClipDistanceWritten(int index)
+ {
+ ClipDistancesWritten |= (byte)(1 << index);
+ }
+
public void SetUsedFeature(FeatureFlags flags)
{
UsedFeatures |= flags;
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index 9f0f9010..1c15ccf2 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -94,7 +94,8 @@ namespace Ryujinx.Graphics.Shader.Translation
program.SBufferDescriptors,
program.TextureDescriptors,
program.ImageDescriptors,
- sInfo.UsesInstanceId);
+ config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
+ config.ClipDistancesWritten);
string glslCode = program.Code;
diff --git a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
index 1630835d..a18838ab 100644
--- a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
+++ b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
@@ -24,8 +24,8 @@ namespace Ryujinx.HLE.FileSystem.Content
private Dictionary<StorageId, LinkedList<LocationEntry>> _locationEntries;
- private Dictionary<string, long> _sharedFontTitleDictionary;
- private Dictionary<long, string> _systemTitlesNameDictionary;
+ private Dictionary<string, ulong> _sharedFontTitleDictionary;
+ private Dictionary<ulong, string> _systemTitlesNameDictionary;
private Dictionary<string, string> _sharedFontFilenameDictionary;
private SortedDictionary<(ulong titleId, NcaContentType type), string> _contentDictionary;
@@ -55,7 +55,7 @@ namespace Ryujinx.HLE.FileSystem.Content
_contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>();
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
- _sharedFontTitleDictionary = new Dictionary<string, long>
+ _sharedFontTitleDictionary = new Dictionary<string, ulong>
{
{ "FontStandard", 0x0100000000000811 },
{ "FontChineseSimplified", 0x0100000000000814 },
@@ -65,7 +65,7 @@ namespace Ryujinx.HLE.FileSystem.Content
{ "FontNintendoExtended", 0x0100000000000810 }
};
- _systemTitlesNameDictionary = new Dictionary<long, string>()
+ _systemTitlesNameDictionary = new Dictionary<ulong, string>()
{
{ 0x010000000000080E, "TimeZoneBinary" },
{ 0x0100000000000810, "FontNintendoExtension" },
@@ -140,7 +140,7 @@ namespace Ryujinx.HLE.FileSystem.Content
LocationEntry entry = new LocationEntry(switchPath,
0,
- (long)nca.Header.TitleId,
+ nca.Header.TitleId,
nca.Header.ContentType);
AddEntry(entry);
@@ -167,7 +167,7 @@ namespace Ryujinx.HLE.FileSystem.Content
LocationEntry entry = new LocationEntry(switchPath,
0,
- (long)nca.Header.TitleId,
+ nca.Header.TitleId,
nca.Header.ContentType);
AddEntry(entry);
@@ -197,7 +197,7 @@ namespace Ryujinx.HLE.FileSystem.Content
}
// fs must contain AOC nca files in its root
- public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId)
+ public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId, IntegrityCheckLevel integrityCheckLevel)
{
_virtualFileSystem.ImportTickets(fs);
@@ -214,7 +214,7 @@ namespace Ryujinx.HLE.FileSystem.Content
continue;
}
- using var pfs0 = nca.OpenFileSystem(0, Switch.GetIntegrityCheckLevel());
+ using var pfs0 = nca.OpenFileSystem(0, integrityCheckLevel);
pfs0.OpenFile(out IFile cnmtFile, pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read);
@@ -265,7 +265,7 @@ namespace Ryujinx.HLE.FileSystem.Content
public IList<ulong> GetAocTitleIds() => _aocData.Where(e => e.Value.Enabled).Select(e => e.Key).ToList();
- public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage)
+ public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage, IntegrityCheckLevel integrityCheckLevel)
{
aocStorage = null;
@@ -289,7 +289,7 @@ namespace Ryujinx.HLE.FileSystem.Content
return false; // Print error?
}
- aocStorage = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()).OpenStorage(NcaSectionType.Data, Switch.GetIntegrityCheckLevel());
+ aocStorage = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()).OpenStorage(NcaSectionType.Data, integrityCheckLevel);
return true;
}
@@ -297,7 +297,7 @@ namespace Ryujinx.HLE.FileSystem.Content
return false;
}
- public void ClearEntry(long titleId, NcaContentType contentType, StorageId storageId)
+ public void ClearEntry(ulong titleId, NcaContentType contentType, StorageId storageId)
{
lock (_lock)
{
@@ -333,7 +333,7 @@ namespace Ryujinx.HLE.FileSystem.Content
if (_contentDictionary.ContainsValue(ncaId))
{
var content = _contentDictionary.FirstOrDefault(x => x.Value == ncaId);
- long titleId = (long)content.Key.Item1;
+ ulong titleId = content.Key.Item1;
NcaContentType contentType = content.Key.type;
StorageId storage = GetInstalledStorage(titleId, contentType, storageId);
@@ -345,20 +345,20 @@ namespace Ryujinx.HLE.FileSystem.Content
return false;
}
- public UInt128 GetInstalledNcaId(long titleId, NcaContentType contentType)
+ public UInt128 GetInstalledNcaId(ulong titleId, NcaContentType contentType)
{
lock (_lock)
{
- if (_contentDictionary.ContainsKey(((ulong)titleId, contentType)))
+ if (_contentDictionary.ContainsKey((titleId, contentType)))
{
- return new UInt128(_contentDictionary[((ulong)titleId, contentType)]);
+ return new UInt128(_contentDictionary[(titleId, contentType)]);
}
}
return new UInt128();
}
- public StorageId GetInstalledStorage(long titleId, NcaContentType contentType, StorageId storageId)
+ public StorageId GetInstalledStorage(ulong titleId, NcaContentType contentType, StorageId storageId)
{
lock (_lock)
{
@@ -369,7 +369,7 @@ namespace Ryujinx.HLE.FileSystem.Content
}
}
- public string GetInstalledContentPath(long titleId, StorageId storageId, NcaContentType contentType)
+ public string GetInstalledContentPath(ulong titleId, StorageId storageId, NcaContentType contentType)
{
lock (_lock)
{
@@ -445,7 +445,7 @@ namespace Ryujinx.HLE.FileSystem.Content
}
}
- private void RemoveLocationEntry(long titleId, NcaContentType contentType, StorageId storageId)
+ private void RemoveLocationEntry(ulong titleId, NcaContentType contentType, StorageId storageId)
{
LinkedList<LocationEntry> locationList = null;
@@ -466,7 +466,7 @@ namespace Ryujinx.HLE.FileSystem.Content
}
}
- public bool TryGetFontTitle(string fontName, out long titleId)
+ public bool TryGetFontTitle(string fontName, out ulong titleId)
{
return _sharedFontTitleDictionary.TryGetValue(fontName, out titleId);
}
@@ -476,12 +476,12 @@ namespace Ryujinx.HLE.FileSystem.Content
return _sharedFontFilenameDictionary.TryGetValue(fontName, out filename);
}
- public bool TryGetSystemTitlesName(long titleId, out string name)
+ public bool TryGetSystemTitlesName(ulong titleId, out string name)
{
return _systemTitlesNameDictionary.TryGetValue(titleId, out name);
}
- private LocationEntry GetLocation(long titleId, NcaContentType contentType, StorageId storageId)
+ private LocationEntry GetLocation(ulong titleId, NcaContentType contentType, StorageId storageId)
{
LinkedList<LocationEntry> locationList = _locationEntries[storageId];
@@ -710,8 +710,6 @@ namespace Ryujinx.HLE.FileSystem.Content
SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
{
- IntegrityCheckLevel integrityCheckLevel = Switch.GetIntegrityCheckLevel();
-
SystemVersion systemVersion = null;
foreach (var entry in archive.Entries)
@@ -751,7 +749,7 @@ namespace Ryujinx.HLE.FileSystem.Content
{
Nca metaNca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage());
- IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel);
+ IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
@@ -781,7 +779,7 @@ namespace Ryujinx.HLE.FileSystem.Content
{
Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage());
- var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel);
+ var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
if (romfs.OpenFile(out IFile systemVersionFile, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
@@ -816,7 +814,7 @@ namespace Ryujinx.HLE.FileSystem.Content
{
Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaNcaStream.AsStorage());
- IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel);
+ IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
@@ -873,8 +871,6 @@ namespace Ryujinx.HLE.FileSystem.Content
SystemVersion VerifyAndGetVersion(IFileSystem filesystem)
{
- IntegrityCheckLevel integrityCheckLevel = Switch.GetIntegrityCheckLevel();
-
SystemVersion systemVersion = null;
CnmtContentMetaEntry[] metaEntries = null;
@@ -887,7 +883,7 @@ namespace Ryujinx.HLE.FileSystem.Content
if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta)
{
- IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel);
+ IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
@@ -905,7 +901,7 @@ namespace Ryujinx.HLE.FileSystem.Content
}
else if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data)
{
- var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel);
+ var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
if (romfs.OpenFile(out IFile systemVersionFile, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
@@ -952,7 +948,7 @@ namespace Ryujinx.HLE.FileSystem.Content
Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaStorage);
- IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel);
+ IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
@@ -1004,8 +1000,6 @@ namespace Ryujinx.HLE.FileSystem.Content
public SystemVersion GetCurrentFirmwareVersion()
{
- IntegrityCheckLevel integrityCheckLevel = Switch.GetIntegrityCheckLevel();
-
LoadEntries();
lock (_lock)
@@ -1024,7 +1018,7 @@ namespace Ryujinx.HLE.FileSystem.Content
if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data)
{
- var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel);
+ var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
if (romfs.OpenFile(out IFile systemVersionFile, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
diff --git a/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs
index 15485148..cc259c3a 100644
--- a/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs
+++ b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs
@@ -6,10 +6,10 @@ namespace Ryujinx.HLE.FileSystem.Content
{
public string ContentPath { get; private set; }
public int Flag { get; private set; }
- public long TitleId { get; private set; }
+ public ulong TitleId { get; private set; }
public NcaContentType ContentType { get; private set; }
- public LocationEntry(string contentPath, int flag, long titleId, NcaContentType contentType)
+ public LocationEntry(string contentPath, int flag, ulong titleId, NcaContentType contentType)
{
ContentPath = contentPath;
Flag = flag;
diff --git a/Ryujinx.HLE/HLEConfiguration.cs b/Ryujinx.HLE/HLEConfiguration.cs
new file mode 100644
index 00000000..00c79169
--- /dev/null
+++ b/Ryujinx.HLE/HLEConfiguration.cs
@@ -0,0 +1,179 @@
+using LibHac.FsSystem;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.FileSystem.Content;
+using Ryujinx.HLE.HOS;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.HLE.HOS.SystemState;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE
+{
+ /// <summary>
+ /// HLE configuration.
+ /// </summary>
+ public class HLEConfiguration
+ {
+ /// <summary>
+ /// The virtual file system used by the FS service.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly VirtualFileSystem VirtualFileSystem;
+
+ /// <summary>
+ /// The account manager used by the account service.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly AccountManager AccountManager;
+
+ /// <summary>
+ /// The content manager used by the NCM service.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly ContentManager ContentManager;
+
+ /// <summary>
+ /// The persistant information between run for multi-application capabilities.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ public readonly UserChannelPersistence UserChannelPersistence;
+
+ /// <summary>
+ /// The GPU renderer to use for all GPU operations.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly IRenderer GpuRenderer;
+
+ /// <summary>
+ /// The audio device driver to use for all audio operations.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly IHardwareDeviceDriver AudioDeviceDriver;
+
+ /// <summary>
+ /// The handler for various UI related operations needed outside of HLE.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly IHostUiHandler HostUiHandler;
+
+ /// <summary>
+ /// Control the memory configuration used by the emulation context.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly MemoryConfiguration MemoryConfiguration;
+
+ /// <summary>
+ /// The system language to use in the settings service.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly SystemLanguage SystemLanguage;
+
+ /// <summary>
+ /// The system region to use in the settings service.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly RegionCode Region;
+
+ /// <summary>
+ /// Control the initial state of the vertical sync in the SurfaceFlinger service.
+ /// </summary>
+ internal readonly bool EnableVsync;
+
+ /// <summary>
+ /// Control the initial state of the docked mode.
+ /// </summary>
+ internal readonly bool EnableDockedMode;
+
+ /// <summary>
+ /// Control if the Profiled Translation Cache (PTC) should be used.
+ /// </summary>
+ internal readonly bool EnablePtc;
+
+ /// <summary>
+ /// Control LibHac's integrity check level.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly IntegrityCheckLevel FsIntegrityCheckLevel;
+
+ /// <summary>
+ /// Control LibHac's global access logging level. Value must be between 0 and 3.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly int FsGlobalAccessLogMode;
+
+ /// <summary>
+ /// The system time offset to apply to the time service steady and local clocks.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly long SystemTimeOffset;
+
+ /// <summary>
+ /// The system timezone used by the time service.
+ /// </summary>
+ /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
+ internal readonly string TimeZone;
+
+ /// <summary>
+ /// Control the inital state of the ignore missing services setting.
+ /// If this is set to true, when a missing service is encountered, it will try to automatically handle it instead of throwing an exception.
+ /// </summary>
+ /// TODO: Update this again.
+ public bool IgnoreMissingServices { internal get; set; }
+
+ /// <summary>
+ /// Aspect Ratio applied to the renderer window by the SurfaceFlinger service.
+ /// </summary>
+ public AspectRatio AspectRatio { internal get; set; }
+
+ /// <summary>
+ /// An action called when HLE force a refresh of output after docked mode changed.
+ /// </summary>
+ public Action RefreshInputConfig { internal get; set; }
+
+ public HLEConfiguration(VirtualFileSystem virtualFileSystem,
+ ContentManager contentManager,
+ AccountManager accountManager,
+ UserChannelPersistence userChannelPersistence,
+ IRenderer gpuRenderer,
+ IHardwareDeviceDriver audioDeviceDriver,
+ MemoryConfiguration memoryConfiguration,
+ IHostUiHandler hostUiHandler,
+ SystemLanguage systemLanguage,
+ RegionCode region,
+ bool enableVsync,
+ bool enableDockedMode,
+ bool enablePtc,
+ IntegrityCheckLevel fsIntegrityCheckLevel,
+ int fsGlobalAccessLogMode,
+ long systemTimeOffset,
+ string timeZone,
+ bool ignoreMissingServices,
+ AspectRatio aspectRatio)
+ {
+ VirtualFileSystem = virtualFileSystem;
+ AccountManager = accountManager;
+ ContentManager = contentManager;
+ UserChannelPersistence = userChannelPersistence;
+ GpuRenderer = gpuRenderer;
+ AudioDeviceDriver = audioDeviceDriver;
+ MemoryConfiguration = memoryConfiguration;
+ HostUiHandler = hostUiHandler;
+ SystemLanguage = systemLanguage;
+ Region = region;
+ EnableVsync = enableVsync;
+ EnableDockedMode = enableDockedMode;
+ EnablePtc = enablePtc;
+ FsIntegrityCheckLevel = fsIntegrityCheckLevel;
+ FsGlobalAccessLogMode = fsGlobalAccessLogMode;
+ SystemTimeOffset = systemTimeOffset;
+ TimeZone = timeZone;
+ IgnoreMissingServices = ignoreMissingServices;
+ AspectRatio = aspectRatio;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
index fd89e8f6..0597cf9b 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
@@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
+using Ryujinx.HLE.HOS.Services.Hid.Types;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using static Ryujinx.HLE.HOS.Services.Hid.HidServer.HidUtils;
diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs
index 40e3f646..3832dd3e 100644
--- a/Ryujinx.HLE/HOS/ApplicationLoader.cs
+++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs
@@ -10,7 +10,6 @@ using LibHac.Ns;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Npdm;
@@ -49,10 +48,7 @@ namespace Ryujinx.HLE.HOS
"sdk"
};
- private readonly Switch _device;
- private readonly ContentManager _contentManager;
- private readonly VirtualFileSystem _fileSystem;
-
+ private readonly Switch _device;
private string _titleName;
private string _displayVersion;
private BlitStruct<ApplicationControlProperty> _controlData;
@@ -66,12 +62,9 @@ namespace Ryujinx.HLE.HOS
public string TitleIdText => TitleId.ToString("x16");
- public ApplicationLoader(Switch device, VirtualFileSystem fileSystem, ContentManager contentManager)
+ public ApplicationLoader(Switch device)
{
- _device = device;
- _contentManager = contentManager;
- _fileSystem = fileSystem;
-
+ _device = device;
_controlData = new BlitStruct<ApplicationControlProperty>(1);
}
@@ -79,14 +72,14 @@ namespace Ryujinx.HLE.HOS
{
if (romFsFile != null)
{
- _fileSystem.LoadRomFs(romFsFile);
+ _device.Configuration.VirtualFileSystem.LoadRomFs(romFsFile);
}
LocalFileSystem codeFs = new LocalFileSystem(exeFsDir);
Npdm metaData = ReadNpdm(codeFs);
- _fileSystem.ModLoader.CollectMods(new[] { TitleId }, _fileSystem.ModLoader.GetModsBasePath());
+ _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(new[] { TitleId }, _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath());
if (TitleId != 0)
{
@@ -209,7 +202,7 @@ namespace Ryujinx.HLE.HOS
public void LoadXci(string xciFile)
{
FileStream file = new FileStream(xciFile, FileMode.Open, FileAccess.Read);
- Xci xci = new Xci(_fileSystem.KeySet, file.AsStorage());
+ Xci xci = new Xci(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage());
if (!xci.HasPartition(XciPartitionType.Secure))
{
@@ -226,7 +219,7 @@ namespace Ryujinx.HLE.HOS
try
{
- (mainNca, patchNca, controlNca) = GetGameData(_fileSystem, securePartition, _device.UserChannelPersistence.Index);
+ (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, securePartition, _device.Configuration.UserChannelPersistence.Index);
}
catch (Exception e)
{
@@ -242,9 +235,9 @@ namespace Ryujinx.HLE.HOS
return;
}
- _contentManager.LoadEntries(_device);
- _contentManager.ClearAocData();
- _contentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId);
+ _device.Configuration.ContentManager.LoadEntries(_device);
+ _device.Configuration.ContentManager.ClearAocData();
+ _device.Configuration.ContentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel);
LoadNca(mainNca, patchNca, controlNca);
}
@@ -260,7 +253,7 @@ namespace Ryujinx.HLE.HOS
try
{
- (mainNca, patchNca, controlNca) = GetGameData(_fileSystem, nsp, _device.UserChannelPersistence.Index);
+ (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, nsp, _device.Configuration.UserChannelPersistence.Index);
}
catch (Exception e)
{
@@ -278,8 +271,8 @@ namespace Ryujinx.HLE.HOS
if (mainNca != null)
{
- _contentManager.ClearAocData();
- _contentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId);
+ _device.Configuration.ContentManager.ClearAocData();
+ _device.Configuration.ContentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel);
LoadNca(mainNca, patchNca, controlNca);
@@ -293,7 +286,7 @@ namespace Ryujinx.HLE.HOS
public void LoadNca(string ncaFile)
{
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
- Nca nca = new Nca(_fileSystem.KeySet, file.AsStorage(false));
+ Nca nca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
LoadNca(nca, null, null);
}
@@ -310,7 +303,7 @@ namespace Ryujinx.HLE.HOS
IStorage dataStorage = null;
IFileSystem codeFs = null;
- (Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_fileSystem, mainNca.Header.TitleId.ToString("x16"), _device.UserChannelPersistence.Index, out _);
+ (Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), _device.Configuration.UserChannelPersistence.Index, out _);
if (updatePatchNca != null)
{
@@ -323,7 +316,7 @@ namespace Ryujinx.HLE.HOS
}
// Load program 0 control NCA as we are going to need it for display version.
- (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_fileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
+ (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
// Load Aoc
string titleAocMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
@@ -336,7 +329,7 @@ namespace Ryujinx.HLE.HOS
{
foreach (DlcNca dlcNca in dlcContainer.DlcNcaList)
{
- _contentManager.AddAocItem(dlcNca.TitleId, dlcContainer.Path, dlcNca.Path, dlcNca.Enabled);
+ _device.Configuration.ContentManager.AddAocItem(dlcNca.TitleId, dlcContainer.Path, dlcNca.Path, dlcNca.Enabled);
}
}
}
@@ -375,7 +368,7 @@ namespace Ryujinx.HLE.HOS
Npdm metaData = ReadNpdm(codeFs);
- _fileSystem.ModLoader.CollectMods(_contentManager.GetAocTitleIds().Prepend(TitleId), _fileSystem.ModLoader.GetModsBasePath());
+ _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(_device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId), _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath());
if (controlNca != null)
{
@@ -388,7 +381,7 @@ namespace Ryujinx.HLE.HOS
// NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application.
// BODY: As such, to avoid PTC cache confusion, we only trust the the program 0 display version when launching a sub program.
- if (updateProgram0ControlNca != null && _device.UserChannelPersistence.Index != 0)
+ if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0)
{
string dummyTitleName = "";
BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1);
@@ -402,9 +395,9 @@ namespace Ryujinx.HLE.HOS
}
else
{
- IStorage newStorage = _fileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage);
+ IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage);
- _fileSystem.SetRomFs(newStorage.AsStream(FileAccess.Read));
+ _device.Configuration.VirtualFileSystem.SetRomFs(newStorage.AsStream(FileAccess.Read));
}
if (TitleId != 0)
@@ -470,7 +463,7 @@ namespace Ryujinx.HLE.HOS
private void LoadExeFs(IFileSystem codeFs, Npdm metaData = null)
{
- if (_fileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
+ if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
{
metaData = null; //TODO: Check if we should retain old npdm
}
@@ -496,7 +489,7 @@ namespace Ryujinx.HLE.HOS
}
// ExeFs file replacements
- ModLoadResult modLoadResult = _fileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
+ ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
// collect the nsos, ignoring ones that aren't used
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
@@ -507,20 +500,18 @@ namespace Ryujinx.HLE.HOS
metaData = modLoadResult.Npdm;
}
- bool hasPatches = _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs);
+ _device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(TitleId, programs);
- _contentManager.LoadEntries(_device);
+ _device.Configuration.ContentManager.LoadEntries(_device);
bool usePtc = _device.System.EnablePtc;
- // don't use PTC if exefs files have been replaced
+ // Don't use PPTC if ExeFs files have been replaced.
usePtc &= !modLoadResult.Modified;
- // don't use PTC if exefs files have been patched
- usePtc &= !hasPatches;
if (_device.System.EnablePtc && !usePtc)
{
- Logger.Warning?.Print(LogClass.Ptc, $"Detected exefs modifications. PPTC disabled.");
+ Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PPTC disabled.");
}
Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;
@@ -530,7 +521,7 @@ namespace Ryujinx.HLE.HOS
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, executables: programs);
- _fileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
+ _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
}
public void LoadProgram(string filePath)
@@ -571,7 +562,7 @@ namespace Ryujinx.HLE.HOS
if (romfsSize != 0)
{
- _fileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset));
+ _device.Configuration.VirtualFileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset));
}
if (nacpSize != 0)
@@ -619,7 +610,7 @@ namespace Ryujinx.HLE.HOS
executable = new NsoExecutable(new LocalStorage(filePath, FileAccess.Read), Path.GetFileNameWithoutExtension(filePath));
}
- _contentManager.LoadEntries(_device);
+ _device.Configuration.ContentManager.LoadEntries(_device);
_titleName = metaData.TitleName;
TitleId = metaData.Aci0.TitleId;
@@ -631,7 +622,7 @@ namespace Ryujinx.HLE.HOS
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, executables: executable);
- _fileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
+ _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
}
private Npdm GetDefaultNpdm()
@@ -666,7 +657,7 @@ namespace Ryujinx.HLE.HOS
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
}
- FileSystemClient fileSystem = _fileSystem.FsClient;
+ FileSystemClient fileSystem = _device.Configuration.VirtualFileSystem.FsClient;
Result resultCode = fileSystem.EnsureApplicationCacheStorage(out _, applicationId, ref control);
if (resultCode.IsFailure())
diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
index ac96b865..d5763ff8 100644
--- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
@@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Font
FontInfo CreateFont(string name)
{
- if (contentManager.TryGetFontTitle(name, out long fontTitle) &&
+ if (contentManager.TryGetFontTitle(name, out ulong fontTitle) &&
contentManager.TryGetFontFilename(name, out string fontFilename))
{
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, NcaContentType.Data);
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index fa88d775..13d7a2af 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -8,8 +8,6 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Output;
using Ryujinx.Audio.Renderer.Device;
using Ryujinx.Audio.Renderer.Server;
-using Ryujinx.Common;
-using Ryujinx.Configuration;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS.Font;
using Ryujinx.HLE.HOS.Kernel;
@@ -111,13 +109,13 @@ namespace Ryujinx.HLE.HOS
internal LibHac.Horizon LibHacHorizonServer { get; private set; }
internal HorizonClient LibHacHorizonClient { get; private set; }
- public Horizon(Switch device, ContentManager contentManager, AccountManager accountManager, MemoryConfiguration memoryConfiguration)
+ public Horizon(Switch device)
{
KernelContext = new KernelContext(
device,
device.Memory,
- memoryConfiguration.ToKernelMemorySize(),
- memoryConfiguration.ToKernelMemoryArrange());
+ device.Configuration.MemoryConfiguration.ToKernelMemorySize(),
+ device.Configuration.MemoryConfiguration.ToKernelMemoryArrange());
Device = device;
@@ -166,8 +164,8 @@ namespace Ryujinx.HLE.HOS
DisplayResolutionChangeEvent = new KEvent(KernelContext);
- AccountManager = accountManager;
- ContentManager = contentManager;
+ AccountManager = device.Configuration.AccountManager;
+ ContentManager = device.Configuration.ContentManager;
CaptureManager = new CaptureManager(device);
// TODO: use set:sys (and get external clock source id from settings)
@@ -179,7 +177,7 @@ namespace Ryujinx.HLE.HOS
TimeSpanType systemTime = TimeSpanType.FromSeconds((long)rtcValue);
// Configure and setup internal offset
- TimeSpanType internalOffset = TimeSpanType.FromSeconds(ConfigurationState.Instance.System.SystemTimeOffset);
+ TimeSpanType internalOffset = TimeSpanType.FromSeconds(device.Configuration.SystemTimeOffset);
TimeSpanType systemTimeOffset = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds);
@@ -219,8 +217,6 @@ namespace Ryujinx.HLE.HOS
SurfaceFlinger = new SurfaceFlinger(device);
- ConfigurationState.Instance.System.EnableDockedMode.Event += OnDockedModeChange;
-
InitLibHacHorizon();
InitializeAudioRenderer();
}
@@ -274,6 +270,7 @@ namespace Ryujinx.HLE.HOS
public void InitializeServices()
{
IUserInterface sm = new IUserInterface(KernelContext);
+ sm.TrySetServer(new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext)));
// Wait until SM server thread is done with initialization,
// only then doing connections to SM is safe.
@@ -312,11 +309,11 @@ namespace Ryujinx.HLE.HOS
LibHacHorizonClient = ryujinxClient;
}
- private void OnDockedModeChange(object sender, ReactiveEventArgs<bool> e)
+ public void ChangeDockedModeState(bool newState)
{
- if (e.NewValue != State.DockedMode)
+ if (newState != State.DockedMode)
{
- State.DockedMode = e.NewValue;
+ State.DockedMode = newState;
PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
AppletState.Messages.Enqueue(MessageInfo.OperationModeChanged);
@@ -325,8 +322,7 @@ namespace Ryujinx.HLE.HOS
SignalDisplayResolutionChange();
- // Reconfigure controllers
- Device.Hid.RefreshInputConfig(ConfigurationState.Instance.Hid.InputConfig.Value);
+ Device.Configuration.RefreshInputConfig?.Invoke();
}
}
@@ -387,8 +383,6 @@ namespace Ryujinx.HLE.HOS
{
if (!_isDisposed && disposing)
{
- ConfigurationState.Instance.System.EnableDockedMode.Event -= OnDockedModeChange;
-
_isDisposed = true;
KProcess terminationProcess = new KProcess(KernelContext);
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs
index dddd2671..b61d5697 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs
@@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Ipc
{
struct IpcBuffDesc
{
- public long Position { get; private set; }
- public long Size { get; private set; }
- public int Flags { get; private set; }
+ public ulong Position { get; private set; }
+ public ulong Size { get; private set; }
+ public byte Flags { get; private set; }
public IpcBuffDesc(BinaryReader reader)
{
- long word0 = reader.ReadUInt32();
- long word1 = reader.ReadUInt32();
- long word2 = reader.ReadUInt32();
+ ulong word0 = reader.ReadUInt32();
+ ulong word1 = reader.ReadUInt32();
+ ulong word2 = reader.ReadUInt32();
Position = word1;
Position |= (word2 << 4) & 0x0f00000000;
@@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Ipc
Size = word0;
Size |= (word2 << 8) & 0xf00000000;
- Flags = (int)word2 & 3;
+ Flags = (byte)(word2 & 3);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
index 9fe3ae60..e5b9bf04 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
namespace Ryujinx.HLE.HOS.Ipc
@@ -185,6 +186,53 @@ namespace Ryujinx.HLE.HOS.Ipc
}
}
+ public byte[] GetBytesTipc()
+ {
+ Debug.Assert(PtrBuff.Count == 0);
+
+ using (MemoryStream ms = new MemoryStream())
+ {
+ BinaryWriter writer = new BinaryWriter(ms);
+
+ int word0;
+ int word1;
+
+ word0 = (int)Type;
+ word0 |= (SendBuff.Count & 0xf) << 20;
+ word0 |= (ReceiveBuff.Count & 0xf) << 24;
+ word0 |= (ExchangeBuff.Count & 0xf) << 28;
+
+ byte[] handleData = new byte[0];
+
+ if (HandleDesc != null)
+ {
+ handleData = HandleDesc.GetBytes();
+ }
+
+ int dataLength = RawData?.Length ?? 0;
+
+ dataLength = ((dataLength + 3) & ~3) / 4;
+
+ word1 = (dataLength & 0x3ff);
+
+ if (HandleDesc != null)
+ {
+ word1 |= 1 << 31;
+ }
+
+ writer.Write(word0);
+ writer.Write(word1);
+ writer.Write(handleData);
+
+ if (RawData != null)
+ {
+ writer.Write(RawData);
+ }
+
+ return ms.ToArray();
+ }
+ }
+
private long GetPadSize16(long position)
{
if ((position & 0xf) != 0)
@@ -196,7 +244,7 @@ namespace Ryujinx.HLE.HOS.Ipc
}
// ReSharper disable once InconsistentNaming
- public (long Position, long Size) GetBufferType0x21(int index = 0)
+ public (ulong Position, ulong Size) GetBufferType0x21(int index = 0)
{
if (PtrBuff.Count > index &&
PtrBuff[index].Position != 0 &&
@@ -216,7 +264,7 @@ namespace Ryujinx.HLE.HOS.Ipc
}
// ReSharper disable once InconsistentNaming
- public (long Position, long Size) GetBufferType0x22(int index = 0)
+ public (ulong Position, ulong Size) GetBufferType0x22(int index = 0)
{
if (RecvListBuff.Count > index &&
RecvListBuff[index].Position != 0 &&
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs
index 67cf17c9..05798fe1 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs
@@ -4,11 +4,11 @@ namespace Ryujinx.HLE.HOS.Ipc
{
struct IpcPtrBuffDesc
{
- public long Position { get; private set; }
- public int Index { get; private set; }
- public long Size { get; private set; }
+ public ulong Position { get; private set; }
+ public uint Index { get; private set; }
+ public ulong Size { get; private set; }
- public IpcPtrBuffDesc(long position, int index, long size)
+ public IpcPtrBuffDesc(ulong position, uint index, ulong size)
{
Position = position;
Index = index;
@@ -17,20 +17,20 @@ namespace Ryujinx.HLE.HOS.Ipc
public IpcPtrBuffDesc(BinaryReader reader)
{
- long word0 = reader.ReadUInt32();
- long word1 = reader.ReadUInt32();
+ ulong word0 = reader.ReadUInt32();
+ ulong word1 = reader.ReadUInt32();
- Position = word1;
+ Position = word1;
Position |= (word0 << 20) & 0x0f00000000;
Position |= (word0 << 30) & 0x7000000000;
- Index = ((int)word0 >> 0) & 0x03f;
- Index |= ((int)word0 >> 3) & 0x1c0;
+ Index = ((uint)word0 >> 0) & 0x03f;
+ Index |= ((uint)word0 >> 3) & 0x1c0;
Size = (ushort)(word0 >> 16);
}
- public IpcPtrBuffDesc WithSize(long size)
+ public IpcPtrBuffDesc WithSize(ulong size)
{
return new IpcPtrBuffDesc(Position, Index, size);
}
@@ -42,8 +42,8 @@ namespace Ryujinx.HLE.HOS.Ipc
word0 = (uint)((Position & 0x0f00000000) >> 20);
word0 |= (uint)((Position & 0x7000000000) >> 30);
- word0 |= (uint)(Index & 0x03f) << 0;
- word0 |= (uint)(Index & 0x1c0) << 3;
+ word0 |= (Index & 0x03f) << 0;
+ word0 |= (Index & 0x1c0) << 3;
word0 |= (uint)Size << 16;
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
index 78732550..10406ac7 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
@@ -4,10 +4,10 @@ namespace Ryujinx.HLE.HOS.Ipc
{
struct IpcRecvListBuffDesc
{
- public long Position { get; private set; }
- public long Size { get; private set; }
+ public ulong Position { get; private set; }
+ public ulong Size { get; private set; }
- public IpcRecvListBuffDesc(long position, long size)
+ public IpcRecvListBuffDesc(ulong position, ulong size)
{
Position = position;
Size = size;
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Ipc
public IpcRecvListBuffDesc(BinaryReader reader)
{
- long value = reader.ReadInt64();
+ ulong value = reader.ReadUInt64();
Position = value & 0xffffffffffff;
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
index 3002b6a9..53e539a2 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
@@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
if (currentProcess.CpuMemory.IsMapped(address) &&
currentProcess.CpuMemory.IsMapped(address + (ulong)size - 1))
{
- value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, (long)address, size);
+ value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, address, size);
return true;
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index 429c23e3..15ab82b8 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -340,7 +340,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
UserExceptionContextAddress = userExceptionContextAddress;
- MemoryHelper.FillWithZeros(CpuMemory, (long)userExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
+ MemoryHelper.FillWithZeros(CpuMemory, userExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
Name = creationInfo.Name;
@@ -461,7 +461,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
pageInfo = new KTlsPageInfo(tlsPageVa);
- MemoryHelper.FillWithZeros(CpuMemory, (long)tlsPageVa, KMemoryManager.PageSize);
+ MemoryHelper.FillWithZeros(CpuMemory, tlsPageVa, KMemoryManager.PageSize);
}
return result;
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index 418a02f0..238f1226 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -1431,7 +1431,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
KProcess process = KernelStatic.GetCurrentProcess();
- string str = MemoryHelper.ReadAsciiString(process.CpuMemory, (long)strPtr, (long)size);
+ string str = MemoryHelper.ReadAsciiString(process.CpuMemory, strPtr, (long)size);
Logger.Warning?.Print(LogClass.KernelSvc, str);
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
index e427f24d..0982ceff 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -22,8 +22,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private struct SchedulingState
{
- public bool NeedsScheduling;
- public KThread SelectedThread;
+ public volatile bool NeedsScheduling;
+ public volatile KThread SelectedThread;
}
private SchedulingState _state;
@@ -349,31 +349,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
nextThread ??= _idleThread;
- if (currentThread == nextThread)
+ if (currentThread != nextThread)
{
- return;
- }
+ long previousTicks = LastContextSwitchTime;
+ long currentTicks = PerformanceCounter.ElapsedTicks;
+ long ticksDelta = currentTicks - previousTicks;
- long previousTicks = LastContextSwitchTime;
- long currentTicks = PerformanceCounter.ElapsedTicks;
- long ticksDelta = currentTicks - previousTicks;
+ currentThread.AddCpuTime(ticksDelta);
- currentThread.AddCpuTime(ticksDelta);
-
- if (currentProcess != null)
- {
- currentProcess.AddCpuTime(ticksDelta);
- }
+ if (currentProcess != null)
+ {
+ currentProcess.AddCpuTime(ticksDelta);
+ }
- LastContextSwitchTime = currentTicks;
+ LastContextSwitchTime = currentTicks;
- if (currentProcess != null)
- {
- _previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null;
- }
- else if (currentThread == _idleThread)
- {
- _previousThread = null;
+ if (currentProcess != null)
+ {
+ _previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null;
+ }
+ else if (currentThread == _idleThread)
+ {
+ _previousThread = null;
+ }
}
if (nextThread.CurrentCore != _coreId)
@@ -469,6 +467,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
KThread currentThread = KernelStatic.GetCurrentThread();
+ if (!currentThread.IsSchedulable)
+ {
+ return;
+ }
+
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
@@ -491,6 +494,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
KThread currentThread = KernelStatic.GetCurrentThread();
+ if (!currentThread.IsSchedulable)
+ {
+ return;
+ }
+
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
@@ -550,6 +558,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
KThread currentThread = KernelStatic.GetCurrentThread();
+ if (!currentThread.IsSchedulable)
+ {
+ return;
+ }
+
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
index b95b1e8e..7ba9e43a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
@@ -161,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
TlsDramAddress = owner.MemoryManager.GetDramAddressFromVa(_tlsAddress);
- MemoryHelper.FillWithZeros(owner.CpuMemory, (long)_tlsAddress, KTlsPageInfo.TlsEntrySize);
+ MemoryHelper.FillWithZeros(owner.CpuMemory, _tlsAddress, KTlsPageInfo.TlsEntrySize);
}
bool is64Bits;
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
index d36ea931..2cea57e9 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
@@ -1,41 +1,85 @@
-using Ryujinx.Common;
+using LibHac;
+using LibHac.Fs;
+using LibHac.Fs.Shim;
+using Ryujinx.Common;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.FileSystem.Content;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
public class AccountManager
{
+ public static readonly UserId DefaultUserId = new UserId("00000000000000010000000000000000");
+
+ private readonly VirtualFileSystem _virtualFileSystem;
+ private readonly AccountSaveDataManager _accountSaveDataManager;
+
private ConcurrentDictionary<string, UserProfile> _profiles;
public UserProfile LastOpenedUser { get; private set; }
- public AccountManager()
+ public AccountManager(VirtualFileSystem virtualFileSystem)
{
+ _virtualFileSystem = virtualFileSystem;
+
_profiles = new ConcurrentDictionary<string, UserProfile>();
- UserId defaultUserId = new UserId("00000000000000010000000000000000");
- byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg");
+ _accountSaveDataManager = new AccountSaveDataManager(_profiles);
+
+ if (!_profiles.TryGetValue(DefaultUserId.ToString(), out _))
+ {
+ byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg");
- AddUser(defaultUserId, "Player", defaultUserImage);
-
- OpenUser(defaultUserId);
+ AddUser("RyuPlayer", defaultUserImage, DefaultUserId);
+
+ OpenUser(DefaultUserId);
+ }
+ else
+ {
+ OpenUser(_accountSaveDataManager.LastOpened);
+ }
}
- public void AddUser(UserId userId, string name, byte[] image)
+ public void AddUser(string name, byte[] image, UserId userId = new UserId())
{
+ if (userId.IsNull)
+ {
+ userId = new UserId(Guid.NewGuid().ToString().Replace("-", ""));
+ }
+
UserProfile profile = new UserProfile(userId, name, image);
_profiles.AddOrUpdate(userId.ToString(), profile, (key, old) => profile);
+
+ _accountSaveDataManager.Save(_profiles);
}
public void OpenUser(UserId userId)
{
if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
{
+ // TODO: Support multiple open users ?
+ foreach (UserProfile userProfile in GetAllUsers())
+ {
+ if (userProfile == LastOpenedUser)
+ {
+ userProfile.AccountState = AccountState.Closed;
+
+ break;
+ }
+ }
+
(LastOpenedUser = profile).AccountState = AccountState.Open;
+
+ _accountSaveDataManager.LastOpened = userId;
}
+
+ _accountSaveDataManager.Save(_profiles);
}
public void CloseUser(UserId userId)
@@ -44,9 +88,117 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
profile.AccountState = AccountState.Closed;
}
+
+ _accountSaveDataManager.Save(_profiles);
+ }
+
+ public void OpenUserOnlinePlay(UserId userId)
+ {
+ if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
+ {
+ // TODO: Support multiple open online users ?
+ foreach (UserProfile userProfile in GetAllUsers())
+ {
+ if (userProfile == LastOpenedUser)
+ {
+ userProfile.OnlinePlayState = AccountState.Closed;
+
+ break;
+ }
+ }
+
+ profile.OnlinePlayState = AccountState.Open;
+ }
+
+ _accountSaveDataManager.Save(_profiles);
+ }
+
+ public void CloseUserOnlinePlay(UserId userId)
+ {
+ if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
+ {
+ profile.OnlinePlayState = AccountState.Closed;
+ }
+
+ _accountSaveDataManager.Save(_profiles);
+ }
+
+ public void SetUserImage(UserId userId, byte[] image)
+ {
+ foreach (UserProfile userProfile in GetAllUsers())
+ {
+ if (userProfile.UserId == userId)
+ {
+ userProfile.Image = image;
+
+ break;
+ }
+ }
+
+ _accountSaveDataManager.Save(_profiles);
+ }
+
+ public void SetUserName(UserId userId, string name)
+ {
+ foreach (UserProfile userProfile in GetAllUsers())
+ {
+ if (userProfile.UserId == userId)
+ {
+ userProfile.Name = name;
+
+ break;
+ }
+ }
+
+ _accountSaveDataManager.Save(_profiles);
+ }
+
+ public void DeleteUser(UserId userId)
+ {
+ DeleteSaveData(userId);
+
+ _profiles.Remove(userId.ToString(), out _);
+
+ OpenUser(DefaultUserId);
+
+ _accountSaveDataManager.Save(_profiles);
+ }
+
+ private void DeleteSaveData(UserId userId)
+ {
+ SaveDataFilter saveDataFilter = new SaveDataFilter();
+ saveDataFilter.SetUserId(new LibHac.Fs.UserId((ulong)userId.High, (ulong)userId.Low));
+
+ Result result = _virtualFileSystem.FsClient.OpenSaveDataIterator(out SaveDataIterator saveDataIterator, SaveDataSpaceId.User, ref saveDataFilter);
+ if (result.IsSuccess())
+ {
+ Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
+
+ while (true)
+ {
+ saveDataIterator.ReadSaveDataInfo(out long readCount, saveDataInfo);
+
+ if (readCount == 0)
+ {
+ break;
+ }
+
+ for (int i = 0; i < readCount; i++)
+ {
+ // TODO: We use Directory.Delete workaround because DeleteSaveData softlock without, due to a bug in LibHac 0.12.0.
+ string savePath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataInfo[i].SaveDataId:x16}");
+ string saveMetaPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/saveMeta/{saveDataInfo[i].SaveDataId:x16}");
+
+ Directory.Delete(savePath, true);
+ Directory.Delete(saveMetaPath, true);
+
+ _virtualFileSystem.FsClient.DeleteSaveData(SaveDataSpaceId.User, saveDataInfo[i].SaveDataId);
+ }
+ }
+ }
}
- public int GetUserCount()
+ internal int GetUserCount()
{
return _profiles.Count;
}
@@ -56,7 +208,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return _profiles.TryGetValue(userId.ToString(), out profile);
}
- internal IEnumerable<UserProfile> GetAllUsers()
+ public IEnumerable<UserProfile> GetAllUsers()
{
return _profiles.Values;
}
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs
new file mode 100644
index 00000000..44ef3f33
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs
@@ -0,0 +1,87 @@
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Utilities;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Text.Json.Serialization;
+
+namespace Ryujinx.HLE.HOS.Services.Account.Acc
+{
+ class AccountSaveDataManager
+ {
+ private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
+
+ private struct ProfilesJson
+ {
+ [JsonPropertyName("profiles")]
+ public List<UserProfileJson> Profiles { get; set; }
+ [JsonPropertyName("last_opened")]
+ public string LastOpened { get; set; }
+ }
+
+ private struct UserProfileJson
+ {
+ [JsonPropertyName("user_id")]
+ public string UserId { get; set; }
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+ [JsonPropertyName("account_state")]
+ public AccountState AccountState { get; set; }
+ [JsonPropertyName("online_play_state")]
+ public AccountState OnlinePlayState { get; set; }
+ [JsonPropertyName("last_modified_timestamp")]
+ public long LastModifiedTimestamp { get; set; }
+ [JsonPropertyName("image")]
+ public byte[] Image { get; set; }
+ }
+
+ public UserId LastOpened { get; set; }
+
+ public AccountSaveDataManager(ConcurrentDictionary<string, UserProfile> profiles)
+ {
+ // TODO: Use 0x8000000000000010 system savedata instead of a JSON file if needed.
+
+ if (File.Exists(_profilesJsonPath))
+ {
+ ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath);
+
+ foreach (var profile in profilesJson.Profiles)
+ {
+ UserProfile addedProfile = new UserProfile(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp);
+
+ profiles.AddOrUpdate(profile.UserId, addedProfile, (key, old) => addedProfile);
+ }
+
+ LastOpened = new UserId(profilesJson.LastOpened);
+ }
+ else
+ {
+ LastOpened = AccountManager.DefaultUserId;
+ }
+ }
+
+ public void Save(ConcurrentDictionary<string, UserProfile> profiles)
+ {
+ ProfilesJson profilesJson = new ProfilesJson()
+ {
+ Profiles = new List<UserProfileJson>(),
+ LastOpened = LastOpened.ToString()
+ };
+
+ foreach (var profile in profiles)
+ {
+ profilesJson.Profiles.Add(new UserProfileJson()
+ {
+ UserId = profile.Value.UserId.ToString(),
+ Name = profile.Value.Name,
+ AccountState = profile.Value.AccountState,
+ OnlinePlayState = profile.Value.OnlinePlayState,
+ LastModifiedTimestamp = profile.Value.LastModifiedTimestamp,
+ Image = profile.Value.Image,
+ });
+ }
+
+ File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs
index c7efe778..471942f1 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs
@@ -73,8 +73,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
public ResultCode LoadIdTokenCache(ServiceCtx context)
{
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferSize = context.Request.ReceiveBuff[0].Size;
// NOTE: This opens the file at "su/cache/USERID_IN_UUID_STRING.dat" (where USERID_IN_UUID_STRING is formatted as "%08x-%04x-%04x-%02x%02x-%08x%04x")
// in the "account:/" savedata and writes some data in the buffer.
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs
index 18534393..8e29f94b 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs
@@ -16,16 +16,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
public ResultCode Get(ServiceCtx context)
{
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x80L);
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x80UL);
- long bufferPosition = context.Request.RecvListBuff[0].Position;
+ ulong bufferPosition = context.Request.RecvListBuff[0].Position;
MemoryHelper.FillWithZeros(context.Memory, bufferPosition, 0x80);
// TODO: Determine the struct.
- context.Memory.Write((ulong)bufferPosition, 0); // Unknown
- context.Memory.Write((ulong)bufferPosition + 4, 1); // Icon ID. 0 = Mii, the rest are character icon IDs.
- context.Memory.Write((ulong)bufferPosition + 8, (byte)1); // Profile icon background color ID
+ context.Memory.Write(bufferPosition, 0); // Unknown
+ context.Memory.Write(bufferPosition + 4, 1); // Icon ID. 0 = Mii, the rest are character icon IDs.
+ context.Memory.Write(bufferPosition + 8, (byte)1); // Profile icon background color ID
// 0x07 bytes - Unknown
// 0x10 bytes - Some ID related to the Mii? All zeros when a character icon is used.
// 0x60 bytes - Usually zeros?
@@ -57,15 +57,15 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
public ResultCode LoadImage(ServiceCtx context)
{
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferLen = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferLen = context.Request.ReceiveBuff[0].Size;
- if (_profile.Image.Length > bufferLen)
+ if ((ulong)_profile.Image.Length > bufferLen)
{
return ResultCode.InvalidBufferSize;
}
- context.Memory.Write((ulong)bufferPosition, _profile.Image);
+ context.Memory.Write(bufferPosition, _profile.Image);
context.ResponseData.Write(_profile.Image.Length);
@@ -74,12 +74,12 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
public ResultCode Store(ServiceCtx context)
{
- long userDataPosition = context.Request.PtrBuff[0].Position;
- long userDataSize = context.Request.PtrBuff[0].Size;
+ ulong userDataPosition = context.Request.PtrBuff[0].Position;
+ ulong userDataSize = context.Request.PtrBuff[0].Size;
byte[] userData = new byte[userDataSize];
- context.Memory.Read((ulong)userDataPosition, userData);
+ context.Memory.Read(userDataPosition, userData);
// TODO: Read the nn::account::profile::ProfileBase and store everything in the savedata.
@@ -90,19 +90,19 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
public ResultCode StoreWithImage(ServiceCtx context)
{
- long userDataPosition = context.Request.PtrBuff[0].Position;
- long userDataSize = context.Request.PtrBuff[0].Size;
+ ulong userDataPosition = context.Request.PtrBuff[0].Position;
+ ulong userDataSize = context.Request.PtrBuff[0].Size;
byte[] userData = new byte[userDataSize];
- context.Memory.Read((ulong)userDataPosition, userData);
+ context.Memory.Read(userDataPosition, userData);
- long profileImagePosition = context.Request.SendBuff[0].Position;
- long profileImageSize = context.Request.SendBuff[0].Size;
+ ulong profileImagePosition = context.Request.SendBuff[0].Position;
+ ulong profileImageSize = context.Request.SendBuff[0].Size;
byte[] profileImageData = new byte[profileImageSize];
- context.Memory.Read((ulong)profileImagePosition, profileImageData);
+ context.Memory.Read(profileImagePosition, profileImageData);
// TODO: Read the nn::account::profile::ProfileBase and store everything in the savedata.
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs
index 29cce5d7..794c72ce 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs
@@ -53,8 +53,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return ResultCode.InvalidBuffer;
}
- long outputPosition = context.Request.RecvListBuff[0].Position;
- long outputSize = context.Request.RecvListBuff[0].Size;
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputSize = context.Request.RecvListBuff[0].Size;
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
@@ -67,8 +67,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
break;
}
- context.Memory.Write((ulong)outputPosition + offset, userProfile.UserId.High);
- context.Memory.Write((ulong)outputPosition + offset + 8, userProfile.UserId.Low);
+ context.Memory.Write(outputPosition + offset, userProfile.UserId.High);
+ context.Memory.Write(outputPosition + offset + 8, userProfile.UserId.Low);
offset += 0x10;
}
@@ -156,8 +156,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return ResultCode.InvalidBuffer;
}
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
if (inputSize != 0x24000)
{
@@ -166,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
byte[] thumbnailBuffer = new byte[inputSize];
- context.Memory.Read((ulong)inputPosition, thumbnailBuffer);
+ context.Memory.Read(inputPosition, thumbnailBuffer);
// NOTE: Account service call nn::fs::WriteSaveDataThumbnailFile().
// TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ?
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs
index 6067dc44..2fbf950c 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs
@@ -142,8 +142,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
// ListOpenContextStoredUsers() -> array<nn::account::Uid, 0xa>
public ResultCode ListOpenContextStoredUsers(ServiceCtx context)
{
- long outputPosition = context.Request.RecvListBuff[0].Position;
- long outputSize = context.Request.RecvListBuff[0].Size;
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputSize = context.Request.RecvListBuff[0].Size;
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs
index a57796c9..ef0a1a64 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs
@@ -8,31 +8,80 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
public UserId UserId { get; }
- public string Name { get; }
+ public long LastModifiedTimestamp { get; set; }
- public byte[] Image { get; }
+ private string _name;
- public long LastModifiedTimestamp { get; private set; }
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ _name = value;
+
+ UpdateLastModifiedTimestamp();
+ }
+ }
- public AccountState AccountState { get; set; }
- public AccountState OnlinePlayState { get; set; }
+ private byte[] _image;
- public UserProfile(UserId userId, string name, byte[] image)
+ public byte[] Image
{
- UserId = userId;
- Name = name;
+ get => _image;
+ set
+ {
+ _image = value;
- Image = image;
+ UpdateLastModifiedTimestamp();
+ }
+ }
+
+ private AccountState _accountState;
- LastModifiedTimestamp = 0;
+ public AccountState AccountState
+ {
+ get => _accountState;
+ set
+ {
+ _accountState = value;
+
+ UpdateLastModifiedTimestamp();
+ }
+ }
+
+ public AccountState _onlinePlayState;
+
+ public AccountState OnlinePlayState
+ {
+ get => _onlinePlayState;
+ set
+ {
+ _onlinePlayState = value;
+
+ UpdateLastModifiedTimestamp();
+ }
+ }
+
+ public UserProfile(UserId userId, string name, byte[] image, long lastModifiedTimestamp = 0)
+ {
+ UserId = userId;
+ Name = name;
+ Image = image;
AccountState = AccountState.Closed;
OnlinePlayState = AccountState.Closed;
- UpdateTimestamp();
+ if (lastModifiedTimestamp != 0)
+ {
+ LastModifiedTimestamp = lastModifiedTimestamp;
+ }
+ else
+ {
+ UpdateLastModifiedTimestamp();
+ }
}
- private void UpdateTimestamp()
+ private void UpdateLastModifiedTimestamp()
{
LastModifiedTimestamp = (long)(DateTime.Now - Epoch).TotalSeconds;
}
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs
index cdc59678..33f5393f 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs
@@ -29,20 +29,20 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
return ResultCode.ObjectInvalid;
}
- long writePosition = context.RequestData.ReadInt64();
+ ulong writePosition = context.RequestData.ReadUInt64();
- if (writePosition > _storage.Data.Length)
+ if (writePosition > (ulong)_storage.Data.Length)
{
return ResultCode.OutOfBounds;
}
- (long position, long size) = context.Request.GetBufferType0x21();
+ (ulong position, ulong size) = context.Request.GetBufferType0x21();
- size = Math.Min(size, _storage.Data.Length - writePosition);
+ size = Math.Min(size, (ulong)_storage.Data.Length - writePosition);
if (size > 0)
{
- long maxSize = _storage.Data.Length - writePosition;
+ ulong maxSize = (ulong)_storage.Data.Length - writePosition;
if (size > maxSize)
{
@@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
byte[] data = new byte[size];
- context.Memory.Read((ulong)position, data);
+ context.Memory.Read(position, data);
Buffer.BlockCopy(data, 0, _storage.Data, (int)writePosition, (int)size);
}
@@ -63,22 +63,22 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
// Read(u64) -> buffer<bytes, 0x22>
public ResultCode Read(ServiceCtx context)
{
- long readPosition = context.RequestData.ReadInt64();
+ ulong readPosition = context.RequestData.ReadUInt64();
- if (readPosition > _storage.Data.Length)
+ if (readPosition > (ulong)_storage.Data.Length)
{
return ResultCode.OutOfBounds;
}
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
- size = Math.Min(size, _storage.Data.Length - readPosition);
+ size = Math.Min(size, (ulong)_storage.Data.Length - readPosition);
byte[] data = new byte[size];
Buffer.BlockCopy(_storage.Data, (int)readPosition, data, 0, (int)size);
- context.Memory.Write((ulong)position, data);
+ context.Memory.Write(position, data);
return ResultCode.Success;
}
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
index a1a5d8c4..e0633145 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
@@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
switch (kind)
{
case LaunchParameterKind.UserChannel:
- storageData = context.Device.UserChannelPersistence.Pop();
+ storageData = context.Device.Configuration.UserChannelPersistence.Pop();
break;
case LaunchParameterKind.PreselectedUser:
// Only the first 0x18 bytes of the Data seems to be actually used.
@@ -359,13 +359,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// SetApplicationCopyrightImage(buffer<bytes, 0x45> frame_buffer, s32 x, s32 y, s32 width, s32 height, s32 window_origin_mode)
public ResultCode SetApplicationCopyrightImage(ServiceCtx context)
{
- long frameBufferPos = context.Request.SendBuff[0].Position;
- long frameBufferSize = context.Request.SendBuff[0].Size;
- int x = context.RequestData.ReadInt32();
- int y = context.RequestData.ReadInt32();
- int width = context.RequestData.ReadInt32();
- int height = context.RequestData.ReadInt32();
- uint windowOriginMode = context.RequestData.ReadUInt32();
+ ulong frameBufferPos = context.Request.SendBuff[0].Position;
+ ulong frameBufferSize = context.Request.SendBuff[0].Size;
+ int x = context.RequestData.ReadInt32();
+ int y = context.RequestData.ReadInt32();
+ int width = context.RequestData.ReadInt32();
+ int height = context.RequestData.ReadInt32();
+ uint windowOriginMode = context.RequestData.ReadUInt32();
ResultCode resultCode = ResultCode.InvalidParameters;
@@ -388,7 +388,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
return resultCode;
}
- private ResultCode SetApplicationCopyrightImageImpl(int x, int y, int width, int height, long frameBufferPos, long frameBufferSize, uint windowOriginMode)
+ private ResultCode SetApplicationCopyrightImageImpl(int x, int y, int width, int height, ulong frameBufferPos, ulong frameBufferSize, uint windowOriginMode)
{
/*
if (_copyrightBuffer == null)
@@ -453,7 +453,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// ClearUserChannel()
public ResultCode ClearUserChannel(ServiceCtx context)
{
- context.Device.UserChannelPersistence.Clear();
+ context.Device.Configuration.UserChannelPersistence.Clear();
return ResultCode.Success;
}
@@ -464,7 +464,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
{
AppletAE.IStorage data = GetObject<AppletAE.IStorage>(context, 0);
- context.Device.UserChannelPersistence.Push(data.Data);
+ context.Device.Configuration.UserChannelPersistence.Push(data.Data);
return ResultCode.Success;
}
@@ -473,7 +473,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// GetPreviousProgramIndex() -> s32 program_index
public ResultCode GetPreviousProgramIndex(ServiceCtx context)
{
- int previousProgramIndex = context.Device.UserChannelPersistence.PreviousIndex;
+ int previousProgramIndex = context.Device.Configuration.UserChannelPersistence.PreviousIndex;
context.ResponseData.Write(previousProgramIndex);
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs
index a89abce7..b45a4d2c 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs
@@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
// AppendAudioInBuffer(u64 tag, buffer<nn::audio::AudioInBuffer, 5>)
public ResultCode AppendAudioInBuffer(ServiceCtx context)
{
- long position = context.Request.SendBuff[0].Position;
+ ulong position = context.Request.SendBuff[0].Position;
ulong bufferTag = context.RequestData.ReadUInt64();
@@ -74,8 +74,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
// GetReleasedAudioInBuffers() -> (u32 count, buffer<u64, 6> tags)
public ResultCode GetReleasedAudioInBuffers(ServiceCtx context)
{
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
{
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
// AppendUacInBuffer(u64 tag, handle<copy, unknown>, buffer<nn::audio::AudioInBuffer, 5>)
public ResultCode AppendUacInBuffer(ServiceCtx context)
{
- long position = context.Request.SendBuff[0].Position;
+ ulong position = context.Request.SendBuff[0].Position;
ulong bufferTag = context.RequestData.ReadUInt64();
uint handle = (uint)context.Request.HandleDesc.ToCopy[0];
@@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
// AppendAudioInBufferAuto(u64 tag, buffer<nn::audio::AudioInBuffer, 0x21>)
public ResultCode AppendAudioInBufferAuto(ServiceCtx context)
{
- (long position, _) = context.Request.GetBufferType0x21();
+ (ulong position, _) = context.Request.GetBufferType0x21();
ulong bufferTag = context.RequestData.ReadUInt64();
@@ -129,9 +129,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
// GetReleasedAudioInBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags)
public ResultCode GetReleasedAudioInBuffersAuto(ServiceCtx context)
{
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
- using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
+ using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size))
{
ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
@@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
// AppendUacInBufferAuto(u64 tag, handle<copy, event>, buffer<nn::audio::AudioInBuffer, 0x21>)
public ResultCode AppendUacInBufferAuto(ServiceCtx context)
{
- (long position, _) = context.Request.GetBufferType0x21();
+ (ulong position, _) = context.Request.GetBufferType0x21();
ulong bufferTag = context.RequestData.ReadUInt64();
uint handle = (uint)context.Request.HandleDesc.ToCopy[0];
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs
index 4806ebe9..7b243c71 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs
@@ -27,10 +27,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
string[] deviceNames = _impl.ListAudioIns(false);
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
- long basePosition = position;
+ ulong basePosition = position;
int count = 0;
@@ -38,15 +38,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
byte[] buffer = Encoding.ASCII.GetBytes(name);
- if ((position - basePosition) + buffer.Length > size)
+ if ((position - basePosition) + (ulong)buffer.Length > size)
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
break;
}
- context.Memory.Write((ulong)position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+ context.Memory.Write(position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length);
position += AudioInNameSize;
count++;
@@ -65,15 +65,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
ulong appletResourceUserId = context.RequestData.ReadUInt64();
- long deviceNameInputPosition = context.Request.SendBuff[0].Position;
- long deviceNameInputSize = context.Request.SendBuff[0].Size;
+ ulong deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ ulong deviceNameInputSize = context.Request.SendBuff[0].Size;
- long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
- long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+ ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
- string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
@@ -83,8 +83,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
- context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
- MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+ context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
MakeObject(context, new AudioInServer(obj));
}
@@ -98,9 +98,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
string[] deviceNames = _impl.ListAudioIns(false);
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
- long basePosition = position;
+ ulong basePosition = position;
int count = 0;
@@ -108,15 +108,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
byte[] buffer = Encoding.ASCII.GetBytes(name);
- if ((position - basePosition) + buffer.Length > size)
+ if ((position - basePosition) + (ulong)buffer.Length > size)
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
break;
}
- context.Memory.Write((ulong)position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+ context.Memory.Write(position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length);
position += AudioInNameSize;
count++;
@@ -135,12 +135,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio
AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
ulong appletResourceUserId = context.RequestData.ReadUInt64();
- (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21();
- (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22();
+ (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21();
+ (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22();
uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
- string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
@@ -150,8 +150,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
- context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
- MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+ context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
MakeObject(context, new AudioInServer(obj));
}
@@ -165,9 +165,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
string[] deviceNames = _impl.ListAudioIns(true);
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
- long basePosition = position;
+ ulong basePosition = position;
int count = 0;
@@ -175,15 +175,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
byte[] buffer = Encoding.ASCII.GetBytes(name);
- if ((position - basePosition) + buffer.Length > size)
+ if ((position - basePosition) + (ulong)buffer.Length > size)
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
break;
}
- context.Memory.Write((ulong)position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+ context.Memory.Write(position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length);
position += AudioInNameSize;
count++;
@@ -205,15 +205,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
ulong appletResourceUserId = context.RequestData.ReadUInt64();
- long deviceNameInputPosition = context.Request.SendBuff[0].Position;
- long deviceNameInputSize = context.Request.SendBuff[0].Size;
+ ulong deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ ulong deviceNameInputSize = context.Request.SendBuff[0].Size;
- long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
- long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+ ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
- string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
@@ -223,8 +223,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
- context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
- MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+ context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
MakeObject(context, new AudioInServer(obj));
}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs
index 72ff4da7..b7515e0f 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs
@@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
// AppendAudioOutBuffer(u64 bufferTag, buffer<nn::audio::AudioOutBuffer, 5> buffer)
public ResultCode AppendAudioOutBuffer(ServiceCtx context)
{
- long position = context.Request.SendBuff[0].Position;
+ ulong position = context.Request.SendBuff[0].Position;
ulong bufferTag = context.RequestData.ReadUInt64();
@@ -74,10 +74,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
// GetReleasedAudioOutBuffers() -> (u32 count, buffer<u64, 6> tags)
public ResultCode GetReleasedAudioOutBuffers(ServiceCtx context)
{
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
- using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
+ using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size))
{
ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
// AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
{
- (long position, _) = context.Request.GetBufferType0x21();
+ (ulong position, _) = context.Request.GetBufferType0x21();
ulong bufferTag = context.RequestData.ReadUInt64();
@@ -115,9 +115,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
// GetReleasedAudioOutBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags)
public ResultCode GetReleasedAudioOutBuffersAuto(ServiceCtx context)
{
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
- using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
+ using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size))
{
ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
index a220f90b..3040696e 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
@@ -27,10 +27,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
string[] deviceNames = _impl.ListAudioOuts();
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
- long basePosition = position;
+ ulong basePosition = position;
int count = 0;
@@ -38,15 +38,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
byte[] buffer = Encoding.ASCII.GetBytes(name);
- if ((position - basePosition) + buffer.Length > size)
+ if ((position - basePosition) + (ulong)buffer.Length > size)
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
break;
}
- context.Memory.Write((ulong)position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length);
+ context.Memory.Write(position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioOutNameSize - buffer.Length);
position += AudioOutNameSize;
count++;
@@ -65,15 +65,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
ulong appletResourceUserId = context.RequestData.ReadUInt64();
- long deviceNameInputPosition = context.Request.SendBuff[0].Position;
- long deviceNameInputSize = context.Request.SendBuff[0].Size;
+ ulong deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ ulong deviceNameInputSize = context.Request.SendBuff[0].Size;
- long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
- long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+ ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
- string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
@@ -83,8 +83,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
- context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
- MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
+ context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
MakeObject(context, new AudioOutServer(obj));
}
@@ -98,9 +98,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
string[] deviceNames = _impl.ListAudioOuts();
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
- long basePosition = position;
+ ulong basePosition = position;
int count = 0;
@@ -108,15 +108,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
byte[] buffer = Encoding.ASCII.GetBytes(name);
- if ((position - basePosition) + buffer.Length > size)
+ if ((position - basePosition) + (ulong)buffer.Length > size)
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
break;
}
- context.Memory.Write((ulong)position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length);
+ context.Memory.Write(position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioOutNameSize - buffer.Length);
position += AudioOutNameSize;
count++;
@@ -135,12 +135,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio
AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
ulong appletResourceUserId = context.RequestData.ReadUInt64();
- (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21();
- (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22();
+ (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21();
+ (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22();
uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
- string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
@@ -150,8 +150,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
- context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
- MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
+ context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
MakeObject(context, new AudioOutServer(obj));
}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs
index 437b8745..87ec2f6a 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs
@@ -25,10 +25,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
string[] deviceNames = _impl.ListAudioDeviceName();
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
- long basePosition = position;
+ ulong basePosition = position;
int count = 0;
@@ -36,15 +36,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
byte[] buffer = Encoding.ASCII.GetBytes(name);
- if ((position - basePosition) + buffer.Length > size)
+ if ((position - basePosition) + (ulong)buffer.Length > size)
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
break;
}
- context.Memory.Write((ulong)position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length);
+ context.Memory.Write(position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length);
position += AudioDeviceNameSize;
count++;
@@ -61,10 +61,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
float volume = context.RequestData.ReadSingle();
- long position = context.Request.SendBuff[0].Position;
- long size = context.Request.SendBuff[0].Size;
+ ulong position = context.Request.SendBuff[0].Position;
+ ulong size = context.Request.SendBuff[0].Size;
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size);
+ string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
return _impl.SetAudioDeviceOutputVolume(deviceName, volume);
}
@@ -73,10 +73,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
// GetAudioDeviceOutputVolume(buffer<bytes, 5> name) -> f32 volume
public ResultCode GetAudioDeviceOutputVolume(ServiceCtx context)
{
- long position = context.Request.SendBuff[0].Position;
- long size = context.Request.SendBuff[0].Size;
+ ulong position = context.Request.SendBuff[0].Position;
+ ulong size = context.Request.SendBuff[0].Size;
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size);
+ string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume);
@@ -94,14 +94,14 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
string name = _impl.GetActiveAudioDeviceName();
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0");
- if ((ulong)deviceNameBuffer.Length <= (ulong)size)
+ if ((ulong)deviceNameBuffer.Length <= size)
{
- context.Memory.Write((ulong)position, deviceNameBuffer);
+ context.Memory.Write(position, deviceNameBuffer);
}
else
{
@@ -146,9 +146,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
string[] deviceNames = _impl.ListAudioDeviceName();
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
- long basePosition = position;
+ ulong basePosition = position;
int count = 0;
@@ -156,15 +156,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
byte[] buffer = Encoding.ASCII.GetBytes(name);
- if ((position - basePosition) + buffer.Length > size)
+ if ((position - basePosition) + (ulong)buffer.Length > size)
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
break;
}
- context.Memory.Write((ulong)position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length);
+ context.Memory.Write(position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length);
position += AudioDeviceNameSize;
count++;
@@ -181,9 +181,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
float volume = context.RequestData.ReadSingle();
- (long position, long size) = context.Request.GetBufferType0x21();
+ (ulong position, ulong size) = context.Request.GetBufferType0x21();
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size);
+ string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
return _impl.SetAudioDeviceOutputVolume(deviceName, volume);
}
@@ -192,9 +192,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
// GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21> name) -> f32
public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context)
{
- (long position, long size) = context.Request.GetBufferType0x21();
+ (ulong position, ulong size) = context.Request.GetBufferType0x21();
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size);
+ string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume);
@@ -212,13 +212,13 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
{
string name = _impl.GetActiveAudioDeviceName();
- (long position, long size) = context.Request.GetBufferType0x22();
+ (ulong position, ulong size) = context.Request.GetBufferType0x22();
byte[] deviceNameBuffer = Encoding.UTF8.GetBytes(name + '\0');
- if ((ulong)deviceNameBuffer.Length <= (ulong)size)
+ if ((ulong)deviceNameBuffer.Length <= size)
{
- context.Memory.Write((ulong)position, deviceNameBuffer);
+ context.Memory.Write(position, deviceNameBuffer);
}
else
{
@@ -268,10 +268,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
// GetAudioSystemMasterVolumeSetting(buffer<bytes, 5> name) -> f32
public ResultCode GetAudioSystemMasterVolumeSetting(ServiceCtx context)
{
- long position = context.Request.SendBuff[0].Position;
- long size = context.Request.SendBuff[0].Size;
+ ulong position = context.Request.SendBuff[0].Position;
+ ulong size = context.Request.SendBuff[0].Size;
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size);
+ string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
ResultCode result = _impl.GetAudioSystemMasterVolumeSetting(deviceName, out float systemMasterVolume);
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
index 56eb173d..bb51b506 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
@@ -57,16 +57,16 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
// -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> performanceOutput)
public ResultCode RequestUpdate(ServiceCtx context)
{
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
- long performanceOutputPosition = context.Request.ReceiveBuff[1].Position;
- long performanceOutputSize = context.Request.ReceiveBuff[1].Size;
+ ulong performanceOutputPosition = context.Request.ReceiveBuff[1].Position;
+ ulong performanceOutputSize = context.Request.ReceiveBuff[1].Size;
- ReadOnlyMemory<byte> input = context.Memory.GetSpan((ulong)inputPosition, (int)inputSize).ToArray();
+ ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
Memory<byte> output = new byte[outputSize];
Memory<byte> performanceOutput = new byte[performanceOutputSize];
@@ -78,8 +78,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
if (result == ResultCode.Success)
{
- context.Memory.Write((ulong)outputPosition, output.Span);
- context.Memory.Write((ulong)performanceOutputPosition, performanceOutput.Span);
+ context.Memory.Write(outputPosition, output.Span);
+ context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
}
else
{
@@ -149,11 +149,11 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
// -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> performanceOutput)
public ResultCode RequestUpdateAuto(ServiceCtx context)
{
- (long inputPosition, long inputSize) = context.Request.GetBufferType0x21();
- (long outputPosition, long outputSize) = context.Request.GetBufferType0x22(0);
- (long performanceOutputPosition, long performanceOutputSize) = context.Request.GetBufferType0x22(1);
+ (ulong inputPosition, ulong inputSize) = context.Request.GetBufferType0x21();
+ (ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22(0);
+ (ulong performanceOutputPosition, ulong performanceOutputSize) = context.Request.GetBufferType0x22(1);
- ReadOnlyMemory<byte> input = context.Memory.GetSpan((ulong)inputPosition, (int)inputSize).ToArray();
+ ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
Memory<byte> output = new byte[outputSize];
Memory<byte> performanceOutput = new byte[performanceOutputSize];
@@ -165,8 +165,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
if (result == ResultCode.Success)
{
- context.Memory.Write((ulong)outputPosition, output.Span);
- context.Memory.Write((ulong)performanceOutputPosition, performanceOutput.Span);
+ context.Memory.Write(outputPosition, output.Span);
+ context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
}
return result;
diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs b/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs
index a405457a..44eeb32d 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs
@@ -106,24 +106,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
{
ResultCode result;
- long inPosition = context.Request.SendBuff[0].Position;
- long inSize = context.Request.SendBuff[0].Size;
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong inPosition = context.Request.SendBuff[0].Position;
+ ulong inSize = context.Request.SendBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
byte[] buffer = new byte[inSize];
- context.Memory.Read((ulong)inPosition, buffer);
+ context.Memory.Read(inPosition, buffer);
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
{
- result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples);
+ result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
if (result == ResultCode.Success)
{
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
- context.Memory.Write((ulong)outputPosition, pcmDataBytes);
+ context.Memory.Write(outputPosition, pcmDataBytes);
context.ResponseData.Write(outConsumed);
context.ResponseData.Write(outSamples);
@@ -139,24 +139,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
{
ResultCode result;
- long inPosition = context.Request.SendBuff[0].Position;
- long inSize = context.Request.SendBuff[0].Size;
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong inPosition = context.Request.SendBuff[0].Position;
+ ulong inSize = context.Request.SendBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
byte[] buffer = new byte[inSize];
- context.Memory.Read((ulong)inPosition, buffer);
+ context.Memory.Read(inPosition, buffer);
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
{
- result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples);
+ result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
if (result == ResultCode.Success)
{
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
- context.Memory.Write((ulong)outputPosition, pcmDataBytes);
+ context.Memory.Write(outputPosition, pcmDataBytes);
context.ResponseData.Write(outConsumed);
context.ResponseData.Write(outSamples);
@@ -177,24 +177,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
_reset = context.RequestData.ReadBoolean();
- long inPosition = context.Request.SendBuff[0].Position;
- long inSize = context.Request.SendBuff[0].Size;
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong inPosition = context.Request.SendBuff[0].Position;
+ ulong inSize = context.Request.SendBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
byte[] buffer = new byte[inSize];
- context.Memory.Read((ulong)inPosition, buffer);
+ context.Memory.Read(inPosition, buffer);
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
{
- result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples);
+ result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
if (result == ResultCode.Success)
{
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
- context.Memory.Write((ulong)outputPosition, pcmDataBytes);
+ context.Memory.Write(outputPosition, pcmDataBytes);
context.ResponseData.Write(outConsumed);
context.ResponseData.Write(outSamples);
@@ -215,24 +215,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
_reset = context.RequestData.ReadBoolean();
- long inPosition = context.Request.SendBuff[0].Position;
- long inSize = context.Request.SendBuff[0].Size;
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong inPosition = context.Request.SendBuff[0].Position;
+ ulong inSize = context.Request.SendBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
byte[] buffer = new byte[inSize];
- context.Memory.Read((ulong)inPosition, buffer);
+ context.Memory.Read(inPosition, buffer);
using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer)))
{
- result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples);
+ result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples);
if (result == ResultCode.Success)
{
byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)];
Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length);
- context.Memory.Write((ulong)outputPosition, pcmDataBytes);
+ context.Memory.Write(outputPosition, pcmDataBytes);
context.ResponseData.Write(outConsumed);
context.ResponseData.Write(outSamples);
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs
index f232cd3e..51d8f66c 100644
--- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs
@@ -30,14 +30,14 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>)
public ResultCode Read(ServiceCtx context)
{
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
byte[] data = new byte[size];
Result result = _base.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(data));
- context.Memory.Write((ulong)position, data);
+ context.Memory.Write(position, data);
context.ResponseData.Write(entriesRead);
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs
index fe7c2ffc..9354b60e 100644
--- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs
@@ -30,8 +30,8 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// Read(u64) -> (u64, buffer<bytes, 6>)
public ResultCode Read(ServiceCtx context)
{
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
long offset = context.RequestData.ReadInt64();
@@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
Result result = _base.Read(out long bytesRead, offset, data);
- context.Memory.Write((ulong)position, data);
+ context.Memory.Write(position, data);
context.ResponseData.Write(bytesRead);
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs
index f5a6fae0..61b1f1a1 100644
--- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs
@@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
Result = 0
};
- long dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress);
+ ulong dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress);
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(dcpSize);
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
@@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
return ResultCode.Success;
}
- private long WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress)
+ private ulong WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress)
{
return MemoryHelper.Write(context.Memory, ipcDesc.Position, deliveryCacheProgress);
}
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs
index 8e2fb4bf..cac5f170 100644
--- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs
@@ -46,14 +46,14 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>)
public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context)
{
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
+ ulong position = context.Request.ReceiveBuff[0].Position;
+ ulong size = context.Request.ReceiveBuff[0].Size;
byte[] data = new byte[size];
Result result = _base.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(data));
- context.Memory.Write((ulong)position, data);
+ context.Memory.Write(position, data);
context.ResponseData.Write(count);
diff --git a/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
index 37cc9bda..35781562 100644
--- a/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
@@ -1,7 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Caps.Types;
using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.IO;
@@ -19,11 +18,6 @@ namespace Ryujinx.HLE.HOS.Services.Caps
public CaptureManager(Switch device)
{
_sdCardPath = device.FileSystem.GetSdCardPath();
-
- SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
- {
- Quality = 100
- });
}
public ResultCode SetShimLibraryVersion(ServiceCtx context)
diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs
index b907ff35..1789122e 100644
--- a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs
+++ b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs
@@ -26,10 +26,10 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong appletResourceUserId = context.RequestData.ReadUInt64();
ulong pidPlaceholder = context.RequestData.ReadUInt64();
- long screenshotDataPosition = context.Request.SendBuff[0].Position;
- long screenshotDataSize = context.Request.SendBuff[0].Size;
+ ulong screenshotDataPosition = context.Request.SendBuff[0].Position;
+ ulong screenshotDataSize = context.Request.SendBuff[0].Size;
- byte[] screenshotData = context.Memory.GetSpan((ulong)screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
+ byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry);
@@ -49,16 +49,16 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong appletResourceUserId = context.RequestData.ReadUInt64();
ulong pidPlaceholder = context.RequestData.ReadUInt64();
- long applicationDataPosition = context.Request.SendBuff[0].Position;
- long applicationDataSize = context.Request.SendBuff[0].Size;
+ ulong applicationDataPosition = context.Request.SendBuff[0].Position;
+ ulong applicationDataSize = context.Request.SendBuff[0].Size;
- long screenshotDataPosition = context.Request.SendBuff[1].Position;
- long screenshotDataSize = context.Request.SendBuff[1].Size;
+ ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
+ ulong screenshotDataSize = context.Request.SendBuff[1].Size;
// TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now).
- byte[] applicationData = context.Memory.GetSpan((ulong)applicationDataPosition, (int)applicationDataSize).ToArray();
+ byte[] applicationData = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray();
- byte[] screenshotData = context.Memory.GetSpan((ulong)screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
+ byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry);
@@ -77,16 +77,16 @@ namespace Ryujinx.HLE.HOS.Services.Caps
uint unknown = context.RequestData.ReadUInt32();
ulong appletResourceUserId = context.RequestData.ReadUInt64();
- long userIdListPosition = context.Request.SendBuff[0].Position;
- long userIdListSize = context.Request.SendBuff[0].Size;
+ ulong userIdListPosition = context.Request.SendBuff[0].Position;
+ ulong userIdListSize = context.Request.SendBuff[0].Size;
- long screenshotDataPosition = context.Request.SendBuff[1].Position;
- long screenshotDataSize = context.Request.SendBuff[1].Size;
+ ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
+ ulong screenshotDataSize = context.Request.SendBuff[1].Size;
// TODO: Parse the UserIdList.
- byte[] userIdList = context.Memory.GetSpan((ulong)userIdListPosition, (int)userIdListSize).ToArray();
+ byte[] userIdList = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray();
- byte[] screenshotData = context.Memory.GetSpan((ulong)screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
+ byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry);
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
index 1ae5d487..a2865e9c 100644
--- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
@@ -150,12 +150,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
return ResultCode.InvalidArgument;
}
- if (context.Device.System.AccountManager.TryGetUser(userId, out UserProfile profile))
- {
- profile.OnlinePlayState = AccountState.Open;
- }
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString(), profile.OnlinePlayState });
+ context.Device.System.AccountManager.OpenUserOnlinePlay(userId);
+
+ Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
return ResultCode.Success;
}
@@ -171,12 +168,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
return ResultCode.InvalidArgument;
}
- if (context.Device.System.AccountManager.TryGetUser(userId, out UserProfile profile))
- {
- profile.OnlinePlayState = AccountState.Closed;
- }
+ context.Device.System.AccountManager.CloseUserOnlinePlay(userId);
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString(), profile.OnlinePlayState });
+ Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
return ResultCode.Success;
}
@@ -190,12 +184,12 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
// Pid placeholder
context.RequestData.ReadInt64();
- long position = context.Request.PtrBuff[0].Position;
- long size = context.Request.PtrBuff[0].Size;
+ ulong position = context.Request.PtrBuff[0].Position;
+ ulong size = context.Request.PtrBuff[0].Size;
byte[] bufferContent = new byte[size];
- context.Memory.Read((ulong)position, bufferContent);
+ context.Memory.Read(position, bufferContent);
if (uuid.IsNull)
{
@@ -221,9 +215,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
bool unknownBool = context.RequestData.ReadBoolean();
UserId userId = context.RequestData.ReadStruct<UserId>();
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40L);
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40UL);
- long bufferPosition = context.Request.RecvListBuff[0].Position;
+ ulong bufferPosition = context.Request.RecvListBuff[0].Position;
if (userId.IsNull)
{
@@ -271,8 +265,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
*/
- context.Memory.Write((ulong)bufferPosition, playHistoryRegistrationKeyBuffer);
- context.Memory.Write((ulong)bufferPosition + 0x20, new byte[0x20]); // HmacHash
+ context.Memory.Write(bufferPosition, playHistoryRegistrationKeyBuffer);
+ context.Memory.Write(bufferPosition + 0x20, new byte[0x20]); // HmacHash
return ResultCode.Success;
}
@@ -287,14 +281,14 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
context.RequestData.ReadInt64();
long pid = context.Process.Pid;
- long playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position;
- long PlayHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size;
+ ulong playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position;
+ ulong PlayHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size;
- long inAppScreenName1Position = context.Request.PtrBuff[1].Position;
- long inAppScreenName1Size = context.Request.PtrBuff[1].Size;
+ ulong inAppScreenName1Position = context.Request.PtrBuff[1].Position;
+ ulong inAppScreenName1Size = context.Request.PtrBuff[1].Size;
- long inAppScreenName2Position = context.Request.PtrBuff[2].Position;
- long inAppScreenName2Size = context.Request.PtrBuff[2].Size;
+ ulong inAppScreenName2Position = context.Request.PtrBuff[2].Position;
+ ulong inAppScreenName2Size = context.Request.PtrBuff[2].Size;
if (userId.IsNull || inAppScreenName1Size > 0x48 || inAppScreenName2Size > 0x48)
{
diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs
index c4d251f9..7774af23 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs
@@ -116,12 +116,12 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
public static Result ReadFsPath(out FsPath path, ServiceCtx context, int index = 0)
{
- long position = context.Request.PtrBuff[index].Position;
- long size = context.Request.PtrBuff[index].Size;
+ ulong position = context.Request.PtrBuff[index].Position;
+ ulong size = context.Request.PtrBuff[index].Size;
byte[] pathBytes = new byte[size];
- context.Memory.Read((ulong)position, pathBytes);
+ context.Memory.Read(position, pathBytes);
return FsPath.FromSpan(out path, pathBytes);
}
diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs
index 014e11f4..565ddc4c 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs
@@ -18,15 +18,15 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries)
public ResultCode Read(ServiceCtx context)
{
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferLen = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] entriesBytes = new byte[bufferLen];
Span<DirectoryEntry> entries = MemoryMarshal.Cast<byte, DirectoryEntry>(entriesBytes);
Result result = _baseDirectory.Read(out long entriesRead, entries);
- context.Memory.Write((ulong)bufferPosition, entriesBytes);
+ context.Memory.Write(bufferPosition, entriesBytes);
context.ResponseData.Write(entriesRead);
return (ResultCode)result.Value;
diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs
index b5f342f0..681b6c17 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> out_buf)
public ResultCode Read(ServiceCtx context)
{
- long position = context.Request.ReceiveBuff[0].Position;
+ ulong position = context.Request.ReceiveBuff[0].Position;
ReadOption readOption = new ReadOption(context.RequestData.ReadInt32());
context.RequestData.BaseStream.Position += 4;
@@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
Result result = _baseFile.Read(out long bytesRead, offset, data, readOption);
- context.Memory.Write((ulong)position, data);
+ context.Memory.Write(position, data);
context.ResponseData.Write(bytesRead);
@@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// Write(u32 writeOption, u64 offset, u64 size, buffer<u8, 0x45, 0>)
public ResultCode Write(ServiceCtx context)
{
- long position = context.Request.SendBuff[0].Position;
+ ulong position = context.Request.SendBuff[0].Position;
WriteOption writeOption = new WriteOption(context.RequestData.ReadInt32());
context.RequestData.BaseStream.Position += 4;
@@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
byte[] data = new byte[size];
- context.Memory.Read((ulong)position, data);
+ context.Memory.Read(position, data);
return (ResultCode)_baseFile.Write(offset, data, writeOption).Value;
}
diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs
index 7889be4b..89955634 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs
@@ -17,8 +17,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
public ResultCode Read(ServiceCtx context)
{
- long offset = context.RequestData.ReadInt64();
- long size = context.RequestData.ReadInt64();
+ ulong offset = context.RequestData.ReadUInt64();
+ ulong size = context.RequestData.ReadUInt64();
if (context.Request.ReceiveBuff.Count > 0)
{
@@ -32,9 +32,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
byte[] data = new byte[size];
- Result result = _baseStorage.Read(offset, data);
+ Result result = _baseStorage.Read((long)offset, data);
- context.Memory.Write((ulong)buffDesc.Position, data);
+ context.Memory.Write(buffDesc.Position, data);
return (ResultCode)result.Value;
}
diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
index eaaf1fe9..fd8844c7 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
@@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
public ResultCode OpenFileSystemWithId(ServiceCtx context)
{
FileSystemType fileSystemType = (FileSystemType)context.RequestData.ReadInt32();
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
string switchPath = ReadUtf8String(context);
string fullPath = context.Device.FileSystem.SwitchPathToSystemPath(switchPath);
@@ -337,14 +337,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
SaveDataFilter filter = context.RequestData.ReadStruct<SaveDataFilter>();
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferLen = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] infoBuffer = new byte[bufferLen];
Result result = _baseFileSystemProxy.FindSaveDataWithFilter(out long count, infoBuffer, spaceId, ref filter);
- context.Memory.Write((ulong)bufferPosition, infoBuffer);
+ context.Memory.Write(bufferPosition, infoBuffer);
context.ResponseData.Write(count);
return (ResultCode)result.Value;
@@ -392,11 +392,11 @@ namespace Ryujinx.HLE.HOS.Services.Fs
{
StorageId storageId = (StorageId)context.RequestData.ReadByte();
byte[] padding = context.RequestData.ReadBytes(7);
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
// We do a mitm here to find if the request is for an AOC.
// This is because AOC can be distributed over multiple containers in the emulator.
- if (context.Device.System.ContentManager.GetAocDataStorage((ulong)titleId, out LibHac.Fs.IStorage aocStorage))
+ if (context.Device.System.ContentManager.GetAocDataStorage((ulong)titleId, out LibHac.Fs.IStorage aocStorage, context.Device.Configuration.FsIntegrityCheckLevel))
{
Logger.Info?.Print(LogClass.Loader, $"Opened AddOnContent Data TitleID={titleId:X16}");
diff --git a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs
index 7c5d5e61..d6449a2d 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs
@@ -16,14 +16,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs
// ReadSaveDataInfo() -> (u64, buffer<unknown, 6>)
public ResultCode ReadSaveDataInfo(ServiceCtx context)
{
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferLen = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] infoBuffer = new byte[bufferLen];
Result result = _baseReader.Target.Read(out long readCount, infoBuffer);
- context.Memory.Write((ulong)bufferPosition, infoBuffer);
+ context.Memory.Write(bufferPosition, infoBuffer);
context.ResponseData.Write(readCount);
return (ResultCode)result.Value;
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
index 18a7ba11..61a12d9e 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
@@ -3,6 +3,14 @@ using Ryujinx.HLE.Exceptions;
using Ryujinx.Common.Configuration.Hid;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
namespace Ryujinx.HLE.HOS.Services.Hid
{
@@ -12,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private readonly ulong _hidMemoryAddress;
- internal ref HidSharedMemory SharedMemory => ref _device.Memory.GetRef<HidSharedMemory>(_hidMemoryAddress);
+ internal ref SharedMemory SharedMemory => ref _device.Memory.GetRef<SharedMemory>(_hidMemoryAddress);
internal const int SharedMemEntryCount = 17;
@@ -22,40 +30,30 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public KeyboardDevice Keyboard;
public NpadDevices Npads;
- static Hid()
+ private static void CheckTypeSizeOrThrow<T>(int expectedSize)
{
- if (Unsafe.SizeOf<ShMemDebugPad>() != 0x400)
- {
- throw new InvalidStructLayoutException<ShMemDebugPad>(0x400);
- }
- if (Unsafe.SizeOf<ShMemTouchScreen>() != 0x3000)
- {
- throw new InvalidStructLayoutException<ShMemTouchScreen>(0x3000);
- }
- if (Unsafe.SizeOf<ShMemKeyboard>() != 0x400)
- {
- throw new InvalidStructLayoutException<ShMemKeyboard>(0x400);
- }
- if (Unsafe.SizeOf<ShMemMouse>() != 0x400)
- {
- throw new InvalidStructLayoutException<ShMemMouse>(0x400);
- }
- if (Unsafe.SizeOf<ShMemNpad>() != 0x5000)
+ if (Unsafe.SizeOf<T>() != expectedSize)
{
- throw new InvalidStructLayoutException<ShMemNpad>(0x5000);
- }
- if (Unsafe.SizeOf<HidSharedMemory>() != Horizon.HidSize)
- {
- throw new InvalidStructLayoutException<HidSharedMemory>(Horizon.HidSize);
+ throw new InvalidStructLayoutException<T>(expectedSize);
}
}
+ static Hid()
+ {
+ CheckTypeSizeOrThrow<RingLifo<DebugPadState>>(0x2c8);
+ CheckTypeSizeOrThrow<RingLifo<TouchScreenState>>(0x2C38);
+ CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350);
+ CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8);
+ CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000);
+ CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize);
+ }
+
public Hid(in Switch device, ulong sharedHidMemoryAddress)
{
_device = device;
_hidMemoryAddress = sharedHidMemoryAddress;
- device.Memory.ZeroFill(sharedHidMemoryAddress, Horizon.HidSize);
+ SharedMemory = SharedMemory.Create();
}
public void InitDevices()
@@ -67,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
Npads = new NpadDevices(_device, true);
}
- internal void RefreshInputConfig(List<InputConfig> inputConfig)
+ public void RefreshInputConfig(List<InputConfig> inputConfig)
{
ControllerConfig[] npadConfig = new ControllerConfig[inputConfig.Count];
@@ -80,11 +78,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid
_device.Hid.Npads.Configure(npadConfig);
}
- internal void RefreshInputConfigEvent(object _, ReactiveEventArgs<List<InputConfig>> args)
- {
- RefreshInputConfig(args.NewValue);
- }
-
public ControllerKeys UpdateStickButtons(JoystickPosition leftStick, JoystickPosition rightStick)
{
const int stickButtonThreshold = short.MaxValue / 2;
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs
index 59d6dfa3..e8bf628a 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs
@@ -12,18 +12,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid
_device = device;
Active = active;
}
-
- internal static int UpdateEntriesHeader(ref CommonEntriesHeader header, out int previousEntry)
- {
- header.NumEntries = SharedMemEntryCount;
- header.MaxEntryIndex = SharedMemEntryCount - 1;
-
- previousEntry = (int)header.LatestEntry;
- header.LatestEntry = (header.LatestEntry + 1) % SharedMemEntryCount;
-
- header.TimestampTicks = GetTimestampTicks();
-
- return (int)header.LatestEntry; // EntryCount shouldn't overflow int
- }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs
index 7e708e32..e3b95390 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs
@@ -1,3 +1,6 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
+
namespace Ryujinx.HLE.HOS.Services.Hid
{
public class DebugPadDevice : BaseDevice
@@ -6,20 +9,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public void Update()
{
- ref ShMemDebugPad debugPad = ref _device.Hid.SharedMemory.DebugPad;
+ ref RingLifo<DebugPadState> lifo = ref _device.Hid.SharedMemory.DebugPad;
+
+ ref DebugPadState previousEntry = ref lifo.GetCurrentEntryRef();
- int currentIndex = UpdateEntriesHeader(ref debugPad.Header, out int previousIndex);
+ DebugPadState newState = new DebugPadState();
- if (!Active)
+ if (Active)
{
- return;
+ // TODO: This is a debug device only present in dev environment, do we want to support it?
}
- ref DebugPadEntry currentEntry = ref debugPad.Entries[currentIndex];
- DebugPadEntry previousEntry = debugPad.Entries[previousIndex];
+ newState.SamplingNumber = previousEntry.SamplingNumber + 1;
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
+ lifo.Write(ref newState);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs
index e8ed6a3e..99dc078d 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs
@@ -1,3 +1,7 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
+using System;
+
namespace Ryujinx.HLE.HOS.Services.Hid
{
public class KeyboardDevice : BaseDevice
@@ -6,27 +10,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public unsafe void Update(KeyboardInput keyState)
{
- ref ShMemKeyboard keyboard = ref _device.Hid.SharedMemory.Keyboard;
-
- int currentIndex = UpdateEntriesHeader(ref keyboard.Header, out int previousIndex);
+ ref RingLifo<KeyboardState> lifo = ref _device.Hid.SharedMemory.Keyboard;
if (!Active)
{
+ lifo.Clear();
+
return;
}
- ref KeyboardState currentEntry = ref keyboard.Entries[currentIndex];
- KeyboardState previousEntry = keyboard.Entries[previousIndex];
+ ref KeyboardState previousEntry = ref lifo.GetCurrentEntryRef();
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
-
- for (int i = 0; i < 8; ++i)
+ KeyboardState newState = new KeyboardState
{
- currentEntry.Keys[i] = (uint)keyState.Keys[i];
- }
+ SamplingNumber = previousEntry.SamplingNumber + 1,
+ };
+
+ keyState.Keys.AsSpan().CopyTo(newState.Keys.RawData.ToSpan());
+ newState.Modifiers = (KeyboardModifier)keyState.Modifier;
- currentEntry.Modifier = (ulong)keyState.Modifier;
+ lifo.Write(ref newState);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs
index ee58a563..e07c1d20 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs
@@ -1,37 +1,35 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
+
namespace Ryujinx.HLE.HOS.Services.Hid
{
public class MouseDevice : BaseDevice
{
public MouseDevice(Switch device, bool active) : base(device, active) { }
- public void Update(int mouseX, int mouseY, int buttons = 0, int scrollX = 0, int scrollY = 0)
+ public void Update(int mouseX, int mouseY, uint buttons = 0, int scrollX = 0, int scrollY = 0)
{
- ref ShMemMouse mouse = ref _device.Hid.SharedMemory.Mouse;
+ ref RingLifo<MouseState> lifo = ref _device.Hid.SharedMemory.Mouse;
- int currentIndex = UpdateEntriesHeader(ref mouse.Header, out int previousIndex);
+ ref MouseState previousEntry = ref lifo.GetCurrentEntryRef();
+
+ MouseState newState = new MouseState()
+ {
+ SamplingNumber = previousEntry.SamplingNumber + 1,
+ };
- if (!Active)
+ if (Active)
{
- return;
+ newState.Buttons = (MouseButton)buttons;
+ newState.X = mouseX;
+ newState.Y = mouseY;
+ newState.DeltaX = mouseX - previousEntry.DeltaX;
+ newState.DeltaY = mouseY - previousEntry.DeltaY;
+ newState.WheelDeltaX = scrollX;
+ newState.WheelDeltaY = scrollY;
}
- ref MouseState currentEntry = ref mouse.Entries[currentIndex];
- MouseState previousEntry = mouse.Entries[previousIndex];
-
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
-
- currentEntry.Buttons = (ulong)buttons;
-
- currentEntry.Position = new MousePosition
- {
- X = mouseX,
- Y = mouseY,
- VelocityX = mouseX - previousEntry.Position.X,
- VelocityY = mouseY - previousEntry.Position.Y,
- ScrollVelocityX = scrollX,
- ScrollVelocityY = scrollY
- };
+ lifo.Write(ref newState);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
index 2150f278..3ff7e733 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
@@ -1,16 +1,17 @@
using System;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
-using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Hid.Types;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
namespace Ryujinx.HLE.HOS.Services.Hid
{
public class NpadDevices : BaseDevice
{
- private const BatteryCharge DefaultBatteryCharge = BatteryCharge.Percent100;
-
private const int NoMatchNotifyFrequencyMs = 2000;
private int _activeCount;
private long _lastNotifyTimestamp;
@@ -86,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
continue;
}
- ControllerType currentType = _device.Hid.SharedMemory.Npads[i].Header.Type;
+ ControllerType currentType = (ControllerType)_device.Hid.SharedMemory.Npads[i].InternalState.StyleSet;
if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i])
{
@@ -135,12 +136,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{
Remap();
- UpdateAllEntries();
+ Span<bool> updated = stackalloc bool[10];
// Update configured inputs
for (int i = 0; i < states.Count; ++i)
{
- UpdateInput(states[i]);
+ GamepadInput state = states[i];
+
+ updated[(int)state.PlayerId] = true;
+
+ UpdateInput(state);
+ }
+
+ for (int i = 0; i < updated.Length; i++)
+ {
+ if (!updated[i])
+ {
+ UpdateDisconnectedInput((PlayerIndex)i);
+ }
}
}
@@ -185,16 +198,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private void SetupNpad(PlayerIndex player, ControllerType type)
{
- ref ShMemNpad controller = ref _device.Hid.SharedMemory.Npads[(int)player];
+ ref NpadInternalState controller = ref _device.Hid.SharedMemory.Npads[(int)player].InternalState;
- ControllerType oldType = controller.Header.Type;
+ ControllerType oldType = (ControllerType)controller.StyleSet;
if (oldType == type)
{
return; // Already configured
}
- controller = new ShMemNpad(); // Zero it
+ controller = NpadInternalState.Create(); // Reset it
if (type == ControllerType.None)
{
@@ -207,87 +220,151 @@ namespace Ryujinx.HLE.HOS.Services.Hid
}
// TODO: Allow customizing colors at config
- NpadStateHeader defaultHeader = new NpadStateHeader
- {
- IsHalf = false,
- SingleColorBody = NpadColor.BodyGray,
- SingleColorButtons = NpadColor.ButtonGray,
- LeftColorBody = NpadColor.BodyNeonBlue,
- LeftColorButtons = NpadColor.ButtonGray,
- RightColorBody = NpadColor.BodyNeonRed,
- RightColorButtons = NpadColor.ButtonGray
- };
-
- controller.SystemProperties = NpadSystemProperties.PowerInfo0Connected |
- NpadSystemProperties.PowerInfo1Connected |
- NpadSystemProperties.PowerInfo2Connected;
-
- controller.BatteryState.ToSpan().Fill(DefaultBatteryCharge);
+ controller.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
+ controller.FullKeyColor.FullKeyBody = (uint)NpadColor.BodyGray;
+ controller.FullKeyColor.FullKeyButtons = (uint)NpadColor.ButtonGray;
+ controller.JoyColor.LeftBody = (uint)NpadColor.BodyNeonBlue;
+ controller.JoyColor.LeftButtons = (uint)NpadColor.ButtonGray;
+ controller.JoyColor.RightBody = (uint)NpadColor.BodyNeonRed;
+ controller.JoyColor.RightButtons = (uint)NpadColor.ButtonGray;
+
+ controller.SystemProperties = NpadSystemProperties.IsPoweredJoyDual |
+ NpadSystemProperties.IsPoweredJoyLeft |
+ NpadSystemProperties.IsPoweredJoyRight;
+
+ controller.BatteryLevelJoyDual = NpadBatteryLevel.Percent100;
+ controller.BatteryLevelJoyLeft = NpadBatteryLevel.Percent100;
+ controller.BatteryLevelJoyRight = NpadBatteryLevel.Percent100;
switch (type)
{
case ControllerType.ProController:
- defaultHeader.Type = ControllerType.ProController;
+ controller.StyleSet = NpadStyleTag.FullKey;
controller.DeviceType = DeviceType.FullKey;
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
- NpadSystemProperties.PlusButtonCapability |
- NpadSystemProperties.MinusButtonCapability;
+ controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
+ NpadSystemProperties.IsPlusAvailable |
+ NpadSystemProperties.IsMinusAvailable;
break;
case ControllerType.Handheld:
- defaultHeader.Type = ControllerType.Handheld;
+ controller.StyleSet = NpadStyleTag.Handheld;
controller.DeviceType = DeviceType.HandheldLeft |
DeviceType.HandheldRight;
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
- NpadSystemProperties.PlusButtonCapability |
- NpadSystemProperties.MinusButtonCapability;
+ controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
+ NpadSystemProperties.IsPlusAvailable |
+ NpadSystemProperties.IsMinusAvailable;
break;
case ControllerType.JoyconPair:
- defaultHeader.Type = ControllerType.JoyconPair;
+ controller.StyleSet = NpadStyleTag.JoyDual;
controller.DeviceType = DeviceType.JoyLeft |
DeviceType.JoyRight;
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
- NpadSystemProperties.PlusButtonCapability |
- NpadSystemProperties.MinusButtonCapability;
+ controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
+ NpadSystemProperties.IsPlusAvailable |
+ NpadSystemProperties.IsMinusAvailable;
break;
case ControllerType.JoyconLeft:
- defaultHeader.Type = ControllerType.JoyconLeft;
- defaultHeader.IsHalf = true;
+ controller.StyleSet = NpadStyleTag.JoyLeft;
+ controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
controller.DeviceType = DeviceType.JoyLeft;
- controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
- NpadSystemProperties.MinusButtonCapability;
+ controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
+ NpadSystemProperties.IsMinusAvailable;
break;
case ControllerType.JoyconRight:
- defaultHeader.Type = ControllerType.JoyconRight;
- defaultHeader.IsHalf = true;
+ controller.StyleSet = NpadStyleTag.JoyRight;
+ controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
controller.DeviceType = DeviceType.JoyRight;
- controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
- NpadSystemProperties.PlusButtonCapability;
+ controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
+ NpadSystemProperties.IsPlusAvailable;
break;
case ControllerType.Pokeball:
- defaultHeader.Type = ControllerType.Pokeball;
+ controller.StyleSet = NpadStyleTag.Palma;
controller.DeviceType = DeviceType.Palma;
break;
}
- controller.Header = defaultHeader;
-
_styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
_activeCount++;
Logger.Info?.Print(LogClass.Hid, $"Connected Controller {type} to {player}");
}
- private static NpadLayoutsIndex ControllerTypeToNpadLayout(ControllerType controllerType)
- => controllerType switch
+ private ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad)
+ {
+ switch (npad.StyleSet)
+ {
+ case NpadStyleTag.FullKey:
+ return ref npad.FullKey;
+ case NpadStyleTag.Handheld:
+ return ref npad.Handheld;
+ case NpadStyleTag.JoyDual:
+ return ref npad.JoyDual;
+ case NpadStyleTag.JoyLeft:
+ return ref npad.JoyLeft;
+ case NpadStyleTag.JoyRight:
+ return ref npad.JoyRight;
+ case NpadStyleTag.Palma:
+ return ref npad.Palma;
+ default:
+ return ref npad.SystemExt;
+ }
+ }
+
+ private void UpdateUnusedInputIfNotEqual(ref RingLifo<NpadCommonState> currentlyUsed, ref RingLifo<NpadCommonState> possiblyUnused)
+ {
+ bool isEquals;
+
+ unsafe
+ {
+ var aPointer = Unsafe.AsPointer(ref currentlyUsed);
+ var bPointer = Unsafe.AsPointer(ref possiblyUnused);
+
+ isEquals = aPointer == bPointer;
+ }
+
+ if (!isEquals)
+ {
+ NpadCommonState newState = new NpadCommonState();
+
+ WriteNewInputEntry(ref possiblyUnused, ref newState);
+ }
+ }
+
+ private void WriteNewInputEntry(ref RingLifo<NpadCommonState> lifo, ref NpadCommonState state)
+ {
+ ref NpadCommonState previousEntry = ref lifo.GetCurrentEntryRef();
+
+ state.SamplingNumber = previousEntry.SamplingNumber + 1;
+
+ lifo.Write(ref state);
+ }
+
+ private void UpdateUnusedSixInputIfNotEqual(ref RingLifo<SixAxisSensorState> currentlyUsed, ref RingLifo<SixAxisSensorState> possiblyUnused)
{
- ControllerType.ProController => NpadLayoutsIndex.ProController,
- ControllerType.Handheld => NpadLayoutsIndex.Handheld,
- ControllerType.JoyconPair => NpadLayoutsIndex.JoyDual,
- ControllerType.JoyconLeft => NpadLayoutsIndex.JoyLeft,
- ControllerType.JoyconRight => NpadLayoutsIndex.JoyRight,
- ControllerType.Pokeball => NpadLayoutsIndex.Pokeball,
- _ => NpadLayoutsIndex.SystemExternal
- };
+ bool isEquals;
+
+ unsafe
+ {
+ var aPointer = Unsafe.AsPointer(ref currentlyUsed);
+ var bPointer = Unsafe.AsPointer(ref possiblyUnused);
+
+ isEquals = aPointer == bPointer;
+ }
+
+ if (!isEquals)
+ {
+ SixAxisSensorState newState = new SixAxisSensorState();
+
+ WriteNewSixInputEntry(ref possiblyUnused, ref newState);
+ }
+ }
+
+ private void WriteNewSixInputEntry(ref RingLifo<SixAxisSensorState> lifo, ref SixAxisSensorState state)
+ {
+ ref SixAxisSensorState previousEntry = ref lifo.GetCurrentEntryRef();
+
+ state.SamplingNumber = previousEntry.SamplingNumber + 1;
+
+ lifo.Write(ref state);
+ }
private void UpdateInput(GamepadInput state)
{
@@ -296,43 +373,88 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return;
}
- ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId];
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
- if (currentNpad.Header.Type == ControllerType.None)
+ if (currentNpad.StyleSet == NpadStyleTag.None)
{
return;
}
- ref NpadLayout currentLayout = ref currentNpad.Layouts[(int)ControllerTypeToNpadLayout(currentNpad.Header.Type)];
- ref NpadState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
+ ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
+
+ NpadCommonState newState = new NpadCommonState
+ {
+ Buttons = (NpadButton)state.Buttons,
+ AnalogStickL = new AnalogStickState
+ {
+ X = state.LStick.Dx,
+ Y = state.LStick.Dy,
+ },
+ AnalogStickR = new AnalogStickState
+ {
+ X = state.RStick.Dx,
+ Y = state.RStick.Dy,
+ }
+ };
+
+ newState.Attributes = NpadAttribute.IsConnected;
+
+ switch (currentNpad.StyleSet)
+ {
+ case NpadStyleTag.Handheld:
+ case NpadStyleTag.FullKey:
+ newState.Attributes |= NpadAttribute.IsWired;
+ break;
+ case NpadStyleTag.JoyDual:
+ newState.Attributes |= NpadAttribute.IsLeftConnected |
+ NpadAttribute.IsRightConnected;
+ break;
+ case NpadStyleTag.JoyLeft:
+ newState.Attributes |= NpadAttribute.IsLeftConnected;
+ break;
+ case NpadStyleTag.JoyRight:
+ newState.Attributes |= NpadAttribute.IsRightConnected;
+ break;
+ }
- currentEntry.Buttons = state.Buttons;
- currentEntry.LStickX = state.LStick.Dx;
- currentEntry.LStickY = state.LStick.Dy;
- currentEntry.RStickX = state.RStick.Dx;
- currentEntry.RStickY = state.RStick.Dy;
+ WriteNewInputEntry(ref lifo, ref newState);
// Mirror data to Default layout just in case
- ref NpadLayout mainLayout = ref currentNpad.Layouts[(int)NpadLayoutsIndex.SystemExternal];
- mainLayout.Entries[(int)mainLayout.Header.LatestEntry] = currentEntry;
+ if (!currentNpad.StyleSet.HasFlag(NpadStyleTag.SystemExt))
+ {
+ WriteNewInputEntry(ref currentNpad.SystemExt, ref newState);
+ }
+
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.FullKey);
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Handheld);
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyDual);
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyLeft);
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyRight);
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Palma);
}
- private static SixAxixLayoutsIndex ControllerTypeToSixAxisLayout(ControllerType controllerType)
- => controllerType switch
+ private void UpdateDisconnectedInput(PlayerIndex index)
{
- ControllerType.ProController => SixAxixLayoutsIndex.ProController,
- ControllerType.Handheld => SixAxixLayoutsIndex.Handheld,
- ControllerType.JoyconPair => SixAxixLayoutsIndex.JoyDualLeft,
- ControllerType.JoyconLeft => SixAxixLayoutsIndex.JoyLeft,
- ControllerType.JoyconRight => SixAxixLayoutsIndex.JoyRight,
- ControllerType.Pokeball => SixAxixLayoutsIndex.Pokeball,
- _ => SixAxixLayoutsIndex.SystemExternal
- };
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
+
+ NpadCommonState newState = new NpadCommonState();
+
+ WriteNewInputEntry(ref currentNpad.FullKey, ref newState);
+ WriteNewInputEntry(ref currentNpad.Handheld, ref newState);
+ WriteNewInputEntry(ref currentNpad.JoyDual, ref newState);
+ WriteNewInputEntry(ref currentNpad.JoyLeft, ref newState);
+ WriteNewInputEntry(ref currentNpad.JoyRight, ref newState);
+ WriteNewInputEntry(ref currentNpad.Palma, ref newState);
+ }
public void UpdateSixAxis(IList<SixAxisInput> states)
{
+ Span<bool> updated = stackalloc bool[10];
+
for (int i = 0; i < states.Count; ++i)
{
+ updated[(int)states[i].PlayerId] = true;
+
if (SetSixAxisState(states[i]))
{
i++;
@@ -345,6 +467,40 @@ namespace Ryujinx.HLE.HOS.Services.Hid
SetSixAxisState(states[i], true);
}
}
+
+ for (int i = 0; i < updated.Length; i++)
+ {
+ if (!updated[i])
+ {
+ UpdateDisconnectedInputSixAxis((PlayerIndex)i);
+ }
+ }
+ }
+
+ private ref RingLifo<SixAxisSensorState> GetSixAxisSensorLifo(ref NpadInternalState npad, bool isRightPair)
+ {
+ switch (npad.StyleSet)
+ {
+ case NpadStyleTag.FullKey:
+ return ref npad.FullKeySixAxisSensor;
+ case NpadStyleTag.Handheld:
+ return ref npad.HandheldSixAxisSensor;
+ case NpadStyleTag.JoyDual:
+ if (isRightPair)
+ {
+ return ref npad.JoyDualRightSixAxisSensor;
+ }
+ else
+ {
+ return ref npad.JoyDualSixAxisSensor;
+ }
+ case NpadStyleTag.JoyLeft:
+ return ref npad.JoyLeftSixAxisSensor;
+ case NpadStyleTag.JoyRight:
+ return ref npad.JoyRightSixAxisSensor;
+ default:
+ throw new NotImplementedException($"{npad.StyleSet}");
+ }
}
private bool SetSixAxisState(SixAxisInput state, bool isRightPair = false)
@@ -354,9 +510,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return false;
}
- ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId];
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
- if (currentNpad.Header.Type == ControllerType.None)
+ if (currentNpad.StyleSet == NpadStyleTag.None)
{
return false;
}
@@ -382,87 +538,57 @@ namespace Ryujinx.HLE.HOS.Services.Hid
Z = state.Rotation.Z
};
- ref NpadSixAxis currentLayout = ref currentNpad.Sixaxis[(int)ControllerTypeToSixAxisLayout(currentNpad.Header.Type) + (isRightPair ? 1 : 0)];
- ref SixAxisState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
+ SixAxisSensorState newState = new SixAxisSensorState
+ {
+ Acceleration = accel,
+ AngularVelocity = gyro,
+ Angle = rotation,
+ Attributes = SixAxisSensorAttribute.IsConnected
+ };
- int previousEntryIndex = (int)(currentLayout.Header.LatestEntry == 0 ?
- currentLayout.Header.MaxEntryIndex : currentLayout.Header.LatestEntry - 1);
+ state.Orientation.AsSpan().CopyTo(newState.Direction.ToSpan());
- ref SixAxisState previousEntry = ref currentLayout.Entries[previousEntryIndex];
+ ref RingLifo<SixAxisSensorState> lifo = ref GetSixAxisSensorLifo(ref currentNpad, isRightPair);
- currentEntry.Accelerometer = accel;
- currentEntry.Gyroscope = gyro;
- currentEntry.Rotations = rotation;
+ WriteNewSixInputEntry(ref lifo, ref newState);
- unsafe
+ bool needUpdateRight = currentNpad.StyleSet == NpadStyleTag.JoyDual && !isRightPair;
+
+ if (!isRightPair)
{
- for (int i = 0; i < 9; i++)
- {
- currentEntry.Orientation[i] = state.Orientation[i];
- }
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.FullKeySixAxisSensor);
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.HandheldSixAxisSensor);
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyDualSixAxisSensor);
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyLeftSixAxisSensor);
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyRightSixAxisSensor);
}
- return currentNpad.Header.Type == ControllerType.JoyconPair && !isRightPair;
- }
-
- private void UpdateAllEntries()
- {
- ref Array10<ShMemNpad> controllers = ref _device.Hid.SharedMemory.Npads;
- for (int i = 0; i < controllers.Length; ++i)
+ if (!needUpdateRight)
{
- ref Array7<NpadLayout> layouts = ref controllers[i].Layouts;
- for (int l = 0; l < layouts.Length; ++l)
- {
- ref NpadLayout currentLayout = ref layouts[l];
- int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex);
+ SixAxisSensorState emptyState = new SixAxisSensorState();
- ref NpadState currentEntry = ref currentLayout.Entries[currentIndex];
- NpadState previousEntry = currentLayout.Entries[previousIndex];
+ emptyState.Attributes = SixAxisSensorAttribute.IsConnected;
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
-
- if (controllers[i].Header.Type == ControllerType.None)
- {
- continue;
- }
-
- currentEntry.ConnectionState = NpadConnectionState.ControllerStateConnected;
+ WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState);
+ }
- switch (controllers[i].Header.Type)
- {
- case ControllerType.Handheld:
- case ControllerType.ProController:
- currentEntry.ConnectionState |= NpadConnectionState.ControllerStateWired;
- break;
- case ControllerType.JoyconPair:
- currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected |
- NpadConnectionState.JoyRightConnected;
- break;
- case ControllerType.JoyconLeft:
- currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected;
- break;
- case ControllerType.JoyconRight:
- currentEntry.ConnectionState |= NpadConnectionState.JoyRightConnected;
- break;
- }
- }
+ return needUpdateRight;
+ }
- ref Array6<NpadSixAxis> sixaxis = ref controllers[i].Sixaxis;
- for (int l = 0; l < sixaxis.Length; ++l)
- {
- ref NpadSixAxis currentLayout = ref sixaxis[l];
- int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex);
+ private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
+ {
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
- ref SixAxisState currentEntry = ref currentLayout.Entries[currentIndex];
- SixAxisState previousEntry = currentLayout.Entries[previousIndex];
+ SixAxisSensorState newState = new SixAxisSensorState();
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
+ newState.Attributes = SixAxisSensorAttribute.IsConnected;
- currentEntry._unknown2 = 1;
- }
- }
+ WriteNewSixInputEntry(ref currentNpad.FullKeySixAxisSensor, ref newState);
+ WriteNewSixInputEntry(ref currentNpad.HandheldSixAxisSensor, ref newState);
+ WriteNewSixInputEntry(ref currentNpad.JoyDualSixAxisSensor, ref newState);
+ WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref newState);
+ WriteNewSixInputEntry(ref currentNpad.JoyLeftSixAxisSensor, ref newState);
+ WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs
index 10c34453..432a37e3 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs
@@ -1,3 +1,5 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
using System;
namespace Ryujinx.HLE.HOS.Services.Hid
@@ -8,39 +10,38 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public void Update(params TouchPoint[] points)
{
- ref ShMemTouchScreen touchscreen = ref _device.Hid.SharedMemory.TouchScreen;
+ ref RingLifo<TouchScreenState> lifo = ref _device.Hid.SharedMemory.TouchScreen;
- int currentIndex = UpdateEntriesHeader(ref touchscreen.Header, out int previousIndex);
+ ref TouchScreenState previousEntry = ref lifo.GetCurrentEntryRef();
- if (!Active)
+ TouchScreenState newState = new TouchScreenState
{
- return;
- }
-
- ref TouchScreenState currentEntry = ref touchscreen.Entries[currentIndex];
- TouchScreenState previousEntry = touchscreen.Entries[previousIndex];
-
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
+ SamplingNumber = previousEntry.SamplingNumber + 1
+ };
- currentEntry.NumTouches = (ulong)points.Length;
+ if (Active)
+ {
+ newState.TouchesCount = points.Length;
- int pointsLength = Math.Min(points.Length, currentEntry.Touches.Length);
+ int pointsLength = Math.Min(points.Length, newState.Touches.Length);
- for (int i = 0; i < pointsLength; ++i)
- {
- TouchPoint pi = points[i];
- currentEntry.Touches[i] = new TouchScreenStateData
+ for (int i = 0; i < pointsLength; ++i)
{
- SampleTimestamp = currentEntry.SampleTimestamp,
- X = pi.X,
- Y = pi.Y,
- TouchIndex = (uint)i,
- DiameterX = pi.DiameterX,
- DiameterY = pi.DiameterY,
- Angle = pi.Angle
- };
+ TouchPoint pi = points[i];
+ newState.Touches[i] = new TouchState
+ {
+ DeltaTime = newState.SamplingNumber,
+ X = pi.X,
+ Y = pi.Y,
+ FingerId = (uint)i,
+ DiameterX = pi.DiameterX,
+ DiameterY = pi.DiameterY,
+ RotationAngle = pi.Angle
+ };
+ }
}
+
+ lifo.Write(ref newState);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs
index 26681270..be6857fb 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs
@@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public struct KeyboardInput
{
public int Modifier;
- public int[] Keys;
+ public ulong[] Keys;
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
index e45e695f..e3c22edf 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
@@ -3,7 +3,10 @@ using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
+using Ryujinx.HLE.HOS.Services.Hid.Types;
using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Hid
{
@@ -69,6 +72,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{
long appletResourceUserId = context.RequestData.ReadInt64();
+ // Initialize entries to avoid issues with some games.
+
+ for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
+ {
+ context.Device.Hid.DebugPad.Update();
+ }
+
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
return ResultCode.Success;
@@ -82,6 +92,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid
context.Device.Hid.Touchscreen.Active = true;
+ // Initialize entries to avoid issues with some games.
+
+ for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
+ {
+ context.Device.Hid.Touchscreen.Update();
+ }
+
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
return ResultCode.Success;
@@ -95,6 +112,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid
context.Device.Hid.Mouse.Active = true;
+ // Initialize entries to avoid issues with some games.
+
+ for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
+ {
+ context.Device.Hid.Mouse.Update(0, 0);
+ }
+
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
return ResultCode.Success;
@@ -108,6 +132,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid
context.Device.Hid.Keyboard.Active = true;
+ // Initialize entries to avoid issues with some games.
+
+ KeyboardInput emptyInput = new KeyboardInput();
+ emptyInput.Keys = new ulong[4];
+
+ for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
+ {
+ context.Device.Hid.Keyboard.Update(emptyInput);
+ }
+
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
return ResultCode.Success;
@@ -590,25 +624,22 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public ResultCode SetSupportedNpadIdType(ServiceCtx context)
{
long appletResourceUserId = context.RequestData.ReadInt64();
- long arraySize = context.Request.PtrBuff[0].Size / 4;
+ ulong arrayPosition = context.Request.PtrBuff[0].Position;
+ ulong arraySize = context.Request.PtrBuff[0].Size;
- NpadIdType[] supportedPlayerIds = new NpadIdType[arraySize];
+ ReadOnlySpan<NpadIdType> supportedPlayerIds = MemoryMarshal.Cast<byte, NpadIdType>(context.Memory.GetSpan(arrayPosition, (int)arraySize));
context.Device.Hid.Npads.ClearSupportedPlayers();
- for (int i = 0; i < arraySize; ++i)
+ for (int i = 0; i < supportedPlayerIds.Length; ++i)
{
- NpadIdType id = context.Memory.Read<NpadIdType>((ulong)(context.Request.PtrBuff[0].Position + i * 4));
-
- if (id >= 0)
+ if (supportedPlayerIds[i] >= 0)
{
- context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(id));
+ context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i]));
}
-
- supportedPlayerIds[i] = id;
}
- Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{arraySize} " + string.Join(",", supportedPlayerIds));
+ Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{supportedPlayerIds.Length} " + string.Join(",", supportedPlayerIds.ToArray()));
return ResultCode.Success;
}
@@ -620,6 +651,32 @@ namespace Ryujinx.HLE.HOS.Services.Hid
long appletResourceUserId = context.RequestData.ReadInt64();
context.Device.Hid.Npads.Active = true;
+
+ // Initialize entries to avoid issues with some games.
+
+ List<GamepadInput> emptyGamepadInputs = new List<GamepadInput>();
+ List<SixAxisInput> emptySixAxisInputs = new List<SixAxisInput>();
+
+ for (int player = 0; player < NpadDevices.MaxControllers; player++)
+ {
+ GamepadInput gamepadInput = new GamepadInput();
+ SixAxisInput sixaxisInput = new SixAxisInput();
+
+ gamepadInput.PlayerId = (PlayerIndex)player;
+ sixaxisInput.PlayerId = (PlayerIndex)player;
+
+ sixaxisInput.Orientation = new float[9];
+
+ emptyGamepadInputs.Add(gamepadInput);
+ emptySixAxisInputs.Add(sixaxisInput);
+ }
+
+ for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
+ {
+ context.Device.Hid.Npads.Update(emptyGamepadInputs);
+ context.Device.Hid.Npads.UpdateSixAxis(emptySixAxisInputs);
+ }
+
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
return ResultCode.Success;
@@ -692,6 +749,31 @@ namespace Ryujinx.HLE.HOS.Services.Hid
int revision = context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
+ // Initialize entries to avoid issues with some games.
+
+ List<GamepadInput> emptyGamepadInputs = new List<GamepadInput>();
+ List<SixAxisInput> emptySixAxisInputs = new List<SixAxisInput>();
+
+ for (int player = 0; player < NpadDevices.MaxControllers; player++)
+ {
+ GamepadInput gamepadInput = new GamepadInput();
+ SixAxisInput sixaxisInput = new SixAxisInput();
+
+ gamepadInput.PlayerId = (PlayerIndex)player;
+ sixaxisInput.PlayerId = (PlayerIndex)player;
+
+ sixaxisInput.Orientation = new float[9];
+
+ emptyGamepadInputs.Add(gamepadInput);
+ emptySixAxisInputs.Add(sixaxisInput);
+ }
+
+ for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
+ {
+ context.Device.Hid.Npads.Update(emptyGamepadInputs);
+ context.Device.Hid.Npads.UpdateSixAxis(emptySixAxisInputs);
+ }
+
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, revision });
return ResultCode.Success;
@@ -1007,11 +1089,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
byte[] vibrationDeviceHandleBuffer = new byte[context.Request.PtrBuff[0].Size];
- context.Memory.Read((ulong)context.Request.PtrBuff[0].Position, vibrationDeviceHandleBuffer);
+ context.Memory.Read(context.Request.PtrBuff[0].Position, vibrationDeviceHandleBuffer);
byte[] vibrationValueBuffer = new byte[context.Request.PtrBuff[1].Size];
- context.Memory.Read((ulong)context.Request.PtrBuff[1].Position, vibrationValueBuffer);
+ context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer);
// TODO: Read all handles and values from buffer.
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Boolean32.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Boolean32.cs
deleted file mode 100644
index 5a8d51c6..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/Boolean32.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct Boolean32
- {
- private uint _value;
- public static implicit operator bool(Boolean32 value) => (value._value & 1) != 0;
- public static implicit operator Boolean32(bool value) => new Boolean32() { _value = value ? 1u : 0u };
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/HidVector.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs
index b41bcb2e..18d9fd9c 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/HidVector.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
+namespace Ryujinx.HLE.HOS.Services.Hid.Types
{
struct HidVector
{
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs
index 57b4b366..3c311e21 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs
@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid
{
- public enum NpadColor : int
+ public enum NpadColor : uint
{
BodyGray = 0x828282,
BodyNeonRed = 0xFF3C28,
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs
index 5f6a68cb..1abd8468 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs
@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid
{
- public enum NpadIdType
+ public enum NpadIdType : int
{
Player1 = 0,
Player2 = 1,
@@ -13,4 +13,4 @@
Unknown = 16,
Handheld = 32
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadJoyHoldType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs
index a6f29760..d3b51a24 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadJoyHoldType.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs
@@ -1,8 +1,8 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
+namespace Ryujinx.HLE.HOS.Services.Hid.Types
{
enum NpadJoyHoldType
{
Vertical,
Horizontal
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/CommonEntriesHeader.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/CommonEntriesHeader.cs
deleted file mode 100644
index f83fdcdf..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/CommonEntriesHeader.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct CommonEntriesHeader
- {
- public ulong TimestampTicks;
- public ulong NumEntries;
- public ulong LatestEntry;
- public ulong MaxEntryIndex;
- }
-}
-
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs
deleted file mode 100644
index 3fbaa304..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- unsafe struct ShMemDebugPad
- {
- public CommonEntriesHeader Header;
- public Array17<DebugPadEntry> Entries;
- fixed byte _padding[0x138];
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPadEntry.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPadEntry.cs
deleted file mode 100644
index 3089fc5b..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPadEntry.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- unsafe struct DebugPadEntry
- {
- public ulong SampleTimestamp;
- public ulong SampleTimestamp2;
- fixed byte _unknown[0x18];
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs
deleted file mode 100644
index d950425d..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- // TODO: Add missing structs
- unsafe struct HidSharedMemory
- {
- public ShMemDebugPad DebugPad;
- public ShMemTouchScreen TouchScreen;
- public ShMemMouse Mouse;
- public ShMemKeyboard Keyboard;
- public fixed byte BasicXpad[0x4 * 0x400];
- public fixed byte HomeButton[0x200];
- public fixed byte SleepButton[0x200];
- public fixed byte CaptureButton[0x200];
- public fixed byte InputDetector[0x10 * 0x80];
- public fixed byte UniquePad[0x10 * 0x400];
- public Array10<ShMemNpad> Npads;
- public fixed byte Gesture[0x800];
- public fixed byte ConsoleSixAxisSensor[0x20];
- fixed byte _padding[0x3de0];
- }
-}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs
deleted file mode 100644
index e2c1844f..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- unsafe struct ShMemKeyboard
- {
- public CommonEntriesHeader Header;
- public Array17<KeyboardState> Entries;
- fixed byte _padding[0x28];
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/KeyboardState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/KeyboardState.cs
deleted file mode 100644
index 1f54a4fd..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/KeyboardState.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- unsafe struct KeyboardState
- {
- public ulong SampleTimestamp;
- public ulong SampleTimestamp2;
- public ulong Modifier;
- public fixed uint Keys[8];
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs
deleted file mode 100644
index 6b99e04a..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- unsafe struct ShMemMouse
- {
- public CommonEntriesHeader Header;
- public Array17<MouseState> Entries;
- fixed byte _padding[0xB0];
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MousePosition.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MousePosition.cs
deleted file mode 100644
index e94c9e0c..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MousePosition.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct MousePosition
- {
- public int X;
- public int Y;
- public int VelocityX;
- public int VelocityY;
- public int ScrollVelocityX;
- public int ScrollVelocityY;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MouseState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MouseState.cs
deleted file mode 100644
index 7856b09d..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MouseState.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct MouseState
- {
- public ulong SampleTimestamp;
- public ulong SampleTimestamp2;
- public MousePosition Position;
- public ulong Buttons;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/BatterCharge.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/BatterCharge.cs
deleted file mode 100644
index b94ab172..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/BatterCharge.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- enum BatteryCharge : int
- {
- Percent0 = 0,
- Percent25 = 1,
- Percent50 = 2,
- Percent75 = 3,
- Percent100 = 4
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/DeviceType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/DeviceType.cs
deleted file mode 100644
index f6d7b783..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/DeviceType.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- [Flags]
- enum DeviceType : int
- {
- FullKey = 1 << 0,
- DebugPad = 1 << 1,
- HandheldLeft = 1 << 2,
- HandheldRight = 1 << 3,
- JoyLeft = 1 << 4,
- JoyRight = 1 << 5,
- Palma = 1 << 6, // Poké Ball Plus
- FamicomLeft = 1 << 7,
- FamicomRight = 1 << 8,
- NESLeft = 1 << 9,
- NESRight = 1 << 10,
- HandheldFamicomLeft = 1 << 11,
- HandheldFamicomRight = 1 << 12,
- HandheldNESLeft = 1 << 13,
- HandheldNESRight = 1 << 14,
- Lucia = 1 << 15,
- System = 1 << 31
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs
deleted file mode 100644
index 4ef83f3d..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- // TODO: Add missing structs
- unsafe struct ShMemNpad
- {
- public NpadStateHeader Header;
- public Array7<NpadLayout> Layouts; // One for each NpadLayoutsIndex
- public Array6<NpadSixAxis> Sixaxis;
- public DeviceType DeviceType;
- uint _padding1;
- public NpadSystemProperties SystemProperties;
- public uint NpadSystemButtonProperties;
- public Array3<BatteryCharge> BatteryState;
- public fixed byte NfcXcdDeviceHandleHeader[0x20];
- public fixed byte NfcXcdDeviceHandleState[0x20 * 2];
- public ulong Mutex;
- public fixed byte NpadGcTriggerHeader[0x20];
- public fixed byte NpadGcTriggerState[0x18 * 17];
- fixed byte _padding2[0xC38];
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadColorDescription.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadColorDescription.cs
deleted file mode 100644
index ccc7cb8d..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadColorDescription.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- [Flags]
- enum NpadColorDescription : int
- {
- ColorDescriptionColorsNonexistent = (1 << 1)
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadConnectionState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadConnectionState.cs
deleted file mode 100644
index 60f64fd3..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadConnectionState.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- [Flags]
- enum NpadConnectionState : long
- {
- ControllerStateConnected = (1 << 0),
- ControllerStateWired = (1 << 1),
- JoyLeftConnected = (1 << 2),
- JoyRightConnected = (1 << 4)
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs
deleted file mode 100644
index 24c4f4d4..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct NpadLayout
- {
- public CommonEntriesHeader Header;
- public Array17<NpadState> Entries;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs
deleted file mode 100644
index c4419336..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- enum NpadLayoutsIndex : int
- {
- ProController = 0,
- Handheld = 1,
- JoyDual = 2,
- JoyLeft = 3,
- JoyRight = 4,
- Pokeball = 5,
- SystemExternal = 6
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs
deleted file mode 100644
index a0a39fdc..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct NpadSixAxis
- {
- public CommonEntriesHeader Header;
- public Array17<SixAxisState> Entries;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadState.cs
deleted file mode 100644
index 60a5f9d3..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadState.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct NpadState
- {
- public ulong SampleTimestamp;
- public ulong SampleTimestamp2;
- public ControllerKeys Buttons;
- public int LStickX;
- public int LStickY;
- public int RStickX;
- public int RStickY;
- public NpadConnectionState ConnectionState;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadStatesHeader.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadStatesHeader.cs
deleted file mode 100644
index 006d4357..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadStatesHeader.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct NpadStateHeader
- {
- public ControllerType Type;
- public Boolean32 IsHalf;
- public NpadColorDescription SingleColorsDescriptor;
- public NpadColor SingleColorBody;
- public NpadColor SingleColorButtons;
- public NpadColorDescription SplitColorsDescriptor;
- public NpadColor LeftColorBody;
- public NpadColor LeftColorButtons;
- public NpadColor RightColorBody;
- public NpadColor RightColorButtons;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSystemProperties.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSystemProperties.cs
deleted file mode 100644
index 708f7da9..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSystemProperties.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- [Flags]
- enum NpadSystemProperties : long
- {
- PowerInfo0Charging = 1 << 0,
- PowerInfo1Charging = 1 << 1,
- PowerInfo2Charging = 1 << 2,
- PowerInfo0Connected = 1 << 3,
- PowerInfo1Connected = 1 << 4,
- PowerInfo2Connected = 1 << 5,
- UnsupportedButtonPressedNpadSystem = 1 << 9,
- UnsupportedButtonPressedNpadSystemExt = 1 << 10,
- AbxyButtonOriented = 1 << 11,
- SlSrButtonOriented = 1 << 12,
- PlusButtonCapability = 1 << 13,
- MinusButtonCapability = 1 << 14,
- DirectionalButtonsSupported = 1 << 15
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisLayoutsIndex.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisLayoutsIndex.cs
deleted file mode 100644
index a8795fc0..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisLayoutsIndex.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- enum SixAxixLayoutsIndex : int
- {
- ProController = 0,
- Handheld = 1,
- JoyDualLeft = 2,
- JoyDualRight = 3,
- JoyLeft = 4,
- JoyRight = 5,
- Pokeball = 6,
- SystemExternal = 7
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisState.cs
deleted file mode 100644
index 12974e7e..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisState.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- unsafe struct SixAxisState
- {
- public ulong SampleTimestamp;
- ulong _unknown1;
- public ulong SampleTimestamp2;
- public HidVector Accelerometer;
- public HidVector Gyroscope;
- public HidVector Rotations;
- public fixed float Orientation[9];
- public ulong _unknown2;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs
deleted file mode 100644
index 5f12295c..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- unsafe struct ShMemTouchScreen
- {
- public CommonEntriesHeader Header;
- public Array17<TouchScreenState> Entries;
- fixed byte _padding[0x3c8];
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs
deleted file mode 100644
index 1c85e291..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Ryujinx.Common.Memory;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct TouchScreenState
- {
- public ulong SampleTimestamp;
- public ulong SampleTimestamp2;
- public ulong NumTouches;
- public Array16<TouchScreenStateData> Touches;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs
deleted file mode 100644
index 4d4c48d1..00000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
- struct TouchScreenStateData
- {
- public ulong SampleTimestamp;
-#pragma warning disable CS0169
- uint _padding;
-#pragma warning restore CS0169
- public uint TouchIndex;
- public uint X;
- public uint Y;
- public uint DiameterX;
- public uint DiameterY;
- public uint Angle;
-#pragma warning disable CS0169
- uint _padding2;
-#pragma warning restore CS0169
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs
new file mode 100644
index 00000000..bf4b5888
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
+{
+ struct AnalogStickState
+ {
+ public int X;
+ public int Y;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs
new file mode 100644
index 00000000..45b92ba9
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs
@@ -0,0 +1,26 @@
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
+{
+ struct AtomicStorage<T> where T: unmanaged
+ {
+ public ulong SamplingNumber;
+ public T Object;
+
+ public ulong ReadSamplingNumberAtomic()
+ {
+ return Interlocked.Read(ref SamplingNumber);
+ }
+
+ public void SetObject(ref T obj)
+ {
+ ISampledData samplingProvider = obj as ISampledData;
+
+ Interlocked.Exchange(ref SamplingNumber, samplingProvider.SamplingNumber);
+
+ Thread.MemoryBarrier();
+
+ Object = obj;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs
new file mode 100644
index 00000000..08f76747
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
+{
+ interface ISampledData
+ {
+ ulong SamplingNumber { get; }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs
new file mode 100644
index 00000000..615e3893
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs
@@ -0,0 +1,149 @@
+using Ryujinx.Common.Memory;
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
+{
+ struct RingLifo<T> where T: unmanaged
+ {
+ private const ulong MaxEntries = 17;
+
+#pragma warning disable CS0169
+ private ulong _unused;
+#pragma warning restore CS0169
+#pragma warning disable CS0414
+ private ulong _bufferCount;
+#pragma warning restore CS0414
+ private ulong _index;
+ private ulong _count;
+ private Array17<AtomicStorage<T>> _storage;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ulong ReadCurrentIndex()
+ {
+ return Interlocked.Read(ref _index);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ulong ReadCurrentCount()
+ {
+ return Interlocked.Read(ref _count);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static ulong GetNextIndexForWrite(ulong index)
+ {
+ return (index + 1) % MaxEntries;
+ }
+
+ public ref AtomicStorage<T> GetCurrentAtomicEntryRef()
+ {
+ ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), 1);
+
+ if (countAvailaible == 0)
+ {
+ _storage[0] = default;
+
+ return ref _storage[0];
+ }
+
+ ulong index = ReadCurrentIndex();
+
+ while (true)
+ {
+ int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible) % MaxEntries);
+
+ ref AtomicStorage<T> result = ref _storage[inputEntryIndex];
+
+ ulong samplingNumber0 = result.ReadSamplingNumberAtomic();
+ ulong samplingNumber1 = result.ReadSamplingNumberAtomic();
+
+ if (samplingNumber0 != samplingNumber1 && (result.SamplingNumber - result.SamplingNumber) != 1)
+ {
+ ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible);
+
+ countAvailaible = Math.Min(tempCount, 1);
+ index = ReadCurrentIndex();
+
+ continue;
+ }
+
+ return ref result;
+ }
+ }
+
+ public ref T GetCurrentEntryRef()
+ {
+ return ref GetCurrentAtomicEntryRef().Object;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan<AtomicStorage<T>> ReadEntries(uint maxCount)
+ {
+ ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), maxCount);
+
+ if (countAvailaible == 0)
+ {
+ return ReadOnlySpan<AtomicStorage<T>>.Empty;
+ }
+
+ ulong index = ReadCurrentIndex();
+
+ AtomicStorage<T>[] result = new AtomicStorage<T>[countAvailaible];
+
+ for (ulong i = 0; i < countAvailaible; i++)
+ {
+ int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries);
+ int outputEntryIndex = (int)(countAvailaible - i - 1);
+
+ ulong samplingNumber0 = _storage[inputEntryIndex].ReadSamplingNumberAtomic();
+ result[outputEntryIndex] = _storage[inputEntryIndex];
+ ulong samplingNumber1 = _storage[inputEntryIndex].ReadSamplingNumberAtomic();
+
+ if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1))
+ {
+ ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible);
+
+ countAvailaible = Math.Min(tempCount, maxCount);
+ index = ReadCurrentIndex();
+
+ i -= 1;
+ }
+ }
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Write(ref T value)
+ {
+ ulong targetIndex = GetNextIndexForWrite(ReadCurrentIndex());
+
+ _storage[(int)targetIndex].SetObject(ref value);
+
+ Interlocked.Exchange(ref _index, targetIndex);
+
+ ulong count = ReadCurrentCount();
+
+ if (count < (MaxEntries - 1))
+ {
+ Interlocked.Increment(ref _count);
+ }
+ }
+
+ public void Clear()
+ {
+ Interlocked.Exchange(ref _count, 0);
+ Interlocked.Exchange(ref _index, 0);
+ }
+
+ public static RingLifo<T> Create()
+ {
+ return new RingLifo<T>
+ {
+ _bufferCount = MaxEntries
+ };
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs
new file mode 100644
index 00000000..ec5bd3c8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
+{
+ [Flags]
+ enum DebugPadAttribute : uint
+ {
+ None = 0,
+ Connected = 1 << 0
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs
new file mode 100644
index 00000000..e8f28317
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
+{
+ [Flags]
+ enum DebugPadButton : uint
+ {
+ None = 0,
+ A = 1 << 0,
+ B = 1 << 1,
+ X = 1 << 2,
+ Y = 1 << 3,
+ L = 1 << 4,
+ R = 1 << 5,
+ ZL = 1 << 6,
+ ZR = 1 << 7,
+ Start = 1 << 8,
+ Select = 1 << 9,
+ Left = 1 << 10,
+ Up = 1 << 11,
+ Right = 1 << 12,
+ Down = 1 << 13
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs
new file mode 100644
index 00000000..3e1e1ad8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs
@@ -0,0 +1,15 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
+{
+ struct DebugPadState : ISampledData
+ {
+ public ulong SamplingNumber;
+ public DebugPadAttribute Attributes;
+ public DebugPadButton Buttons;
+ public AnalogStickState AnalogStickR;
+ public AnalogStickState AnalogStickL;
+
+ ulong ISampledData.SamplingNumber => SamplingNumber;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs
new file mode 100644
index 00000000..22df7c79
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs
@@ -0,0 +1,29 @@
+using Ryujinx.Common.Memory;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
+{
+ struct KeyboardKey
+ {
+ public Array4<ulong> RawData;
+
+ public bool this[KeyboardKeyShift index]
+ {
+ get
+ {
+ return (RawData[(int)index / 64] & (1UL << ((int)index & 63))) != 0;
+ }
+ set
+ {
+ int arrayIndex = (int)index / 64;
+ ulong mask = 1UL << ((int)index & 63);
+
+ RawData[arrayIndex] &= ~mask;
+
+ if (value)
+ {
+ RawData[arrayIndex] |= mask;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs
new file mode 100644
index 00000000..01c2bb30
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs
@@ -0,0 +1,138 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
+{
+ enum KeyboardKeyShift
+ {
+ A = 4,
+ B = 5,
+ C = 6,
+ D = 7,
+ E = 8,
+ F = 9,
+ G = 10,
+ H = 11,
+ I = 12,
+ J = 13,
+ K = 14,
+ L = 15,
+ M = 16,
+ N = 17,
+ O = 18,
+ P = 19,
+ Q = 20,
+ R = 21,
+ S = 22,
+ T = 23,
+ U = 24,
+ V = 25,
+ W = 26,
+ X = 27,
+ Y = 28,
+ Z = 29,
+ D1 = 30,
+ D2 = 31,
+ D3 = 32,
+ D4 = 33,
+ D5 = 34,
+ D6 = 35,
+ D7 = 36,
+ D8 = 37,
+ D9 = 38,
+ D0 = 39,
+ Return = 40,
+ Escape = 41,
+ Backspace = 42,
+ Tab = 43,
+ Space = 44,
+ Minus = 45,
+ Plus = 46,
+ OpenBracket = 47,
+ CloseBracket = 48,
+ Pipe = 49,
+ Tilde = 50,
+ Semicolon = 51,
+ Quote = 52,
+ Backquote = 53,
+ Comma = 54,
+ Period = 55,
+ Slash = 56,
+ CapsLock = 57,
+ F1 = 58,
+ F2 = 59,
+ F3 = 60,
+ F4 = 61,
+ F5 = 62,
+ F6 = 63,
+ F7 = 64,
+ F8 = 65,
+ F9 = 66,
+ F10 = 67,
+ F11 = 68,
+ F12 = 69,
+ PrintScreen = 70,
+ ScrollLock = 71,
+ Pause = 72,
+ Insert = 73,
+ Home = 74,
+ PageUp = 75,
+ Delete = 76,
+ End = 77,
+ PageDown = 78,
+ RightArrow = 79,
+ LeftArrow = 80,
+ DownArrow = 81,
+ UpArrow = 82,
+ NumLock = 83,
+ NumPadDivide = 84,
+ NumPadMultiply = 85,
+ NumPadSubtract = 86,
+ NumPadAdd = 87,
+ NumPadEnter = 88,
+ NumPad1 = 89,
+ NumPad2 = 90,
+ NumPad3 = 91,
+ NumPad4 = 92,
+ NumPad5 = 93,
+ NumPad6 = 94,
+ NumPad7 = 95,
+ NumPad8 = 96,
+ NumPad9 = 97,
+ NumPad0 = 98,
+ NumPadDot = 99,
+ Backslash = 100,
+ Application = 101,
+ Power = 102,
+ NumPadEquals = 103,
+ F13 = 104,
+ F14 = 105,
+ F15 = 106,
+ F16 = 107,
+ F17 = 108,
+ F18 = 109,
+ F19 = 110,
+ F20 = 111,
+ F21 = 112,
+ F22 = 113,
+ F23 = 114,
+ F24 = 115,
+ NumPadComma = 133,
+ Ro = 135,
+ KatakanaHiragana = 136,
+ Yen = 137,
+ Henkan = 138,
+ Muhenkan = 139,
+ NumPadCommaPc98 = 140,
+ HangulEnglish = 144,
+ Hanja = 145,
+ Katakana = 146,
+ Hiragana = 147,
+ ZenkakuHankaku = 148,
+ LeftControl = 224,
+ LeftShift = 225,
+ LeftAlt = 226,
+ LeftGui = 227,
+ RightControl = 228,
+ RightShift = 229,
+ RightAlt = 230,
+ RightGui = 231
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs
new file mode 100644
index 00000000..72d1603a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
+{
+ // TODO: This seems entirely wrong
+ [Flags]
+ enum KeyboardModifier : uint
+ {
+ None = 0,
+ Control = 1 << 0,
+ Shift = 1 << 1,
+ LeftAlt = 1 << 2,
+ RightAlt = 1 << 3,
+ Gui = 1 << 4,
+ CapsLock = 1 << 8,
+ ScrollLock = 1 << 9,
+ NumLock = 1 << 10,
+ Katakana = 1 << 11,
+ Hiragana = 1 << 12
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs
new file mode 100644
index 00000000..37608506
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs
@@ -0,0 +1,13 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
+{
+ struct KeyboardState : ISampledData
+ {
+ public ulong SamplingNumber;
+ public KeyboardModifier Modifiers;
+ public KeyboardKey Keys;
+
+ ulong ISampledData.SamplingNumber => SamplingNumber;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs
new file mode 100644
index 00000000..5ffba0d7
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
+{
+ [Flags]
+ enum MouseAttribute : uint
+ {
+ None = 0,
+ Transferable = 1 << 0,
+ IsConnected = 1 << 1
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs
new file mode 100644
index 00000000..7e35140c
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
+{
+ [Flags]
+ enum MouseButton : uint
+ {
+ None = 0,
+ Left = 1 << 0,
+ Right = 1 << 1,
+ Middle = 1 << 2,
+ Forward = 1 << 3,
+ Back = 1 << 4
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs
new file mode 100644
index 00000000..67ad6bf1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs
@@ -0,0 +1,19 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
+{
+ struct MouseState : ISampledData
+ {
+ public ulong SamplingNumber;
+ public int X;
+ public int Y;
+ public int DeltaX;
+ public int DeltaY;
+ public int WheelDeltaX;
+ public int WheelDeltaY;
+ public MouseButton Buttons;
+ public MouseAttribute Attributes;
+
+ ulong ISampledData.SamplingNumber => SamplingNumber;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs
new file mode 100644
index 00000000..b0201835
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ [Flags]
+ enum DeviceType : int
+ {
+ None = 0,
+
+ FullKey = 1 << 0,
+ DebugPad = 1 << 1,
+ HandheldLeft = 1 << 2,
+ HandheldRight = 1 << 3,
+ JoyLeft = 1 << 4,
+ JoyRight = 1 << 5,
+ Palma = 1 << 6,
+ LarkHvcLeft = 1 << 7,
+ LarkHvcRight = 1 << 8,
+ LarkNesLeft = 1 << 9,
+ LarkNesRight = 1 << 10,
+ HandheldLarkHvcLeft = 1 << 11,
+ HandheldLarkHvcRight = 1 << 12,
+ HandheldLarkNesLeft = 1 << 13,
+ HandheldLarkNesRight = 1 << 14,
+ Lucia = 1 << 15,
+
+ System = 1 << 31
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs
new file mode 100644
index 00000000..0960b7bf
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ [Flags]
+ enum NpadAttribute : uint
+ {
+ None = 0,
+ IsConnected = 1 << 0,
+ IsWired = 1 << 1,
+ IsLeftConnected = 1 << 2,
+ IsLeftWired = 1 << 3,
+ IsRightConnected = 1 << 4,
+ IsRightWired = 1 << 5
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs
new file mode 100644
index 00000000..477dfd10
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ enum NpadBatteryLevel : int
+ {
+ Percent0,
+ Percent25,
+ Percent50,
+ Percent75,
+ Percent100
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs
new file mode 100644
index 00000000..5b3e13a7
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ [Flags]
+ enum NpadButton : ulong
+ {
+ None = 0,
+ A = 1 << 0,
+ B = 1 << 1,
+ X = 1 << 2,
+ Y = 1 << 3,
+ StickL = 1 << 4,
+ StickR = 1 << 5,
+ L = 1 << 6,
+ R = 1 << 7,
+ ZL = 1 << 8,
+ ZR = 1 << 9,
+ Plus = 1 << 10,
+ Minus = 1 << 11,
+ Left = 1 << 12,
+ Up = 1 << 13,
+ Right = 1 << 14,
+ Down = 1 << 15,
+ StickLLeft = 1 << 16,
+ StickLUp = 1 << 17,
+ StickLRight = 1 << 18,
+ StickLDown = 1 << 19,
+ StickRLeft = 1 << 20,
+ StickRUp = 1 << 21,
+ StickRRight = 1 << 22,
+ StickRDown = 1 << 23,
+ LeftSL = 1 << 24,
+ LeftSR = 1 << 25,
+ RightSL = 1 << 26,
+ RightSR = 1 << 27,
+ Palma = 1 << 28,
+
+ // FIXME: Probably a button on Lark.
+ Unknown29 = 1 << 29,
+
+ HandheldLeftB = 1 << 30
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs
new file mode 100644
index 00000000..1e547cc8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ enum NpadColorAttribute : uint
+ {
+ Ok,
+ ReadError,
+ NoController
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs
new file mode 100644
index 00000000..eaccef80
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs
@@ -0,0 +1,16 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ struct NpadCommonState : ISampledData
+ {
+ public ulong SamplingNumber;
+ public NpadButton Buttons;
+ public AnalogStickState AnalogStickL;
+ public AnalogStickState AnalogStickR;
+ public NpadAttribute Attributes;
+ private uint _reserved;
+
+ ulong ISampledData.SamplingNumber => SamplingNumber;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs
new file mode 100644
index 00000000..990eafb2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ struct NpadFullKeyColorState
+ {
+ public NpadColorAttribute Attribute;
+ public uint FullKeyBody;
+ public uint FullKeyButtons;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs
new file mode 100644
index 00000000..52668f85
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs
@@ -0,0 +1,15 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ struct NpadGcTriggerState : ISampledData
+ {
+#pragma warning disable CS0649
+ public ulong SamplingNumber;
+ public uint TriggerL;
+ public uint TriggerR;
+#pragma warning restore CS0649
+
+ ulong ISampledData.SamplingNumber => SamplingNumber;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs
new file mode 100644
index 00000000..f225ff67
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs
@@ -0,0 +1,61 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ struct NpadInternalState
+ {
+ public NpadStyleTag StyleSet;
+ public NpadJoyAssignmentMode JoyAssignmentMode;
+ public NpadFullKeyColorState FullKeyColor;
+ public NpadJoyColorState JoyColor;
+ public RingLifo<NpadCommonState> FullKey;
+ public RingLifo<NpadCommonState> Handheld;
+ public RingLifo<NpadCommonState> JoyDual;
+ public RingLifo<NpadCommonState> JoyLeft;
+ public RingLifo<NpadCommonState> JoyRight;
+ public RingLifo<NpadCommonState> Palma;
+ public RingLifo<NpadCommonState> SystemExt;
+ public RingLifo<SixAxisSensorState> FullKeySixAxisSensor;
+ public RingLifo<SixAxisSensorState> HandheldSixAxisSensor;
+ public RingLifo<SixAxisSensorState> JoyDualSixAxisSensor;
+ public RingLifo<SixAxisSensorState> JoyDualRightSixAxisSensor;
+ public RingLifo<SixAxisSensorState> JoyLeftSixAxisSensor;
+ public RingLifo<SixAxisSensorState> JoyRightSixAxisSensor;
+ public DeviceType DeviceType;
+ private uint _reserved1;
+ public NpadSystemProperties SystemProperties;
+ public NpadSystemButtonProperties SystemButtonProperties;
+ public NpadBatteryLevel BatteryLevelJoyDual;
+ public NpadBatteryLevel BatteryLevelJoyLeft;
+ public NpadBatteryLevel BatteryLevelJoyRight;
+ public uint AppletFooterUiAttributes;
+ public byte AppletFooterUiType;
+ private unsafe fixed byte _reserved2[0x7B];
+ public RingLifo<NpadGcTriggerState> GcTrigger;
+ public NpadLarkType LarkTypeLeftAndMain;
+ public NpadLarkType LarkTypeRight;
+ public NpadLuciaType LuciaType;
+ public uint Unknown43EC;
+
+ public static NpadInternalState Create()
+ {
+ return new NpadInternalState
+ {
+ FullKey = RingLifo<NpadCommonState>.Create(),
+ Handheld = RingLifo<NpadCommonState>.Create(),
+ JoyDual = RingLifo<NpadCommonState>.Create(),
+ JoyLeft = RingLifo<NpadCommonState>.Create(),
+ JoyRight = RingLifo<NpadCommonState>.Create(),
+ Palma = RingLifo<NpadCommonState>.Create(),
+ SystemExt = RingLifo<NpadCommonState>.Create(),
+ FullKeySixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
+ HandheldSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
+ JoyDualSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
+ JoyDualRightSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
+ JoyLeftSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
+ JoyRightSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
+ GcTrigger = RingLifo<NpadGcTriggerState>.Create(),
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs
new file mode 100644
index 00000000..871c4c5a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ enum NpadJoyAssignmentMode : uint
+ {
+ Dual,
+ Single
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs
new file mode 100644
index 00000000..3986dd5e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ struct NpadJoyColorState
+ {
+ public NpadColorAttribute Attribute;
+ public uint LeftBody;
+ public uint LeftButtons;
+ public uint RightBody;
+ public uint RightButtons;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs
new file mode 100644
index 00000000..a487a911
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ enum NpadLarkType : uint
+ {
+ Invalid,
+ H1,
+ H2,
+ NL,
+ NR
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs
new file mode 100644
index 00000000..95148485
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ enum NpadLuciaType
+ {
+ Invalid,
+ J,
+ E,
+ U
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs
new file mode 100644
index 00000000..ed9e7c0d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs
@@ -0,0 +1,18 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x5000)]
+ struct NpadState
+ {
+ public NpadInternalState InternalState;
+
+ public static NpadState Create()
+ {
+ return new NpadState
+ {
+ InternalState = NpadInternalState.Create()
+ };
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs
new file mode 100644
index 00000000..f31978e2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs
@@ -0,0 +1,76 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ /// <summary>
+ /// Nintendo pad style
+ /// </summary>
+ [Flags]
+ enum NpadStyleTag : uint
+ {
+ /// <summary>
+ /// No type.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Pro controller.
+ /// </summary>
+ FullKey = 1 << 0,
+
+ /// <summary>
+ /// Joy-Con controller in handheld mode.
+ /// </summary>
+ Handheld = 1 << 1,
+
+ /// <summary>
+ /// Joy-Con controller in dual mode.
+ /// </summary>
+ JoyDual = 1 << 2,
+
+ /// <summary>
+ /// Joy-Con left controller in single mode.
+ /// </summary>
+ JoyLeft = 1 << 3,
+
+ /// <summary>
+ /// Joy-Con right controller in single mode.
+ /// </summary>
+ JoyRight = 1 << 4,
+
+ /// <summary>
+ /// GameCube controller.
+ /// </summary>
+ Gc = 1 << 5,
+
+ /// <summary>
+ /// Poké Ball Plus controller.
+ /// </summary>
+ Palma = 1 << 6,
+
+ /// <summary>
+ /// NES and Famicom controller.
+ /// </summary>
+ Lark = 1 << 7,
+
+ /// <summary>
+ /// NES and Famicom controller in handheld mode.
+ /// </summary>
+ HandheldLark = 1 << 8,
+
+ /// <summary>
+ /// SNES controller.
+ /// </summary>
+ Lucia = 1 << 9,
+
+ /// <summary>
+ /// Generic external controller.
+ /// </summary>
+ SystemExt = 1 << 29,
+
+ /// <summary>
+ /// Generic controller.
+ /// </summary>
+ System = 1 << 30
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs
new file mode 100644
index 00000000..68603271
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ [Flags]
+ enum NpadSystemButtonProperties : uint
+ {
+ None = 0,
+ IsUnintendedHomeButtonInputProtectionEnabled = 1 << 0
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs
new file mode 100644
index 00000000..13444555
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ [Flags]
+ enum NpadSystemProperties : ulong
+ {
+ None = 0,
+
+ IsChargingJoyDual = 1 << 0,
+ IsChargingJoyLeft = 1 << 1,
+ IsChargingJoyRight = 1 << 2,
+ IsPoweredJoyDual = 1 << 3,
+ IsPoweredJoyLeft = 1 << 4,
+ IsPoweredJoyRight = 1 << 5,
+ IsUnsuportedButtonPressedOnNpadSystem = 1 << 9,
+ IsUnsuportedButtonPressedOnNpadSystemExt = 1 << 10,
+ IsAbxyButtonOriented = 1 << 11,
+ IsSlSrButtonOriented = 1 << 12,
+ IsPlusAvailable = 1 << 13,
+ IsMinusAvailable = 1 << 14,
+ IsDirectionalButtonsAvailable = 1 << 15
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs
new file mode 100644
index 00000000..7ed46d98
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ [Flags]
+ enum SixAxisSensorAttribute : uint
+ {
+ None = 0,
+ IsConnected = 1 << 0,
+ IsInterpolated = 1 << 1
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs
new file mode 100644
index 00000000..d024b0b0
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs
@@ -0,0 +1,19 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
+{
+ struct SixAxisSensorState : ISampledData
+ {
+ public ulong DeltaTime;
+ public ulong SamplingNumber;
+ public HidVector Acceleration;
+ public HidVector AngularVelocity;
+ public HidVector Angle;
+ public Array9<float> Direction;
+ public SixAxisSensorAttribute Attributes;
+ private uint _reserved;
+
+ ulong ISampledData.SamplingNumber => SamplingNumber;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
new file mode 100644
index 00000000..48acfc3f
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
@@ -0,0 +1,66 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
+{
+ /// <summary>
+ /// Represent the shared memory shared between applications for input.
+ /// </summary>
+ [StructLayout(LayoutKind.Explicit, Size = 0x40000)]
+ struct SharedMemory
+ {
+ /// <summary>
+ /// Debug controller.
+ /// </summary>
+ [FieldOffset(0)]
+ public RingLifo<DebugPadState> DebugPad;
+
+ /// <summary>
+ /// Touchscreen.
+ /// </summary>
+ [FieldOffset(0x400)]
+ public RingLifo<TouchScreenState> TouchScreen;
+
+ /// <summary>
+ /// Mouse.
+ /// </summary>
+ [FieldOffset(0x3400)]
+ public RingLifo<MouseState> Mouse;
+
+ /// <summary>
+ /// Keyboard.
+ /// </summary>
+ [FieldOffset(0x3800)]
+ public RingLifo<KeyboardState> Keyboard;
+
+ /// <summary>
+ /// Nintendo Pads.
+ /// </summary>
+ [FieldOffset(0x9A00)]
+ public Array10<NpadState> Npads;
+
+ public static SharedMemory Create()
+ {
+ SharedMemory result = new SharedMemory
+ {
+ DebugPad = RingLifo<DebugPadState>.Create(),
+ TouchScreen = RingLifo<TouchScreenState>.Create(),
+ Mouse = RingLifo<MouseState>.Create(),
+ Keyboard = RingLifo<KeyboardState>.Create(),
+ };
+
+ for (int i = 0; i < result.Npads.Length; i++)
+ {
+ result.Npads[i] = NpadState.Create();
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs
new file mode 100644
index 00000000..8a8f9cc1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
+{
+ [Flags]
+ enum TouchAttribute : uint
+ {
+ None = 0,
+ Start = 1 << 0,
+ End = 1 << 1
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs
new file mode 100644
index 00000000..8203e49b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs
@@ -0,0 +1,15 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
+{
+ struct TouchScreenState : ISampledData
+ {
+ public ulong SamplingNumber;
+ public int TouchesCount;
+ private int _reserved;
+ public Array16<TouchState> Touches;
+
+ ulong ISampledData.SamplingNumber => SamplingNumber;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs
new file mode 100644
index 00000000..ba621a2b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
+{
+ struct TouchState
+ {
+ public ulong DeltaTime;
+#pragma warning disable CS0649
+ public TouchAttribute Attribute;
+#pragma warning restore CS0649
+ public uint FingerId;
+ public uint X;
+ public uint Y;
+ public uint DiameterX;
+ public uint DiameterY;
+ public uint RotationAngle;
+#pragma warning disable CS0169
+ private uint _reserved;
+#pragma warning restore CS0169
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs
index 69d461de..e9582c26 100644
--- a/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services
bool serviceExists = service.HipcCommands.TryGetValue(commandId, out MethodInfo processRequest);
- if (ServiceConfiguration.IgnoreMissingServices || serviceExists)
+ if (context.Device.Configuration.IgnoreMissingServices || serviceExists)
{
ResultCode result = ResultCode.Success;
@@ -163,7 +163,7 @@ namespace Ryujinx.HLE.HOS.Services
bool serviceExists = TipcCommands.TryGetValue(commandId, out MethodInfo processRequest);
- if (ServiceConfiguration.IgnoreMissingServices || serviceExists)
+ if (context.Device.Configuration.IgnoreMissingServices || serviceExists)
{
ResultCode result = ResultCode.Success;
diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
index 0bf6f177..3181668f 100644
--- a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
+++ b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
@@ -19,11 +19,11 @@ namespace Ryujinx.HLE.HOS.Services.Lm.LogService
private string LogImpl(ServiceCtx context)
{
- (long bufPos, long bufSize) = context.Request.GetBufferType0x21();
+ (ulong bufPos, ulong bufSize) = context.Request.GetBufferType0x21();
byte[] logBuffer = new byte[bufSize];
- context.Memory.Read((ulong)bufPos, logBuffer);
+ context.Memory.Read(bufPos, logBuffer);
using MemoryStream ms = new MemoryStream(logBuffer);
diff --git a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs
index 73f1a673..dea32f62 100644
--- a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs
+++ b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs
@@ -261,7 +261,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
ResultCode result = Export(data);
- context.Memory.Write((ulong)outputBuffer.Position, data.ToArray());
+ context.Memory.Write(outputBuffer.Position, data.ToArray());
return result;
}
@@ -352,7 +352,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
{
rawData = new byte[ipcBuff.Size];
- context.Memory.Read((ulong)ipcBuff.Position, rawData);
+ context.Memory.Read(ipcBuff.Position, rawData);
}
return new Span<byte>(rawData);
@@ -367,7 +367,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
{
Span<byte> rawData = MemoryMarshal.Cast<T, byte>(span);
- context.Memory.Write((ulong)ipcBuff.Position, rawData);
+ context.Memory.Write(ipcBuff.Position, rawData);
}
protected abstract bool IsUpdated(SourceFlag flag);
diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs
index 6cd643d3..bce52119 100644
--- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs
+++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs
@@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm
private static uint _uniqueId = 1;
- public IRequest(ServiceCtx context) {}
+ public IRequest(ServiceCtx context) { }
[CommandHipc(0)]
// InitializeOld(u32, u32, u32)
@@ -50,14 +50,14 @@ namespace Ryujinx.HLE.HOS.Services.Mm
public ResultCode SetAndWaitOld(ServiceCtx context)
{
MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
- uint value = context.RequestData.ReadUInt32();
+ uint frequenceHz = context.RequestData.ReadUInt32();
int timeout = context.RequestData.ReadInt32();
- Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, value, timeout });
+ Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, frequenceHz, timeout });
lock (_sessionListLock)
{
- GetSessionByType(operationType)?.SetAndWait(value, timeout);
+ GetSessionByType(operationType)?.SetAndWait(frequenceHz, timeout);
}
return ResultCode.Success;
@@ -120,15 +120,15 @@ namespace Ryujinx.HLE.HOS.Services.Mm
// SetAndWait(u32, u32, u32)
public ResultCode SetAndWait(ServiceCtx context)
{
- uint id = context.RequestData.ReadUInt32();
- uint value = context.RequestData.ReadUInt32();
- int timeout = context.RequestData.ReadInt32();
+ uint id = context.RequestData.ReadUInt32();
+ uint frequenceHz = context.RequestData.ReadUInt32();
+ int timeout = context.RequestData.ReadInt32();
- Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id, value, timeout });
+ Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id, frequenceHz, timeout });
lock (_sessionListLock)
{
- GetSessionById(id)?.SetAndWait(value, timeout);
+ GetSessionById(id)?.SetAndWait(frequenceHz, timeout);
}
return ResultCode.Success;
diff --git a/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs b/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs
index cf4cdf20..2742af6c 100644
--- a/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs
+++ b/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs
@@ -2,10 +2,9 @@
{
enum MultiMediaOperationType : uint
{
- // TODO: figure out the unknown variants.
- Unknown2 = 2,
- VideoDecode = 5,
- VideoEncode = 6,
- Unknown7 = 7
+ Ram = 2,
+ NvEnc = 5,
+ NvDec = 6,
+ NvJpg = 7
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs
index 6ffa33f4..3fbc42a9 100644
--- a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs
+++ b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs
@@ -17,10 +17,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(0)]
- // ResolveProgramPath()
+ // ResolveProgramPath(u64 titleId)
public ResultCode ResolveProgramPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
if (ResolvePath(context, titleId, NcaContentType.Program))
{
@@ -33,10 +33,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(1)]
- // RedirectProgramPath()
+ // RedirectProgramPath(u64 titleId)
public ResultCode RedirectProgramPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
RedirectPath(context, titleId, 0, NcaContentType.Program);
@@ -44,10 +44,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(2)]
- // ResolveApplicationControlPath()
+ // ResolveApplicationControlPath(u64 titleId)
public ResultCode ResolveApplicationControlPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
if (ResolvePath(context, titleId, NcaContentType.Control))
{
@@ -60,10 +60,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(3)]
- // ResolveApplicationHtmlDocumentPath()
+ // ResolveApplicationHtmlDocumentPath(u64 titleId)
public ResultCode ResolveApplicationHtmlDocumentPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
if (ResolvePath(context, titleId, NcaContentType.Manual))
{
@@ -76,10 +76,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(4)]
- // ResolveDataPath()
+ // ResolveDataPath(u64 titleId)
public ResultCode ResolveDataPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
if (ResolvePath(context, titleId, NcaContentType.Data) || ResolvePath(context, titleId, NcaContentType.PublicData))
{
@@ -92,10 +92,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(5)]
- // RedirectApplicationControlPath()
+ // RedirectApplicationControlPath(u64 titleId)
public ResultCode RedirectApplicationControlPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
RedirectPath(context, titleId, 1, NcaContentType.Control);
@@ -103,10 +103,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(6)]
- // RedirectApplicationHtmlDocumentPath()
+ // RedirectApplicationHtmlDocumentPath(u64 titleId)
public ResultCode RedirectApplicationHtmlDocumentPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
RedirectPath(context, titleId, 1, NcaContentType.Manual);
@@ -114,10 +114,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(7)]
- // ResolveApplicationLegalInformationPath()
+ // ResolveApplicationLegalInformationPath(u64 titleId)
public ResultCode ResolveApplicationLegalInformationPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
if (ResolvePath(context, titleId, NcaContentType.Manual))
{
@@ -130,10 +130,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(8)]
- // RedirectApplicationLegalInformationPath()
+ // RedirectApplicationLegalInformationPath(u64 titleId)
public ResultCode RedirectApplicationLegalInformationPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
RedirectPath(context, titleId, 1, NcaContentType.Manual);
@@ -150,10 +150,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(10)]
- // SetProgramNcaPath2()
+ // SetProgramNcaPath2(u64 titleId)
public ResultCode SetProgramNcaPath2(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
RedirectPath(context, titleId, 1, NcaContentType.Program);
@@ -170,10 +170,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(12)]
- // DeleteProgramNcaPath()
+ // DeleteProgramNcaPath(u64 titleId)
public ResultCode DeleteProgramNcaPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
DeleteContentPath(context, titleId, NcaContentType.Program);
@@ -181,10 +181,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(13)]
- // DeleteControlNcaPath()
+ // DeleteControlNcaPath(u64 titleId)
public ResultCode DeleteControlNcaPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
DeleteContentPath(context, titleId, NcaContentType.Control);
@@ -192,10 +192,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(14)]
- // DeleteDocHtmlNcaPath()
+ // DeleteDocHtmlNcaPath(u64 titleId)
public ResultCode DeleteDocHtmlNcaPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
DeleteContentPath(context, titleId, NcaContentType.Manual);
@@ -203,17 +203,17 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
}
[CommandHipc(15)]
- // DeleteInfoHtmlNcaPath()
+ // DeleteInfoHtmlNcaPath(u64 titleId)
public ResultCode DeleteInfoHtmlNcaPath(ServiceCtx context)
{
- long titleId = context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
DeleteContentPath(context, titleId, NcaContentType.Manual);
return ResultCode.Success;
}
- private void RedirectPath(ServiceCtx context, long titleId, int flag, NcaContentType contentType)
+ private void RedirectPath(ServiceCtx context, ulong titleId, int flag, NcaContentType contentType)
{
string contentPath = ReadUtf8String(context);
LocationEntry newLocation = new LocationEntry(contentPath, flag, titleId, contentType);
@@ -221,19 +221,19 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
context.Device.System.ContentManager.RedirectLocation(newLocation, _storageId);
}
- private bool ResolvePath(ServiceCtx context, long titleId, NcaContentType contentType)
+ private bool ResolvePath(ServiceCtx context, ulong titleId, NcaContentType contentType)
{
ContentManager contentManager = context.Device.System.ContentManager;
string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Program);
if (!string.IsNullOrWhiteSpace(contentPath))
{
- long position = context.Request.RecvListBuff[0].Position;
- long size = context.Request.RecvListBuff[0].Size;
+ ulong position = context.Request.RecvListBuff[0].Position;
+ ulong size = context.Request.RecvListBuff[0].Size;
byte[] contentPathBuffer = Encoding.UTF8.GetBytes(contentPath);
- context.Memory.Write((ulong)position, contentPathBuffer);
+ context.Memory.Write(position, contentPathBuffer);
}
else
{
@@ -243,7 +243,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
return true;
}
- private void DeleteContentPath(ServiceCtx context, long titleId, NcaContentType contentType)
+ private void DeleteContentPath(ServiceCtx context, ulong titleId, NcaContentType contentType)
{
ContentManager contentManager = context.Device.System.ContentManager;
string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Manual);
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs
index dd3990ae..f241c311 100644
--- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs
@@ -37,12 +37,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
_appletResourceUserId = context.RequestData.ReadUInt64();
_mcuVersionData = context.RequestData.ReadUInt64();
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
_mcuData = new byte[inputSize];
- context.Memory.Read((ulong)inputPosition, _mcuData);
+ context.Memory.Read(inputPosition, _mcuData);
// TODO: The mcuData buffer seems to contains entries with a size of 0x40 bytes each. Usage of the data needs to be determined.
@@ -93,8 +93,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.WrongArgument;
}
- long outputPosition = context.Request.RecvListBuff[0].Position;
- long outputSize = context.Request.RecvListBuff[0].Size;
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputSize = context.Request.RecvListBuff[0].Size;
if (context.Device.System.NfpDevices.Count == 0)
{
@@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
{
- context.Memory.Write((ulong)(outputPosition + (i * sizeof(long))), (uint)context.Device.System.NfpDevices[i].Handle);
+ context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfpDevices[i].Handle);
}
context.ResponseData.Write(context.Device.System.NfpDevices.Count);
@@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound)
{
context.Device.System.NfpDevices[i].SignalActivate();
- Thread.Sleep(50); // NOTE: Simulate amiibo scanning delay.
+ Thread.Sleep(125); // NOTE: Simulate amiibo scanning delay.
context.Device.System.NfpDevices[i].SignalDeactivate();
break;
@@ -376,8 +376,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.DeviceNotFound;
}
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
@@ -397,7 +397,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
byte[] applicationArea = VirtualAmiibo.GetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId);
- context.Memory.Write((ulong)outputPosition, applicationArea);
+ context.Memory.Write(outputPosition, applicationArea);
size = (uint)applicationArea.Length;
@@ -444,12 +444,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.DeviceNotFound;
}
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
byte[] applicationArea = new byte[inputSize];
- context.Memory.Read((ulong)inputPosition, applicationArea);
+ context.Memory.Read(inputPosition, applicationArea);
for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
{
@@ -523,12 +523,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
uint applicationAreaId = context.RequestData.ReadUInt32();
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
byte[] applicationArea = new byte[inputSize];
- context.Memory.Read((ulong)inputPosition, applicationArea);
+ context.Memory.Read(inputPosition, applicationArea);
bool isCreated = false;
@@ -582,9 +582,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.WrongArgument;
}
- long outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(TagInfo)));
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(TagInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(TagInfo)));
@@ -625,7 +625,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
Uuid.CopyTo(tagInfo.Uuid.ToSpan());
- context.Memory.Write((ulong)outputPosition, tagInfo);
+ context.Memory.Write(outputPosition, tagInfo);
resultCode = ResultCode.Success;
}
@@ -658,9 +658,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.WrongArgument;
}
- long outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(RegisterInfo)));
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(RegisterInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(RegisterInfo)));
@@ -685,7 +685,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
RegisterInfo registerInfo = VirtualAmiibo.GetRegisterInfo(context.Device.System.NfpDevices[i].AmiiboId);
- context.Memory.Write((ulong)outputPosition, registerInfo);
+ context.Memory.Write(outputPosition, registerInfo);
resultCode = ResultCode.Success;
}
@@ -718,9 +718,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.WrongArgument;
}
- long outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(CommonInfo)));
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(CommonInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(CommonInfo)));
@@ -745,7 +745,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
CommonInfo commonInfo = VirtualAmiibo.GetCommonInfo(context.Device.System.NfpDevices[i].AmiiboId);
- context.Memory.Write((ulong)outputPosition, commonInfo);
+ context.Memory.Write(outputPosition, commonInfo);
resultCode = ResultCode.Success;
}
@@ -778,9 +778,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.WrongArgument;
}
- long outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(ModelInfo)));
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(ModelInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(ModelInfo)));
@@ -814,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber);
- context.Memory.Write((ulong)outputPosition, modelInfo);
+ context.Memory.Write(outputPosition, modelInfo);
resultCode = ResultCode.Success;
}
diff --git a/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs b/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs
index a1907d4f..8d99721e 100644
--- a/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs
@@ -11,8 +11,8 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
// Then it checks if ngc.t!functionality_override_enabled is enabled and if sys:set GetT is == 2.
// If both conditions are true, it does this following code. Since we currently stub it, it's fine to don't check settings service values.
- long bufferPosition = context.Request.PtrBuff[0].Position;
- long bufferSize = context.Request.PtrBuff[0].Size;
+ ulong bufferPosition = context.Request.PtrBuff[0].Position;
+ ulong bufferSize = context.Request.PtrBuff[0].Size;
bool isMatch = false;
string text = "";
@@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
{
byte[] buffer = new byte[bufferSize];
- context.Memory.Read((ulong)bufferPosition, buffer);
+ context.Memory.Read(bufferPosition, buffer);
text = Encoding.ASCII.GetString(buffer);
@@ -52,10 +52,10 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
// Then it checks if ngc.t!functionality_override_enabled is enabled and if sys:set GetT is == 2.
// If both conditions are true, it does this following code. Since we currently stub it, it's fine to don't check settings service values.
- long bufferPosition = context.Request.PtrBuff[0].Position;
- long bufferSize = context.Request.PtrBuff[0].Size;
+ ulong bufferPosition = context.Request.PtrBuff[0].Position;
+ ulong bufferSize = context.Request.PtrBuff[0].Size;
- long bufferFilteredPosition = context.Request.RecvListBuff[0].Position;
+ ulong bufferFilteredPosition = context.Request.RecvListBuff[0].Position;
string text = "";
string textFiltered = "";
@@ -66,13 +66,13 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
{
textFiltered = new string('*', text.Length);
- context.Memory.Write((ulong)bufferFilteredPosition, Encoding.ASCII.GetBytes(textFiltered));
+ context.Memory.Write(bufferFilteredPosition, Encoding.ASCII.GetBytes(textFiltered));
}
else
{
byte[] buffer = new byte[bufferSize];
- context.Memory.Read((ulong)bufferPosition, buffer);
+ context.Memory.Read(bufferPosition, buffer);
// NOTE: Ngct use the archive 0100000000001034 which contains a words table. This is pushed on Chinese Switchs using Bcat service.
// This call check if the string contains words which are in the table then returns the same string with each matched words replaced by '*'.
@@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
textFiltered = text = Encoding.ASCII.GetString(buffer);
- context.Memory.Write((ulong)bufferFilteredPosition, buffer);
+ context.Memory.Write(bufferFilteredPosition, buffer);
}
}
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
index 5f1f9b3a..e650879b 100644
--- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
@@ -29,11 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
// GetClientId() -> buffer<nn::nifm::ClientId, 0x1a, 4>
public ResultCode GetClientId(ServiceCtx context)
{
- long position = context.Request.RecvListBuff[0].Position;
+ ulong position = context.Request.RecvListBuff[0].Position;
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(4);
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(sizeof(int));
- context.Memory.Write((ulong)position, _generalServiceDetail.ClientId);
+ context.Memory.Write(position, _generalServiceDetail.ClientId);
return ResultCode.Success;
}
@@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
// GetCurrentNetworkProfile() -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a, 0x17c>
public ResultCode GetCurrentNetworkProfile(ServiceCtx context)
{
- long networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
+ ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
@@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Unsafe.SizeOf<NetworkProfileData>());
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<NetworkProfileData>());
NetworkProfileData networkProfile = new NetworkProfileData
{
@@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
Encoding.ASCII.GetBytes("RyujinxNetwork").CopyTo(networkProfile.Name.ToSpan());
- context.Memory.Write((ulong)networkProfileDataPosition, networkProfile);
+ context.Memory.Write(networkProfileDataPosition, networkProfile);
return ResultCode.Success;
}
@@ -148,10 +148,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
// IsAnyInternetRequestAccepted(buffer<nn::nifm::ClientId, 0x19, 4>) -> bool
public ResultCode IsAnyInternetRequestAccepted(ServiceCtx context)
{
- long position = context.Request.PtrBuff[0].Position;
- long size = context.Request.PtrBuff[0].Size;
+ ulong position = context.Request.PtrBuff[0].Position;
+ ulong size = context.Request.PtrBuff[0].Size;
- int clientId = context.Memory.Read<int>((ulong)position);
+ int clientId = context.Memory.Read<int>(position);
context.ResponseData.Write(GeneralServiceManager.Get(clientId).IsAnyInternetRequestAccepted);
diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs
index ab10928f..d3a89178 100644
--- a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs
@@ -12,11 +12,11 @@
byte source = (byte)context.RequestData.ReadInt64();
ulong titleId = context.RequestData.ReadUInt64();
- long position = context.Request.ReceiveBuff[0].Position;
+ ulong position = context.Request.ReceiveBuff[0].Position;
byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray();
- context.Memory.Write((ulong)position, nacpData);
+ context.Memory.Write(position, nacpData);
return ResultCode.Success;
}
diff --git a/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs
index 2ea3ee11..7ee74370 100644
--- a/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs
@@ -20,11 +20,11 @@ namespace Ryujinx.HLE.HOS.Services.Ns
// SetDefaultDeliveryTarget(pid, buffer<bytes, 5> unknown)
public ResultCode SetDefaultDeliveryTarget(ServiceCtx context)
{
- long inBufferPosition = context.Request.SendBuff[0].Position;
- long inBufferSize = context.Request.SendBuff[0].Size;
+ ulong inBufferPosition = context.Request.SendBuff[0].Position;
+ ulong inBufferSize = context.Request.SendBuff[0].Size;
byte[] buffer = new byte[inBufferSize];
- context.Memory.Read((ulong)inBufferPosition, buffer);
+ context.Memory.Read(inBufferPosition, buffer);
// NOTE: Service use the pid to call arp:r GetApplicationLaunchProperty and store it in internal field.
// Then it seems to use the buffer content and compare it with a stored linked instrusive list.
diff --git a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs
index ff57beb0..3b6965d0 100644
--- a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs
@@ -11,11 +11,11 @@
byte source = (byte)context.RequestData.ReadInt64();
ulong titleId = context.RequestData.ReadUInt64();
- long position = context.Request.ReceiveBuff[0].Position;
+ ulong position = context.Request.ReceiveBuff[0].Position;
byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray();
- context.Memory.Write((ulong)position, nacpData);
+ context.Memory.Write(position, nacpData);
return ResultCode.Success;
}
diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
index 35bb4e6f..25279af3 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
@@ -73,8 +73,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
private NvResult GetIoctlArgument(ServiceCtx context, NvIoctl ioctlCommand, out Span<byte> arguments)
{
- (long inputDataPosition, long inputDataSize) = context.Request.GetBufferType0x21(0);
- (long outputDataPosition, long outputDataSize) = context.Request.GetBufferType0x22(0);
+ (ulong inputDataPosition, ulong inputDataSize) = context.Request.GetBufferType0x21(0);
+ (ulong outputDataPosition, ulong outputDataSize) = context.Request.GetBufferType0x22(0);
NvIoctl.Direction ioctlDirection = ioctlCommand.DirectionValue;
uint ioctlSize = ioctlCommand.Size;
@@ -106,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
byte[] temp = new byte[inputDataSize];
- context.Memory.Read((ulong)inputDataPosition, temp);
+ context.Memory.Read(inputDataPosition, temp);
Buffer.BlockCopy(temp, 0, outputData, 0, temp.Length);
@@ -122,7 +122,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
{
byte[] temp = new byte[inputDataSize];
- context.Memory.Read((ulong)inputDataPosition, temp);
+ context.Memory.Read(inputDataPosition, temp);
arguments = new Span<byte>(temp);
}
@@ -226,10 +226,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (errorCode == NvResult.Success)
{
- long pathPtr = context.Request.SendBuff[0].Position;
- long pathSize = context.Request.SendBuff[0].Size;
+ ulong pathPtr = context.Request.SendBuff[0].Position;
+ ulong pathSize = context.Request.SendBuff[0].Size;
- string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, pathSize);
+ string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, (long)pathSize);
fd = Open(context, path);
@@ -275,7 +275,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
- context.Memory.Write((ulong)context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
+ context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
}
}
}
@@ -470,13 +470,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
int fd = context.RequestData.ReadInt32();
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
- (long inlineInBufferPosition, long inlineInBufferSize) = context.Request.GetBufferType0x21(1);
+ (ulong inlineInBufferPosition, ulong inlineInBufferSize) = context.Request.GetBufferType0x21(1);
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
byte[] temp = new byte[inlineInBufferSize];
- context.Memory.Read((ulong)inlineInBufferPosition, temp);
+ context.Memory.Read(inlineInBufferPosition, temp);
Span<byte> inlineInBuffer = new Span<byte>(temp);
@@ -497,7 +497,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
- context.Memory.Write((ulong)context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
+ context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
}
}
}
@@ -519,13 +519,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
int fd = context.RequestData.ReadInt32();
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
- (long inlineOutBufferPosition, long inlineOutBufferSize) = context.Request.GetBufferType0x22(1);
+ (ulong inlineOutBufferPosition, ulong inlineOutBufferSize) = context.Request.GetBufferType0x22(1);
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
byte[] temp = new byte[inlineOutBufferSize];
- context.Memory.Read((ulong)inlineOutBufferPosition, temp);
+ context.Memory.Read(inlineOutBufferPosition, temp);
Span<byte> inlineOutBuffer = new Span<byte>(temp);
@@ -546,8 +546,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
- context.Memory.Write((ulong)context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
- context.Memory.Write((ulong)inlineOutBufferPosition, inlineOutBuffer.ToArray());
+ context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
+ context.Memory.Write(inlineOutBufferPosition, inlineOutBuffer.ToArray());
}
}
}
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs
index 1f24ab7d..44746db6 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs
@@ -1,6 +1,5 @@
using Ryujinx.Common.Collections;
using System.Collections.Generic;
-using Ryujinx.Common;
using System;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Common.Logging;
@@ -198,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
{
bool reachedEndOfAddresses = false;
ulong targetAddress;
- if(start == DefaultStart)
+ if (start == DefaultStart)
{
Logger.Debug?.Print(LogClass.ServiceNv, $"Target address set to start of the last available range: 0x{_list.Last.Value:X}.");
targetAddress = _list.Last.Value;
@@ -301,7 +300,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
freeAddressStartPosition = floorAddress;
if (floorAddress != InvalidAddress)
{
- return !(gpuVa >= floorAddress && ((gpuVa + size) < _tree.Get(floorAddress)));
+ return !(gpuVa >= floorAddress && ((gpuVa + size) <= _tree.Get(floorAddress)));
}
}
return true;
diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs
index 8313a690..4f36d401 100644
--- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs
+++ b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs
@@ -117,8 +117,8 @@ namespace Ryujinx.HLE.HOS.Services.Prepo
return ResultCode.InvalidState;
}
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
if (inputSize == 0)
{
@@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Prepo
byte[] inputBuffer = new byte[inputSize];
- context.Memory.Read((ulong)inputPosition, inputBuffer);
+ context.Memory.Read(inputPosition, inputBuffer);
Logger.Info?.Print(LogClass.ServicePrepo, ReadReportBuffer(inputBuffer, gameRoom, userId));
diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs
index c8277699..8070cf54 100644
--- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs
@@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
_owner = null;
}
- private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, long nrrAddress, long nrrSize)
+ private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize)
{
nrrInfo = null;
@@ -71,12 +71,12 @@ namespace Ryujinx.HLE.HOS.Services.Ro
{
byte[] temp = new byte[0x20];
- _owner.CpuMemory.Read((ulong)(nrrAddress + header.HashOffset + (i * 0x20)), temp);
+ _owner.CpuMemory.Read(nrrAddress + header.HashOffset + (uint)(i * 0x20), temp);
hashes.Add(temp);
}
- nrrInfo = new NrrInfo(nrrAddress, header, hashes);
+ nrrInfo = new NrrInfo((ulong)nrrAddress, header, hashes);
return ResultCode.Success;
}
@@ -333,7 +333,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
process.CpuMemory.Write(roStart, relocatableObject.Ro);
process.CpuMemory.Write(dataStart, relocatableObject.Data);
- MemoryHelper.FillWithZeros(process.CpuMemory, (long)bssStart, (int)(bssEnd - bssStart));
+ MemoryHelper.FillWithZeros(process.CpuMemory, bssStart, (int)(bssEnd - bssStart));
KernelResult result;
@@ -354,7 +354,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return process.MemoryManager.SetProcessMemoryPermission(dataStart, bssEnd - dataStart, KMemoryPermission.ReadAndWrite);
}
- private ResultCode RemoveNrrInfo(long nrrAddress)
+ private ResultCode RemoveNrrInfo(ulong nrrAddress)
{
foreach (NrrInfo info in _nrrInfos)
{
@@ -508,8 +508,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
// pid placeholder, zero
context.RequestData.ReadUInt64();
- long nrrAddress = context.RequestData.ReadInt64();
- long nrrSize = context.RequestData.ReadInt64();
+ ulong nrrAddress = context.RequestData.ReadUInt64();
+ ulong nrrSize = context.RequestData.ReadUInt64();
if (result == ResultCode.Success)
{
@@ -541,7 +541,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
// pid placeholder, zero
context.RequestData.ReadUInt64();
- long nrrHeapAddress = context.RequestData.ReadInt64();
+ ulong nrrHeapAddress = context.RequestData.ReadUInt64();
if (result == ResultCode.Success)
{
diff --git a/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs
index 8e038fcb..45c34f1c 100644
--- a/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs
+++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs
@@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Ro
{
public NrrHeader Header { get; private set; }
public List<byte[]> Hashes { get; private set; }
- public long NrrAddress { get; private set; }
+ public ulong NrrAddress { get; private set; }
- public NrrInfo(long nrrAddress, NrrHeader header, List<byte[]> hashes)
+ public NrrInfo(ulong nrrAddress, NrrHeader header, List<byte[]> hashes)
{
NrrAddress = nrrAddress;
Header = header;
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs
index aaaf26e4..3449e108 100644
--- a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs
@@ -15,11 +15,11 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false)
{
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
UserId userId = byUserId ? context.RequestData.ReadStruct<UserId>() : new UserId();
@@ -35,9 +35,9 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
List<ulong> titleIds = new List<ulong>();
- for (int i = 0; i < inputSize / sizeof(ulong); i++)
+ for (ulong i = 0; i < inputSize / sizeof(ulong); i++)
{
- titleIds.Add(context.Memory.Read<ulong>((ulong)inputPosition));
+ titleIds.Add(context.Memory.Read<ulong>(inputPosition));
}
if (queryCapability == PlayLogQueryCapability.WhiteList)
@@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++)
{
- MemoryHelper.Write(context.Memory, outputPosition + (i * Marshal.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value);
+ MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Marshal.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value);
}
context.ResponseData.Write(filteredApplicationPlayStatistics.Count());
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs
index 1a4bc5bd..d7397f7d 100644
--- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs
@@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
for (SharedFontType type = 0; type < SharedFontType.Count; type++)
{
- int offset = (int)type * 4;
+ uint offset = (uint)type * 4;
if (!AddFontToOrderOfPriorityList(context, type, offset))
{
@@ -103,27 +103,27 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
return ResultCode.Success;
}
- private bool AddFontToOrderOfPriorityList(ServiceCtx context, SharedFontType fontType, int offset)
+ private bool AddFontToOrderOfPriorityList(ServiceCtx context, SharedFontType fontType, uint offset)
{
- long typesPosition = context.Request.ReceiveBuff[0].Position;
- long typesSize = context.Request.ReceiveBuff[0].Size;
+ ulong typesPosition = context.Request.ReceiveBuff[0].Position;
+ ulong typesSize = context.Request.ReceiveBuff[0].Size;
- long offsetsPosition = context.Request.ReceiveBuff[1].Position;
- long offsetsSize = context.Request.ReceiveBuff[1].Size;
+ ulong offsetsPosition = context.Request.ReceiveBuff[1].Position;
+ ulong offsetsSize = context.Request.ReceiveBuff[1].Size;
- long fontSizeBufferPosition = context.Request.ReceiveBuff[2].Position;
- long fontSizeBufferSize = context.Request.ReceiveBuff[2].Size;
+ ulong fontSizeBufferPosition = context.Request.ReceiveBuff[2].Position;
+ ulong fontSizeBufferSize = context.Request.ReceiveBuff[2].Size;
- if ((uint)offset + 4 > (uint)typesSize ||
- (uint)offset + 4 > (uint)offsetsSize ||
- (uint)offset + 4 > (uint)fontSizeBufferSize)
+ if (offset + 4 > (uint)typesSize ||
+ offset + 4 > (uint)offsetsSize ||
+ offset + 4 > (uint)fontSizeBufferSize)
{
return false;
}
- context.Memory.Write((ulong)(typesPosition + offset), (int)fontType);
- context.Memory.Write((ulong)(offsetsPosition + offset), context.Device.System.Font.GetSharedMemoryAddressOffset(fontType));
- context.Memory.Write((ulong)(fontSizeBufferPosition + offset), context.Device.System.Font.GetFontSize(fontType));
+ context.Memory.Write(typesPosition + offset, (int)fontType);
+ context.Memory.Write(offsetsPosition + offset, context.Device.System.Font.GetSharedMemoryAddressOffset(fontType));
+ context.Memory.Write(fontSizeBufferPosition + offset, context.Device.System.Font.GetFontSize(fontType));
return true;
}
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index bf638800..c9d009a9 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -35,16 +35,17 @@ namespace Ryujinx.HLE.HOS.Services
private readonly List<int> _sessionHandles = new List<int>();
private readonly List<int> _portHandles = new List<int>();
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
- private readonly Dictionary<int, IpcService> _ports = new Dictionary<int, IpcService>();
+ private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
public ManualResetEvent InitDone { get; }
- public IpcService SmObject { get; set; }
+ public Func<IpcService> SmObjectFactory { get; }
public string Name { get; }
- public ServerBase(KernelContext context, string name)
+ public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
{
InitDone = new ManualResetEvent(false);
Name = name;
+ SmObjectFactory = smObjectFactory;
_context = context;
const ProcessCreationFlags flags =
@@ -58,10 +59,10 @@ namespace Ryujinx.HLE.HOS.Services
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
}
- private void AddPort(int serverPortHandle, IpcService obj)
+ private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
{
_portHandles.Add(serverPortHandle);
- _ports.Add(serverPortHandle, obj);
+ _ports.Add(serverPortHandle, objectFactory);
}
public void AddSessionObj(KServerSession serverSession, IpcService obj)
@@ -80,11 +81,11 @@ namespace Ryujinx.HLE.HOS.Services
{
_selfProcess = KernelStatic.GetCurrentProcess();
- if (SmObject != null)
+ if (SmObjectFactory != null)
{
_context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle);
- AddPort(serverPortHandle, SmObject);
+ AddPort(serverPortHandle, SmObjectFactory);
InitDone.Set();
}
@@ -141,7 +142,9 @@ namespace Ryujinx.HLE.HOS.Services
// We got a new connection, accept the session to allow servicing future requests.
if (_context.Syscall.AcceptSession(handles[signaledIndex], out int serverSessionHandle) == KernelResult.Success)
{
- AddSessionObj(serverSessionHandle, _ports[handles[signaledIndex]]);
+ IpcService obj = _ports[handles[signaledIndex]].Invoke();
+
+ AddSessionObj(serverSessionHandle, obj);
}
}
@@ -180,17 +183,18 @@ namespace Ryujinx.HLE.HOS.Services
{
for (int i = 0; i < request.RecvListBuff.Count; i++)
{
- int size = BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2));
+ ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2));
- response.PtrBuff.Add(new IpcPtrBuffDesc((long)tempAddr, i, size));
+ response.PtrBuff.Add(new IpcPtrBuffDesc(tempAddr, (uint)i, size));
- request.RecvListBuff[i] = new IpcRecvListBuffDesc((long)tempAddr, size);
+ request.RecvListBuff[i] = new IpcRecvListBuffDesc(tempAddr, size);
- tempAddr += (ulong)size;
+ tempAddr += size;
}
}
bool shouldReply = true;
+ bool isTipcCommunication = false;
using (MemoryStream raw = new MemoryStream(request.RawData))
{
@@ -269,6 +273,8 @@ namespace Ryujinx.HLE.HOS.Services
// If the type is past 0xF, we are using TIPC
else if (request.Type > IpcMessageType.TipcCloseSession)
{
+ isTipcCommunication = true;
+
// Response type is always the same as request on TIPC.
response.Type = request.Type;
@@ -290,13 +296,19 @@ namespace Ryujinx.HLE.HOS.Services
response.RawData = resMs.ToArray();
}
+
+ process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
}
else
{
throw new NotImplementedException(request.Type.ToString());
}
- process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
+ if (!isTipcCommunication)
+ {
+ process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
+ }
+
return shouldReply;
}
}
diff --git a/Ryujinx.HLE/HOS/Services/ServiceConfiguration.cs b/Ryujinx.HLE/HOS/Services/ServiceConfiguration.cs
deleted file mode 100644
index d73c76d9..00000000
--- a/Ryujinx.HLE/HOS/Services/ServiceConfiguration.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services
-{
- public static class ServiceConfiguration
- {
- public static bool IgnoreMissingServices { get; set; }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs
index 2932ee22..6f106789 100644
--- a/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs
@@ -190,17 +190,17 @@ namespace Ryujinx.HLE.HOS.Services.Settings
break;
}
- context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, keyCodeMap);
+ context.Memory.Write(context.Request.ReceiveBuff[0].Position, keyCodeMap);
if (version == 1 && context.Device.System.State.DesiredKeyboardLayout == (long)KeyboardLayout.Default)
{
- context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, (byte)0x01);
+ context.Memory.Write(context.Request.ReceiveBuff[0].Position, (byte)0x01);
}
return ResultCode.Success;
}
- public ResultCode GetAvailableLanguagesCodesImpl(ServiceCtx context, long position, long size, int maxSize)
+ public ResultCode GetAvailableLanguagesCodesImpl(ServiceCtx context, ulong position, ulong size, int maxSize)
{
int count = (int)(size / 8);
@@ -211,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
for (int index = 0; index < count; index++)
{
- context.Memory.Write((ulong)position, SystemStateMgr.GetLanguageCode(index));
+ context.Memory.Write(position, SystemStateMgr.GetLanguageCode(index));
position += 8;
}
diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs
index 6bd6866d..54076b4b 100644
--- a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs
@@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
// GetFirmwareVersion2() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100>
public ResultCode GetFirmwareVersion2(ServiceCtx context)
{
- long replyPos = context.Request.RecvListBuff[0].Position;
+ ulong replyPos = context.Request.RecvListBuff[0].Position;
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x100L);
@@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
if (firmwareData != null)
{
- context.Memory.Write((ulong)replyPos, firmwareData);
+ context.Memory.Write(replyPos, firmwareData);
return ResultCode.Success;
}
@@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
writer.Write(Encoding.ASCII.GetBytes(build));
- context.Memory.Write((ulong)replyPos, ms.ToArray());
+ context.Memory.Write(replyPos, ms.ToArray());
}
return ResultCode.Success;
@@ -110,19 +110,19 @@ namespace Ryujinx.HLE.HOS.Services.Settings
// GetSettingsItemValueSize(buffer<nn::settings::SettingsName, 0x19>, buffer<nn::settings::SettingsItemKey, 0x19>) -> u64
public ResultCode GetSettingsItemValueSize(ServiceCtx context)
{
- long classPos = context.Request.PtrBuff[0].Position;
- long classSize = context.Request.PtrBuff[0].Size;
+ ulong classPos = context.Request.PtrBuff[0].Position;
+ ulong classSize = context.Request.PtrBuff[0].Size;
- long namePos = context.Request.PtrBuff[1].Position;
- long nameSize = context.Request.PtrBuff[1].Size;
+ ulong namePos = context.Request.PtrBuff[1].Position;
+ ulong nameSize = context.Request.PtrBuff[1].Size;
byte[] classBuffer = new byte[classSize];
- context.Memory.Read((ulong)classPos, classBuffer);
+ context.Memory.Read(classPos, classBuffer);
byte[] nameBuffer = new byte[nameSize];
- context.Memory.Read((ulong)namePos, nameBuffer);
+ context.Memory.Read(namePos, nameBuffer);
string askedSetting = Encoding.ASCII.GetString(classBuffer).Trim('\0') + "!" + Encoding.ASCII.GetString(nameBuffer).Trim('\0');
@@ -159,22 +159,22 @@ namespace Ryujinx.HLE.HOS.Services.Settings
// GetSettingsItemValue(buffer<nn::settings::SettingsName, 0x19, 0x48>, buffer<nn::settings::SettingsItemKey, 0x19, 0x48>) -> (u64, buffer<unknown, 6, 0>)
public ResultCode GetSettingsItemValue(ServiceCtx context)
{
- long classPos = context.Request.PtrBuff[0].Position;
- long classSize = context.Request.PtrBuff[0].Size;
+ ulong classPos = context.Request.PtrBuff[0].Position;
+ ulong classSize = context.Request.PtrBuff[0].Size;
- long namePos = context.Request.PtrBuff[1].Position;
- long nameSize = context.Request.PtrBuff[1].Size;
+ ulong namePos = context.Request.PtrBuff[1].Position;
+ ulong nameSize = context.Request.PtrBuff[1].Size;
- long replyPos = context.Request.ReceiveBuff[0].Position;
- long replySize = context.Request.ReceiveBuff[0].Size;
+ ulong replyPos = context.Request.ReceiveBuff[0].Position;
+ ulong replySize = context.Request.ReceiveBuff[0].Size;
byte[] classBuffer = new byte[classSize];
- context.Memory.Read((ulong)classPos, classBuffer);
+ context.Memory.Read(classPos, classBuffer);
byte[] nameBuffer = new byte[nameSize];
- context.Memory.Read((ulong)namePos, nameBuffer);
+ context.Memory.Read(namePos, nameBuffer);
string askedSetting = Encoding.ASCII.GetString(classBuffer).Trim('\0') + "!" + Encoding.ASCII.GetString(nameBuffer).Trim('\0');
@@ -186,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
if (nxSetting is string stringValue)
{
- if (stringValue.Length + 1 > replySize)
+ if ((ulong)(stringValue.Length + 1) > replySize)
{
Logger.Error?.Print(LogClass.ServiceSet, $"{askedSetting} String value size is too big!");
}
@@ -209,7 +209,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
throw new NotImplementedException(nxSetting.GetType().Name);
}
- context.Memory.Write((ulong)replyPos, settingBuffer);
+ context.Memory.Write(replyPos, settingBuffer);
Logger.Debug?.Print(LogClass.ServiceSet, $"{askedSetting} set value: {nxSetting} as {nxSetting.GetType()}");
}
@@ -235,8 +235,9 @@ namespace Ryujinx.HLE.HOS.Services.Settings
public byte[] GetFirmwareData(Switch device)
{
- long titleId = 0x0100000000000809;
- string contentPath = device.System.ContentManager.GetInstalledContentPath(titleId, StorageId.NandSystem, NcaContentType.Data);
+ const ulong SystemVersionTitleId = 0x0100000000000809;
+
+ string contentPath = device.System.ContentManager.GetInstalledContentPath(SystemVersionTitleId, StorageId.NandSystem, NcaContentType.Data);
if (string.IsNullOrWhiteSpace(contentPath))
{
diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index d5dabb2d..8b1ec5b8 100644
--- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -15,9 +15,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
class IUserInterface : IpcService
{
- private Dictionary<string, Type> _services;
+ private static Dictionary<string, Type> _services;
- private readonly ConcurrentDictionary<string, KPort> _registeredServices;
+ private static readonly ConcurrentDictionary<string, KPort> _registeredServices;
private readonly ServerBase _commonServer;
@@ -25,16 +25,17 @@ namespace Ryujinx.HLE.HOS.Services.Sm
public IUserInterface(KernelContext context)
{
+ _commonServer = new ServerBase(context, "CommonServer");
+ }
+
+ static IUserInterface()
+ {
_registeredServices = new ConcurrentDictionary<string, KPort>();
_services = Assembly.GetExecutingAssembly().GetTypes()
.SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
.Select(service => (((ServiceAttribute)service).Name, type)))
.ToDictionary(service => service.Name, service => service.type);
-
- TrySetServer(new ServerBase(context, "SmServer") { SmObject = this });
-
- _commonServer = new ServerBase(context, "CommonServer");
}
[CommandHipc(0)]
@@ -47,9 +48,16 @@ namespace Ryujinx.HLE.HOS.Services.Sm
return ResultCode.Success;
}
- [CommandHipc(1)]
[CommandTipc(1)] // 12.0.0+
// GetService(ServiceName name) -> handle<move, session>
+ public ResultCode GetServiceTipc(ServiceCtx context)
+ {
+ context.Response.HandleDesc = IpcHandleDesc.MakeMove(0);
+
+ return GetService(context);
+ }
+
+ [CommandHipc(1)]
public ResultCode GetService(ServiceCtx context)
{
if (!_isInitialized)
@@ -90,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
}
else
{
- if (ServiceConfiguration.IgnoreMissingServices)
+ if (context.Device.Configuration.IgnoreMissingServices)
{
Logger.Warning?.Print(LogClass.Service, $"Missing service {name} ignored");
}
@@ -142,6 +150,8 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
if (!_isInitialized)
{
+ context.Response.HandleDesc = IpcHandleDesc.MakeMove(0);
+
return ResultCode.NotInitialized;
}
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
index 1246c31b..63f639cb 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
@@ -197,28 +197,28 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return WriteBsdResult(context, _sockets.Count - 1);
}
- private IPEndPoint ParseSockAddr(ServiceCtx context, long bufferPosition, long bufferSize)
+ private IPEndPoint ParseSockAddr(ServiceCtx context, ulong bufferPosition, ulong bufferSize)
{
- int size = context.Memory.Read<byte>((ulong)bufferPosition);
- int family = context.Memory.Read<byte>((ulong)bufferPosition + 1);
- int port = BinaryPrimitives.ReverseEndianness(context.Memory.Read<ushort>((ulong)bufferPosition + 2));
+ int size = context.Memory.Read<byte>(bufferPosition);
+ int family = context.Memory.Read<byte>(bufferPosition + 1);
+ int port = BinaryPrimitives.ReverseEndianness(context.Memory.Read<ushort>(bufferPosition + 2));
byte[] rawIp = new byte[4];
- context.Memory.Read((ulong)bufferPosition + 4, rawIp);
+ context.Memory.Read(bufferPosition + 4, rawIp);
return new IPEndPoint(new IPAddress(rawIp), port);
}
- private void WriteSockAddr(ServiceCtx context, long bufferPosition, IPEndPoint endPoint)
+ private void WriteSockAddr(ServiceCtx context, ulong bufferPosition, IPEndPoint endPoint)
{
- context.Memory.Write((ulong)bufferPosition, (byte)0);
- context.Memory.Write((ulong)bufferPosition + 1, (byte)endPoint.AddressFamily);
- context.Memory.Write((ulong)bufferPosition + 2, BinaryPrimitives.ReverseEndianness((ushort)endPoint.Port));
- context.Memory.Write((ulong)bufferPosition + 4, endPoint.Address.GetAddressBytes());
+ context.Memory.Write(bufferPosition, (byte)0);
+ context.Memory.Write(bufferPosition + 1, (byte)endPoint.AddressFamily);
+ context.Memory.Write(bufferPosition + 2, BinaryPrimitives.ReverseEndianness((ushort)endPoint.Port));
+ context.Memory.Write(bufferPosition + 4, endPoint.Address.GetAddressBytes());
}
- private void WriteSockAddr(ServiceCtx context, long bufferPosition, BsdSocket socket, bool isRemote)
+ private void WriteSockAddr(ServiceCtx context, ulong bufferPosition, BsdSocket socket, bool isRemote)
{
IPEndPoint endPoint = (isRemote ? socket.Handle.RemoteEndPoint : socket.Handle.LocalEndPoint) as IPEndPoint;
@@ -282,13 +282,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
// Open(u32 flags, array<unknown, 0x21> path) -> (i32 ret, u32 bsd_errno)
public ResultCode Open(ServiceCtx context)
{
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
int flags = context.RequestData.ReadInt32();
byte[] rawPath = new byte[bufferSize];
- context.Memory.Read((ulong)bufferPosition, rawPath);
+ context.Memory.Read(bufferPosition, rawPath);
string path = Encoding.ASCII.GetString(rawPath);
@@ -317,10 +317,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
int fdsCount = context.RequestData.ReadInt32();
int timeout = context.RequestData.ReadInt32();
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
- if (timeout < -1 || fdsCount < 0 || (fdsCount * 8) > bufferSize)
+ if (timeout < -1 || fdsCount < 0 || (ulong)(fdsCount * 8) > bufferSize)
{
return WriteBsdResult(context, -1, LinuxError.EINVAL);
}
@@ -329,7 +329,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
for (int i = 0; i < fdsCount; i++)
{
- int socketFd = context.Memory.Read<int>((ulong)(bufferPosition + i * 8));
+ int socketFd = context.Memory.Read<int>(bufferPosition + (ulong)i * 8);
BsdSocket socket = RetrieveSocket(socketFd);
@@ -337,8 +337,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
return WriteBsdResult(context, -1, LinuxError.EBADF);}
- PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>((ulong)(bufferPosition + i * 8 + 4));
- PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>((ulong)(bufferPosition + i * 8 + 6));
+ PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>(bufferPosition + (ulong)i * 8 + 4);
+ PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>(bufferPosition + (ulong)i * 8 + 6);
events[i] = new PollEvent(socketFd, socket, inputEvents, outputEvents);
}
@@ -413,8 +413,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
for (int i = 0; i < fdsCount; i++)
{
PollEvent Event = events[i];
- context.Memory.Write((ulong)(bufferPosition + i * 8), Event.SocketFd);
- context.Memory.Write((ulong)(bufferPosition + i * 8 + 4), (short)Event.InputEvents);
+ context.Memory.Write(bufferPosition + (ulong)i * 8, Event.SocketFd);
+ context.Memory.Write(bufferPosition + (ulong)i * 8 + 4, (short)Event.InputEvents);
PollEvent.EventTypeMask outputEvents = 0;
@@ -443,7 +443,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
outputEvents |= PollEvent.EventTypeMask.Output;
}
- context.Memory.Write((ulong)(bufferPosition + i * 8 + 6), (short)outputEvents);
+ context.Memory.Write(bufferPosition + (ulong)i * 8 + 6, (short)outputEvents);
}
return WriteBsdResult(context, readEvents.Count + writeEvents.Count + errorEvents.Count, LinuxError.SUCCESS);
@@ -467,7 +467,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
int socketFd = context.RequestData.ReadInt32();
SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
- (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22();
+ (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -489,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
result = socket.Handle.Receive(receivedBuffer, socketFlags);
errno = SetResultErrno(socket.Handle, result);
- context.Memory.Write((ulong)receivePosition, receivedBuffer);
+ context.Memory.Write(receivePosition, receivedBuffer);
}
catch (SocketException exception)
{
@@ -507,8 +507,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
int socketFd = context.RequestData.ReadInt32();
SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
- (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22();
- (long sockAddrOutPosition, long sockAddrOutSize) = context.Request.GetBufferType0x22(1);
+ (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22();
+ (ulong sockAddrOutPosition, ulong sockAddrOutSize) = context.Request.GetBufferType0x22(1);
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -532,7 +532,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
result = socket.Handle.ReceiveFrom(receivedBuffer, receivedBuffer.Length, socketFlags, ref endPoint);
errno = SetResultErrno(socket.Handle, result);
- context.Memory.Write((ulong)receivePosition, receivedBuffer);
+ context.Memory.Write(receivePosition, receivedBuffer);
WriteSockAddr(context, sockAddrOutPosition, (IPEndPoint)endPoint);
}
catch (SocketException exception)
@@ -551,7 +551,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
int socketFd = context.RequestData.ReadInt32();
SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
- (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
+ (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -569,7 +569,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
byte[] sendBuffer = new byte[sendSize];
- context.Memory.Read((ulong)sendPosition, sendBuffer);
+ context.Memory.Read(sendPosition, sendBuffer);
try
{
@@ -593,8 +593,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
int socketFd = context.RequestData.ReadInt32();
SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
- (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(1);
+ (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(1);
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -612,7 +612,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
byte[] sendBuffer = new byte[sendSize];
- context.Memory.Read((ulong)sendPosition, sendBuffer);
+ context.Memory.Read(sendPosition, sendBuffer);
EndPoint endPoint = ParseSockAddr(context, bufferPosition, bufferSize);
@@ -637,7 +637,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
int socketFd = context.RequestData.ReadInt32();
- (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22();
+ (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -692,7 +692,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
int socketFd = context.RequestData.ReadInt32();
- (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -722,7 +722,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
int socketFd = context.RequestData.ReadInt32();
- (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -751,7 +751,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
int socketFd = context.RequestData.ReadInt32();
- (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22();
+ (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -774,7 +774,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
int socketFd = context.RequestData.ReadInt32();
- (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22();
+ (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -799,7 +799,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
int level = context.RequestData.ReadInt32();
int optionName = context.RequestData.ReadInt32();
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -866,10 +866,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
case BsdIoctl.AtMark:
errno = LinuxError.SUCCESS;
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22();
// FIXME: OOB not implemented.
- context.Memory.Write((ulong)bufferPosition, 0);
+ context.Memory.Write(bufferPosition, 0);
break;
default:
@@ -917,7 +917,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return WriteBsdResult(context, result, errno);
}
- private LinuxError HandleGetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize)
+ private LinuxError HandleGetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, ulong optionValuePosition, ulong optionValueSize)
{
try
{
@@ -938,13 +938,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
case SocketOptionName.Type:
case SocketOptionName.Linger:
socket.Handle.GetSocketOption(SocketOptionLevel.Socket, optionName, optionValue);
- context.Memory.Write((ulong)optionValuePosition, optionValue);
+ context.Memory.Write(optionValuePosition, optionValue);
return LinuxError.SUCCESS;
case (SocketOptionName)0x200:
socket.Handle.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue);
- context.Memory.Write((ulong)optionValuePosition, optionValue);
+ context.Memory.Write(optionValuePosition, optionValue);
return LinuxError.SUCCESS;
@@ -960,7 +960,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
}
}
- private LinuxError HandleSetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize)
+ private LinuxError HandleSetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, ulong optionValuePosition, ulong optionValueSize)
{
try
{
@@ -1013,7 +1013,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
int level = context.RequestData.ReadInt32();
int optionName = context.RequestData.ReadInt32();
- (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -1105,7 +1105,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
int socketFd = context.RequestData.ReadInt32();
- (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
+ (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -1115,7 +1115,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
byte[] sendBuffer = new byte[sendSize];
- context.Memory.Read((ulong)sendPosition, sendBuffer);
+ context.Memory.Read(sendPosition, sendBuffer);
try
{
@@ -1137,7 +1137,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
int socketFd = context.RequestData.ReadInt32();
- (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22();
+ (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22();
LinuxError errno = LinuxError.EBADF;
BsdSocket socket = RetrieveSocket(socketFd);
@@ -1151,7 +1151,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
result = socket.Handle.Receive(receivedBuffer);
errno = SetResultErrno(socket.Handle, result);
- context.Memory.Write((ulong)receivePosition, receivedBuffer);
+ context.Memory.Write(receivePosition, receivedBuffer);
}
catch (SocketException exception)
{
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
index f6b83eaa..f58953d8 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
@@ -3,6 +3,8 @@ using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Services.Settings;
using Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager;
+using Ryujinx.HLE.HOS.Services.Sockets.Nsd.Types;
+using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
@@ -11,8 +13,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
[Service("nsd:u")] // Max sessions: 20
class IManager : IpcService
{
- private NsdSettings _nsdSettings;
- private FqdnResolver _fqdnResolver;
+ private readonly NsdSettings _nsdSettings;
+ private readonly FqdnResolver _fqdnResolver;
private bool _isInitialized = false;
@@ -20,12 +22,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
{
// TODO: Load nsd settings through the savedata 0x80000000000000B0 (nsdsave:/).
- NxSettings.Settings.TryGetValue("nsd!test_mode", out object testMode);
+ if (!NxSettings.Settings.TryGetValue("nsd!test_mode", out object testMode))
+ {
+ // return ResultCode.InvalidSettingsValue;
+ }
+
+ if (!NxSettings.Settings.TryGetValue("nsd!environment_identifier", out object environmentIdentifier))
+ {
+ // return ResultCode.InvalidSettingsValue;
+ }
_nsdSettings = new NsdSettings
{
Initialized = true,
- TestMode = (bool)testMode
+ TestMode = (bool)testMode,
+ Environment = (string)environmentIdentifier
};
_fqdnResolver = new FqdnResolver(_nsdSettings);
@@ -33,37 +44,35 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
_isInitialized = true;
}
+ [CommandHipc(5)] // 11.0.0+
+ // GetSettingUrl() -> buffer<unknown<0x100>, 0x16>
+ public ResultCode GetSettingUrl(ServiceCtx context)
+ {
+ throw new ServiceNotImplementedException(this, context, false);
+ }
+
[CommandHipc(10)]
// GetSettingName() -> buffer<unknown<0x100>, 0x16>
public ResultCode GetSettingName(ServiceCtx context)
{
- (long outputPosition, long outputSize) = context.Request.GetBufferType0x22();
-
- ResultCode result = _fqdnResolver.GetSettingName(context, out string settingName);
-
- if (result == ResultCode.Success)
- {
- byte[] settingNameBuffer = Encoding.UTF8.GetBytes(settingName + '\0');
-
- context.Memory.Write((ulong)outputPosition, settingNameBuffer);
- }
-
- return result;
+ throw new ServiceNotImplementedException(this, context, false);
}
[CommandHipc(11)]
- // GetEnvironmentIdentifier() -> buffer<unknown<8>, 0x16>
+ // GetEnvironmentIdentifier() -> buffer<bytes<8> environment_identifier, 0x16>
public ResultCode GetEnvironmentIdentifier(ServiceCtx context)
{
- (long outputPosition, long outputSize) = context.Request.GetBufferType0x22();
+ (ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22();
- ResultCode result = _fqdnResolver.GetEnvironmentIdentifier(context, out string identifier);
+ MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
+
+ ResultCode result = _fqdnResolver.GetEnvironmentIdentifier(out string identifier);
if (result == ResultCode.Success)
{
- byte[] identifierBuffer = Encoding.UTF8.GetBytes(identifier + '\0');
+ byte[] identifierBuffer = Encoding.UTF8.GetBytes(identifier);
- context.Memory.Write((ulong)outputPosition, identifierBuffer);
+ context.Memory.Write(outputPosition, identifierBuffer);
}
return result;
@@ -122,23 +131,35 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
throw new ServiceNotImplementedException(this, context, false);
}
- [CommandHipc(15)]
- // Unknown(bytes<1>)
- public ResultCode Unknown(ServiceCtx context)
+ [CommandHipc(15)] // 4.0.0+
+ // SetChangeEnvironmentIdentifierDisabled(bytes<1>)
+ public ResultCode SetChangeEnvironmentIdentifierDisabled(ServiceCtx context)
{
- throw new ServiceNotImplementedException(this, context, false);
+ byte disabled = context.RequestData.ReadByte();
+
+ // TODO: When sys:set service calls will be implemented
+ /*
+ if (((nn::settings::detail::GetServiceDiscoveryControlSettings() ^ disabled) & 1) != 0 )
+ {
+ nn::settings::detail::SetServiceDiscoveryControlSettings(disabled & 1);
+ }
+ */
+
+ Logger.Stub?.PrintStub(LogClass.ServiceNsd, new { disabled });
+
+ return ResultCode.Success;
}
[CommandHipc(20)]
// Resolve(buffer<unknown<0x100>, 0x15>) -> buffer<unknown<0x100>, 0x16>
public ResultCode Resolve(ServiceCtx context)
{
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
ResultCode result = _fqdnResolver.ResolveEx(context, out _, out string resolvedAddress);
- if (resolvedAddress.Length > outputSize)
+ if ((ulong)resolvedAddress.Length > outputSize)
{
return ResultCode.InvalidArgument;
}
@@ -147,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
- context.Memory.Write((ulong)outputPosition, resolvedAddressBuffer);
+ context.Memory.Write(outputPosition, resolvedAddressBuffer);
return result;
}
@@ -156,12 +177,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
// ResolveEx(buffer<unknown<0x100>, 0x15>) -> (u32, buffer<unknown<0x100>, 0x16>)
public ResultCode ResolveEx(ServiceCtx context)
{
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress);
- if (resolvedAddress.Length > outputSize)
+ if ((ulong)resolvedAddress.Length > outputSize)
{
return ResultCode.InvalidArgument;
}
@@ -170,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
- context.Memory.Write((ulong)outputPosition, resolvedAddressBuffer);
+ context.Memory.Write(outputPosition, resolvedAddressBuffer);
context.ResponseData.Write((int)errorCode);
@@ -226,6 +247,24 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
throw new ServiceNotImplementedException(this, context, false);
}
+ [CommandHipc(51)] // 9.0.0+
+ // WriteTestParameter(buffer<?>)
+ public ResultCode WriteTestParameter(ServiceCtx context)
+ {
+ // TODO: Write test parameter through the savedata 0x80000000000000B0 (nsdsave:/test_parameter).
+
+ throw new ServiceNotImplementedException(this, context, false);
+ }
+
+ [CommandHipc(52)] // 9.0.0+
+ // ReadTestParameter() -> buffer<?>
+ public ResultCode ReadTestParameter(ServiceCtx context)
+ {
+ // TODO: Read test parameter through the savedata 0x80000000000000B0 (nsdsave:/test_parameter).
+
+ throw new ServiceNotImplementedException(this, context, false);
+ }
+
[CommandHipc(60)]
// ReadSaveDataFromFsForTest() -> buffer<unknown<0x12bf0>, 0x16>
public ResultCode ReadSaveDataFromFsForTest(ServiceCtx context)
@@ -235,8 +274,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
return ResultCode.ServiceNotInitialized;
}
- // TODO: Call nn::nsd::detail::fs::ReadSaveDataWithOffset() at offset 0 to write the
- // whole savedata inside the buffer.
+ // TODO: Read the savedata 0x80000000000000B0 (nsdsave:/file) and write it inside the buffer.
Logger.Stub?.PrintStub(LogClass.ServiceNsd);
@@ -247,38 +285,114 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
// WriteSaveDataToFsForTest(buffer<unknown<0x12bf0>, 0x15>)
public ResultCode WriteSaveDataToFsForTest(ServiceCtx context)
{
- // NOTE: Stubbed in system module.
-
- if (_isInitialized)
+ if (!_isInitialized)
{
- return ResultCode.NotImplemented;
+ return ResultCode.ServiceNotInitialized;
}
- else
+
+ // TODO: When sys:set service calls will be implemented
+ /*
+ if (nn::settings::detail::GetSettingsItemValueSize("nsd", "test_mode") != 1)
+ {
+ return ResultCode.InvalidSettingsValue;
+ }
+ */
+
+ if (!_nsdSettings.TestMode)
{
- return ResultCode.ServiceNotInitialized;
+ return ResultCode.InvalidSettingsValue;
}
+
+ // TODO: Write the buffer inside the savedata 0x80000000000000B0 (nsdsave:/file).
+
+ Logger.Stub?.PrintStub(LogClass.ServiceNsd);
+
+ return ResultCode.Success;
}
[CommandHipc(62)]
// DeleteSaveDataOfFsForTest()
public ResultCode DeleteSaveDataOfFsForTest(ServiceCtx context)
{
- // NOTE: Stubbed in system module.
-
- if (_isInitialized)
+ if (!_isInitialized)
{
- return ResultCode.NotImplemented;
+ return ResultCode.ServiceNotInitialized;
}
- else
+
+ // TODO: When sys:set service calls will be implemented
+ /*
+ if (nn::settings::detail::GetSettingsItemValueSize("nsd", "test_mode") != 1)
+ {
+ return ResultCode.InvalidSettingsValue;
+ }
+ */
+
+ if (!_nsdSettings.TestMode)
{
- return ResultCode.ServiceNotInitialized;
+ return ResultCode.InvalidSettingsValue;
}
+
+ // TODO: Delete the savedata 0x80000000000000B0.
+
+ Logger.Stub?.PrintStub(LogClass.ServiceNsd);
+
+ return ResultCode.Success;
}
- [CommandHipc(63)]
+ [CommandHipc(63)] // 4.0.0+
// IsChangeEnvironmentIdentifierDisabled() -> bytes<1>
public ResultCode IsChangeEnvironmentIdentifierDisabled(ServiceCtx context)
{
+ // TODO: When sys:set service calls will be implemented use nn::settings::detail::GetServiceDiscoveryControlSettings()
+
+ bool disabled = false;
+
+ context.ResponseData.Write(disabled);
+
+ Logger.Stub?.PrintStub(LogClass.ServiceNsd, new { disabled });
+
+ return ResultCode.Success;
+ }
+
+ [CommandHipc(100)] // 10.0.0+
+ // GetApplicationServerEnvironmentType() -> bytes<1>
+ public ResultCode GetApplicationServerEnvironmentType(ServiceCtx context)
+ {
+ // TODO: Mount the savedata 0x80000000000000B0 (nsdsave:/test_parameter) and returns the environment type stored inside if the mount succeed.
+ // Returns ResultCode.NullOutputObject if failed.
+
+ ResultCode result = _fqdnResolver.GetEnvironmentIdentifier(out string identifier);
+
+ if (result != ResultCode.Success)
+ {
+ return result;
+ }
+
+ byte environmentType = identifier.Substring(0, 2) switch
+ {
+ "lp" => (byte)ApplicationServerEnvironmentType.Lp,
+ "sd" => (byte)ApplicationServerEnvironmentType.Sd,
+ "sp" => (byte)ApplicationServerEnvironmentType.Sp,
+ "dp" => (byte)ApplicationServerEnvironmentType.Dp,
+ _ => (byte)ApplicationServerEnvironmentType.None
+ };
+
+ context.ResponseData.Write(environmentType);
+
+ return ResultCode.Success;
+ }
+
+ [CommandHipc(101)] // 10.0.0+
+ // SetApplicationServerEnvironmentType(bytes<1>)
+ public ResultCode SetApplicationServerEnvironmentType(ServiceCtx context)
+ {
+ throw new ServiceNotImplementedException(this, context, false);
+ }
+
+ [CommandHipc(102)] // 10.0.0+
+ // DeleteApplicationServerEnvironmentType()
+ public ResultCode DeleteApplicationServerEnvironmentType(ServiceCtx context)
+ {
throw new ServiceNotImplementedException(this, context, false);
}
}
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
index 6bdf06ad..2e23f3fb 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
@@ -13,38 +13,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
_nsdSettings = nsdSettings;
}
- public ResultCode GetSettingName(ServiceCtx context, out string settingName)
+ public ResultCode GetEnvironmentIdentifier(out string identifier)
{
if (_nsdSettings.TestMode)
{
- settingName = "";
+ identifier = "err";
- return ResultCode.NotImplemented;
- }
- else
- {
- settingName = "";
-
- if (true) // TODO: Determine field (struct + 0x2C)
- {
- settingName = _nsdSettings.Environment;
-
- return ResultCode.Success;
- }
-
-#pragma warning disable CS0162
- return ResultCode.NullOutputObject;
-#pragma warning restore CS0162
- }
- }
-
- public ResultCode GetEnvironmentIdentifier(ServiceCtx context, out string identifier)
- {
- if (_nsdSettings.TestMode)
- {
- identifier = "rre";
-
- return ResultCode.NotImplemented;
+ return ResultCode.InvalidSettingsValue;
}
else
{
@@ -56,7 +31,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
public ResultCode Resolve(ServiceCtx context, string address, out string resolvedAddress)
{
- if (address != "api.sect.srv.nintendo.net" || address != "conntest.nintendowifi.net")
+ if (address == "api.sect.srv.nintendo.net" ||
+ address == "ctest.cdn.nintendo.net" ||
+ address == "ctest.cdn.n.nintendoswitch.cn" ||
+ address == "unknown.dummy.nintendo.net")
+ {
+ resolvedAddress = address;
+ }
+ else
{
// TODO: Load Environment from the savedata.
address = address.Replace("%", _nsdSettings.Environment);
@@ -87,22 +69,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
_ => address,
};
}
- else
- {
- resolvedAddress = address;
- }
return ResultCode.Success;
}
public ResultCode ResolveEx(ServiceCtx context, out ResultCode resultCode, out string resolvedAddress)
{
- long inputPosition = context.Request.SendBuff[0].Position;
- long inputSize = context.Request.SendBuff[0].Size;
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
byte[] addressBuffer = new byte[inputSize];
- context.Memory.Read((ulong)inputPosition, addressBuffer);
+ context.Memory.Read(inputPosition, addressBuffer);
string address = Encoding.UTF8.GetString(addressBuffer).TrimEnd('\0');
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs
index 0e636f9a..993fbe8a 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
Success = 0,
- NotImplemented = ( 1 << ErrorCodeShift) | ModuleId,
+ InvalidSettingsValue = ( 1 << ErrorCodeShift) | ModuleId,
InvalidObject1 = ( 3 << ErrorCodeShift) | ModuleId,
InvalidObject2 = ( 4 << ErrorCodeShift) | ModuleId,
NullOutputObject = ( 5 << ErrorCodeShift) | ModuleId,
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs
new file mode 100644
index 00000000..150bdab4
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Types
+{
+ enum ApplicationServerEnvironmentType : byte
+ {
+ None,
+ Lp,
+ Sd,
+ Sp,
+ Dp
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs
index 2b31cb1d..0a72fa87 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs
@@ -4,6 +4,6 @@
{
public bool Initialized;
public bool TestMode;
- public string Environment = "lp1"; // or "dd1" if devkit.
+ public string Environment;
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
index f86a27dd..2baeb1ef 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
@@ -24,8 +24,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
public ResultCode SetDnsAddressesPrivateRequest(ServiceCtx context)
{
uint cancelHandleRequest = context.RequestData.ReadUInt32();
- long bufferPosition = context.Request.SendBuff[0].Position;
- long bufferSize = context.Request.SendBuff[0].Size;
+ ulong bufferPosition = context.Request.SendBuff[0].Position;
+ ulong bufferSize = context.Request.SendBuff[0].Size;
// TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness.
Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest });
@@ -38,8 +38,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
public ResultCode GetDnsAddressPrivateRequest(ServiceCtx context)
{
uint cancelHandleRequest = context.RequestData.ReadUInt32();
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferSize = context.Request.ReceiveBuff[0].Size;
// TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness.
Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest });
@@ -51,11 +51,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
// GetHostByNameRequest(u8, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
public ResultCode GetHostByNameRequest(ServiceCtx context)
{
- long inputBufferPosition = context.Request.SendBuff[0].Position;
- long inputBufferSize = context.Request.SendBuff[0].Size;
+ ulong inputBufferPosition = context.Request.SendBuff[0].Position;
+ ulong inputBufferSize = context.Request.SendBuff[0].Size;
- long outputBufferPosition = context.Request.ReceiveBuff[0].Position;
- long outputBufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong outputBufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputBufferSize = context.Request.ReceiveBuff[0].Size;
return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, 0, 0);
}
@@ -64,11 +64,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
// GetHostByAddrRequest(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
public ResultCode GetHostByAddrRequest(ServiceCtx context)
{
- long inputBufferPosition = context.Request.SendBuff[0].Position;
- long inputBufferSize = context.Request.SendBuff[0].Size;
+ ulong inputBufferPosition = context.Request.SendBuff[0].Position;
+ ulong inputBufferSize = context.Request.SendBuff[0].Size;
- long outputBufferPosition = context.Request.ReceiveBuff[0].Position;
- long outputBufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong outputBufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputBufferSize = context.Request.ReceiveBuff[0].Size;
return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, 0, 0);
}
@@ -90,12 +90,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
_ => (errorCode <= NetDbError.Internal) ? "Resolver internal error" : "Unknown resolver error"
};
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferSize = context.Request.ReceiveBuff[0].Size;
- if (errorString.Length + 1 <= bufferSize)
+ if ((ulong)(errorString.Length + 1) <= bufferSize)
{
- context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0'));
+ context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0'));
resultCode = ResultCode.Success;
}
@@ -135,12 +135,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
_ => "Success"
};
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferSize = context.Request.ReceiveBuff[0].Size;
- if (errorString.Length + 1 <= bufferSize)
+ if ((ulong)(errorString.Length + 1) <= bufferSize)
{
- context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0'));
+ context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0'));
resultCode = ResultCode.Success;
}
@@ -152,8 +152,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
// GetAddrInfoRequest(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints) -> (i32 ret, u32 bsd_errno, u32 packed_addrinfo_size, buffer<packed_addrinfo, 6, 0> response)
public ResultCode GetAddrInfoRequest(ServiceCtx context)
{
- long responseBufferPosition = context.Request.ReceiveBuff[0].Position;
- long responseBufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong responseBufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong responseBufferSize = context.Request.ReceiveBuff[0].Size;
return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, 0, 0);
}
@@ -188,9 +188,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
// GetHostByNameRequestWithOptions(u8, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>)
public ResultCode GetHostByNameRequestWithOptions(ServiceCtx context)
{
- (long inputBufferPosition, long inputBufferSize) = context.Request.GetBufferType0x21();
- (long outputBufferPosition, long outputBufferSize) = context.Request.GetBufferType0x22();
- (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21();
+ (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21();
+ (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22();
+ (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21();
return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, optionsBufferPosition, optionsBufferSize);
}
@@ -199,9 +199,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
// GetHostByAddrRequestWithOptions(u32, u32, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>)
public ResultCode GetHostByAddrRequestWithOptions(ServiceCtx context)
{
- (long inputBufferPosition, long inputBufferSize) = context.Request.GetBufferType0x21();
- (long outputBufferPosition, long outputBufferSize) = context.Request.GetBufferType0x22();
- (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21();
+ (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21();
+ (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22();
+ (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21();
return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, optionsBufferPosition, optionsBufferSize);
}
@@ -210,17 +210,17 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
// GetAddrInfoRequestWithOptions(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints, buffer<unknown, 21, 0>) -> (i32 ret, u32 bsd_errno, u32 unknown, u32 packed_addrinfo_size, buffer<packed_addrinfo, 22, 0> response)
public ResultCode GetAddrInfoRequestWithOptions(ServiceCtx context)
{
- (long responseBufferPosition, long responseBufferSize) = context.Request.GetBufferType0x22();
- (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21();
+ (ulong responseBufferPosition, ulong responseBufferSize) = context.Request.GetBufferType0x22();
+ (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21();
return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, optionsBufferPosition, optionsBufferSize);
}
- private ResultCode GetHostByNameRequestImpl(ServiceCtx context, long inputBufferPosition, long inputBufferSize, long outputBufferPosition, long outputBufferSize, long optionsBufferPosition, long optionsBufferSize)
+ private ResultCode GetHostByNameRequestImpl(ServiceCtx context, ulong inputBufferPosition, ulong inputBufferSize, ulong outputBufferPosition, ulong outputBufferSize, ulong optionsBufferPosition, ulong optionsBufferSize)
{
byte[] rawName = new byte[inputBufferSize];
- context.Memory.Read((ulong)inputBufferPosition, rawName);
+ context.Memory.Read(inputBufferPosition, rawName);
string name = Encoding.ASCII.GetString(rawName).TrimEnd('\0');
@@ -238,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
NetDbError netDbErrorCode = NetDbError.Success;
GaiError errno = GaiError.Overflow;
- long serializedSize = 0;
+ ulong serializedSize = 0;
if (name.Length <= byte.MaxValue)
{
@@ -294,11 +294,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
return ResultCode.Success;
}
- private ResultCode GetHostByAddrRequestImpl(ServiceCtx context, long inputBufferPosition, long inputBufferSize, long outputBufferPosition, long outputBufferSize, long optionsBufferPosition, long optionsBufferSize)
+ private ResultCode GetHostByAddrRequestImpl(ServiceCtx context, ulong inputBufferPosition, ulong inputBufferSize, ulong outputBufferPosition, ulong outputBufferSize, ulong optionsBufferPosition, ulong optionsBufferSize)
{
byte[] rawIp = new byte[inputBufferSize];
- context.Memory.Read((ulong)inputBufferPosition, rawIp);
+ context.Memory.Read(inputBufferPosition, rawIp);
// TODO: Use params.
uint socketLength = context.RequestData.ReadUInt32();
@@ -315,7 +315,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
NetDbError netDbErrorCode = NetDbError.Success;
GaiError errno = GaiError.AddressFamily;
- long serializedSize = 0;
+ ulong serializedSize = 0;
if (rawIp.Length == 4)
{
@@ -349,59 +349,59 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
return ResultCode.Success;
}
- private long SerializeHostEntries(ServiceCtx context, long outputBufferPosition, long outputBufferSize, IPHostEntry hostEntry, IEnumerable<IPAddress> addresses = null)
+ private ulong SerializeHostEntries(ServiceCtx context, ulong outputBufferPosition, ulong outputBufferSize, IPHostEntry hostEntry, IEnumerable<IPAddress> addresses = null)
{
- long originalBufferPosition = outputBufferPosition;
- long bufferPosition = originalBufferPosition;
+ ulong originalBufferPosition = outputBufferPosition;
+ ulong bufferPosition = originalBufferPosition;
string hostName = hostEntry.HostName + '\0';
// h_name
- context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(hostName));
- bufferPosition += hostName.Length;
+ context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(hostName));
+ bufferPosition += (ulong)hostName.Length;
// h_aliases list size
- context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness(hostEntry.Aliases.Length));
- bufferPosition += 4;
+ context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness(hostEntry.Aliases.Length));
+ bufferPosition += sizeof(int);
// Actual aliases
foreach (string alias in hostEntry.Aliases)
{
- context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(alias + '\0'));
- bufferPosition += alias.Length + 1;
+ context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(alias + '\0'));
+ bufferPosition += (ulong)(alias.Length + 1);
}
// h_addrtype but it's a short (also only support IPv4)
- context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness((short)AddressFamily.InterNetwork));
- bufferPosition += 2;
+ context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness((short)AddressFamily.InterNetwork));
+ bufferPosition += sizeof(short);
// h_length but it's a short
- context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness((short)4));
- bufferPosition += 2;
+ context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness((short)4));
+ bufferPosition += sizeof(short);
// Ip address count, we can only support ipv4 (blame Nintendo)
- context.Memory.Write((ulong)bufferPosition, addresses != null ? BinaryPrimitives.ReverseEndianness(addresses.Count()) : 0);
- bufferPosition += 4;
+ context.Memory.Write(bufferPosition, addresses != null ? BinaryPrimitives.ReverseEndianness(addresses.Count()) : 0);
+ bufferPosition += sizeof(int);
if (addresses != null)
{
foreach (IPAddress ip in addresses)
{
- context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness(BitConverter.ToInt32(ip.GetAddressBytes(), 0)));
- bufferPosition += 4;
+ context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness(BitConverter.ToInt32(ip.GetAddressBytes(), 0)));
+ bufferPosition += sizeof(int);
}
}
return bufferPosition - originalBufferPosition;
}
- private ResultCode GetAddrInfoRequestImpl(ServiceCtx context, long responseBufferPosition, long responseBufferSize, long optionsBufferPosition, long optionsBufferSize)
+ private ResultCode GetAddrInfoRequestImpl(ServiceCtx context, ulong responseBufferPosition, ulong responseBufferSize, ulong optionsBufferPosition, ulong optionsBufferSize)
{
bool enableNsdResolve = (context.RequestData.ReadInt32() & 1) != 0;
uint cancelHandle = context.RequestData.ReadUInt32();
- string host = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[0].Position, context.Request.SendBuff[0].Size);
- string service = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[1].Position, context.Request.SendBuff[1].Size);
+ string host = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[0].Position, (long)context.Request.SendBuff[0].Size);
+ string service = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[1].Position, (long)context.Request.SendBuff[1].Size);
// NOTE: We ignore hints for now.
DeserializeAddrInfos(context.Memory, (ulong)context.Request.SendBuff[2].Position, (ulong)context.Request.SendBuff[2].Size);
@@ -500,7 +500,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
}
}
- private ulong SerializeAddrInfos(ServiceCtx context, long responseBufferPosition, long responseBufferSize, IPHostEntry hostEntry, int port)
+ private ulong SerializeAddrInfos(ServiceCtx context, ulong responseBufferPosition, ulong responseBufferSize, IPHostEntry hostEntry, int port)
{
ulong originalBufferPosition = (ulong)responseBufferPosition;
ulong bufferPosition = originalBufferPosition;
@@ -533,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
// Termination zero value.
context.Memory.Write(bufferPosition, 0);
- bufferPosition += 4;
+ bufferPosition += sizeof(int);
return bufferPosition - originalBufferPosition;
}
diff --git a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs
index 3d6be4d8..62e118d9 100644
--- a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs
@@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl
_rng.GetBytes(randomBytes);
- context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, randomBytes);
+ context.Memory.Write(context.Request.ReceiveBuff[0].Position, randomBytes);
return ResultCode.Success;
}
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs
index 9d1ffe85..24f3d066 100644
--- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs
+++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs
@@ -26,12 +26,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
// SetHostName(buffer<bytes, 5>)
public ResultCode SetHostName(ServiceCtx context)
{
- long hostNameDataPosition = context.Request.SendBuff[0].Position;
- long hostNameDataSize = context.Request.SendBuff[0].Size;
+ ulong hostNameDataPosition = context.Request.SendBuff[0].Position;
+ ulong hostNameDataSize = context.Request.SendBuff[0].Size;
byte[] hostNameData = new byte[hostNameDataSize];
- context.Memory.Read((ulong)hostNameDataPosition, hostNameData);
+ context.Memory.Read(hostNameDataPosition, hostNameData);
string hostName = Encoding.ASCII.GetString(hostNameData).Trim('\0');
@@ -75,12 +75,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
// Write(buffer<bytes, 5>) -> u32
public ResultCode Write(ServiceCtx context)
{
- long inputDataPosition = context.Request.SendBuff[0].Position;
- long inputDataSize = context.Request.SendBuff[0].Size;
+ ulong inputDataPosition = context.Request.SendBuff[0].Position;
+ ulong inputDataSize = context.Request.SendBuff[0].Size;
byte[] data = new byte[inputDataSize];
- context.Memory.Read((ulong)inputDataPosition, data);
+ context.Memory.Read(inputDataPosition, data);
// NOTE: Tell the guest everything is transferred.
uint transferredSize = (uint)inputDataSize;
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs
index ec4b5112..718af2cb 100644
--- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs
+++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs
@@ -41,8 +41,8 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
{
CertificateFormat certificateFormat = (CertificateFormat)context.RequestData.ReadUInt32();
- long certificateDataPosition = context.Request.SendBuff[0].Position;
- long certificateDataSize = context.Request.SendBuff[0].Size;
+ ulong certificateDataPosition = context.Request.SendBuff[0].Position;
+ ulong certificateDataSize = context.Request.SendBuff[0].Size;
context.ResponseData.Write(_serverCertificateId++);
@@ -55,15 +55,15 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
// ImportClientPki(buffer<bytes, 5> certificate, buffer<bytes, 5> ascii_password) -> u64 certificateId
public ResultCode ImportClientPki(ServiceCtx context)
{
- long certificateDataPosition = context.Request.SendBuff[0].Position;
- long certificateDataSize = context.Request.SendBuff[0].Size;
+ ulong certificateDataPosition = context.Request.SendBuff[0].Position;
+ ulong certificateDataSize = context.Request.SendBuff[0].Size;
- long asciiPasswordDataPosition = context.Request.SendBuff[1].Position;
- long asciiPasswordDataSize = context.Request.SendBuff[1].Size;
+ ulong asciiPasswordDataPosition = context.Request.SendBuff[1].Position;
+ ulong asciiPasswordDataSize = context.Request.SendBuff[1].Size;
byte[] asciiPasswordData = new byte[asciiPasswordDataSize];
- context.Memory.Read((ulong)asciiPasswordDataPosition, asciiPasswordData);
+ context.Memory.Read(asciiPasswordDataPosition, asciiPasswordData);
string asciiPassword = Encoding.ASCII.GetString(asciiPasswordData).Trim('\0');
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
index 011ab6ed..c3dcbee7 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
@@ -18,11 +18,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
uint code = context.RequestData.ReadUInt32();
uint flags = context.RequestData.ReadUInt32();
- ulong dataPos = (ulong)context.Request.SendBuff[0].Position;
- ulong dataSize = (ulong)context.Request.SendBuff[0].Size;
+ ulong dataPos = context.Request.SendBuff[0].Position;
+ ulong dataSize = context.Request.SendBuff[0].Size;
- long replyPos = context.Request.ReceiveBuff[0].Position;
- long replySize = context.Request.ReceiveBuff[0].Size;
+ ulong replyPos = context.Request.ReceiveBuff[0].Position;
+ ulong replySize = context.Request.ReceiveBuff[0].Size;
ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize);
@@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
if (result == ResultCode.Success)
{
- context.Memory.Write((ulong)replyPos, outputParcel);
+ context.Memory.Write(replyPos, outputParcel);
}
return result;
@@ -78,10 +78,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
uint code = context.RequestData.ReadUInt32();
uint flags = context.RequestData.ReadUInt32();
- (long dataPos, long dataSize) = context.Request.GetBufferType0x21();
- (long replyPos, long replySize) = context.Request.GetBufferType0x22();
+ (ulong dataPos, ulong dataSize) = context.Request.GetBufferType0x21();
+ (ulong replyPos, ulong replySize) = context.Request.GetBufferType0x22();
- ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan((ulong)dataPos, (int)dataSize);
+ ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize);
Span<byte> outputParcel = new Span<byte>(new byte[replySize]);
@@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
if (result == ResultCode.Success)
{
- context.Memory.Write((ulong)replyPos, outputParcel);
+ context.Memory.Write(replyPos, outputParcel);
}
return result;
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
index 556e6a3b..9d8e526f 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
@@ -1,6 +1,5 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
-using Ryujinx.Configuration;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
@@ -351,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
bool flipX = item.Transform.HasFlag(NativeWindowTransform.FlipX);
bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY);
- AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
+ AspectRatio aspectRatio = _device.Configuration.AspectRatio;
bool isStretched = aspectRatio == AspectRatio.Stretched;
ImageCrop crop = new ImageCrop(
diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs
index 52530025..534af457 100644
--- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs
@@ -246,7 +246,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
{
byte type = context.RequestData.ReadByte();
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf<ClockSnapshot>());
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ClockSnapshot>());
ResultCode result = _timeManager.StandardUserSystemClock.GetClockContext(context.Thread, out SystemClockContext userContext);
@@ -274,7 +274,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
{
byte type = context.RequestData.ReadByte();
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf<ClockSnapshot>());
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ClockSnapshot>());
context.RequestData.BaseStream.Position += 7;
@@ -354,6 +354,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
clockSnapshot.IsAutomaticCorrectionEnabled = _timeManager.StandardUserSystemClock.IsAutomaticCorrectionEnabled();
clockSnapshot.UserContext = userContext;
clockSnapshot.NetworkContext = networkContext;
+ clockSnapshot.SteadyClockTimePoint = currentTimePoint;
ResultCode result = _timeManager.TimeZone.Manager.GetDeviceLocationName(out string deviceLocationName);
@@ -404,11 +405,11 @@ namespace Ryujinx.HLE.HOS.Services.Time
private ClockSnapshot ReadClockSnapshotFromBuffer(ServiceCtx context, IpcPtrBuffDesc ipcDesc)
{
- Debug.Assert(ipcDesc.Size == Marshal.SizeOf<ClockSnapshot>());
+ Debug.Assert(ipcDesc.Size == (ulong)Marshal.SizeOf<ClockSnapshot>());
byte[] temp = new byte[ipcDesc.Size];
- context.Memory.Read((ulong)ipcDesc.Position, temp);
+ context.Memory.Read(ipcDesc.Position, temp);
using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(temp)))
{
diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
index 9b6c59f6..8b4cd27c 100644
--- a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
@@ -121,11 +121,11 @@ namespace Ryujinx.HLE.HOS.Services.Time
uint totalLocationNameCount = context.RequestData.ReadUInt32();
UInt128 timeZoneRuleVersion = context.RequestData.ReadStruct<UInt128>();
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
byte[] temp = new byte[bufferSize];
- context.Memory.Read((ulong)bufferPosition, temp);
+ context.Memory.Read(bufferPosition, temp);
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
{
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs
index 5f35412d..da806ab6 100644
--- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs
@@ -51,9 +51,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
// LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
public ResultCode LoadLocationNameList(ServiceCtx context)
{
- uint index = context.RequestData.ReadUInt32();
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
+ uint index = context.RequestData.ReadUInt32();
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferSize = context.Request.ReceiveBuff[0].Size;
ResultCode errorCode = _timeZoneContentManager.LoadLocationNameList(index, out string[] locationNameArray, (uint)bufferSize / 0x24);
@@ -70,8 +70,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
return ResultCode.LocationNameTooLong;
}
- context.Memory.Write((ulong)bufferPosition + offset, Encoding.ASCII.GetBytes(locationName));
- MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + locationName.Length, padding);
+ context.Memory.Write(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName));
+ MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + (ulong)locationName.Length, padding);
offset += 0x24;
}
@@ -86,8 +86,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
// LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
public ResultCode LoadTimeZoneRule(ServiceCtx context)
{
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferSize = context.Request.ReceiveBuff[0].Size;
if (bufferSize != 0x4000)
{
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs
index 659e1331..d0ed9379 100644
--- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs
@@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
return ResultCode.PermissionDenied;
}
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
@@ -131,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
byte[] temp = new byte[bufferSize];
- context.Memory.Read((ulong)bufferPosition, temp);
+ context.Memory.Read(bufferPosition, temp);
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
{
@@ -145,10 +145,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
// ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16>
public ResultCode ParseTimeZoneBinary(ServiceCtx context)
{
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+ (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
- long timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position;
- long timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size;
+ ulong timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size;
if (timeZoneRuleBufferSize != 0x4000)
{
@@ -162,7 +162,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
byte[] temp = new byte[bufferSize];
- context.Memory.Read((ulong)bufferPosition, temp);
+ context.Memory.Read(bufferPosition, temp);
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
{
@@ -188,9 +188,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
// ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
public ResultCode ToCalendarTime(ServiceCtx context)
{
- long posixTime = context.RequestData.ReadInt64();
- long bufferPosition = context.Request.SendBuff[0].Position;
- long bufferSize = context.Request.SendBuff[0].Size;
+ long posixTime = context.RequestData.ReadInt64();
+ ulong bufferPosition = context.Request.SendBuff[0].Position;
+ ulong bufferSize = context.Request.SendBuff[0].Size;
if (bufferSize != 0x4000)
{
@@ -220,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
ResultCode resultCode = _timeZoneManager.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar);
- if (resultCode == 0)
+ if (resultCode == ResultCode.Success)
{
context.ResponseData.WriteStruct(calendar);
}
@@ -232,8 +232,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
// ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
public ResultCode ToPosixTime(ServiceCtx context)
{
- long inBufferPosition = context.Request.SendBuff[0].Position;
- long inBufferSize = context.Request.SendBuff[0].Size;
+ ulong inBufferPosition = context.Request.SendBuff[0].Position;
+ ulong inBufferSize = context.Request.SendBuff[0].Size;
CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
@@ -249,12 +249,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
- if (resultCode == 0)
+ if (resultCode == ResultCode.Success)
{
- long outBufferPosition = context.Request.RecvListBuff[0].Position;
- long outBufferSize = context.Request.RecvListBuff[0].Size;
+ ulong outBufferPosition = context.Request.RecvListBuff[0].Position;
+ ulong outBufferSize = context.Request.RecvListBuff[0].Size;
- context.Memory.Write((ulong)outBufferPosition, posixTime);
+ context.Memory.Write(outBufferPosition, posixTime);
context.ResponseData.Write(1);
}
@@ -269,12 +269,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
ResultCode resultCode = _timeZoneManager.ToPosixTimeWithMyRules(calendarTime, out long posixTime);
- if (resultCode == 0)
+ if (resultCode == ResultCode.Success)
{
- long outBufferPosition = context.Request.RecvListBuff[0].Position;
- long outBufferSize = context.Request.RecvListBuff[0].Size;
+ ulong outBufferPosition = context.Request.RecvListBuff[0].Position;
+ ulong outBufferSize = context.Request.RecvListBuff[0].Size;
- context.Memory.Write((ulong)outBufferPosition, posixTime);
+ context.Memory.Write(outBufferPosition, posixTime);
// There could be only one result on one calendar as leap seconds aren't supported.
context.ResponseData.Write(1);
diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs
index b66ffc3f..963ea9fd 100644
--- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs
@@ -5,7 +5,6 @@ using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
-using Ryujinx.Configuration;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
@@ -47,10 +46,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
InitializeLocationNameCache();
}
- public string SanityCheckDeviceLocationName()
+ public string SanityCheckDeviceLocationName(string locationName)
{
- string locationName = ConfigurationState.Instance.System.TimeZone;
-
if (IsLocationNameValid(locationName))
{
return locationName;
@@ -58,8 +55,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
Logger.Warning?.Print(LogClass.ServiceTime, $"Invalid device TimeZone {locationName}, switching back to UTC");
- ConfigurationState.Instance.System.TimeZone.Value = "UTC";
-
return "UTC";
}
@@ -69,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
SteadyClockTimePoint timeZoneUpdatedTimePoint = timeManager.StandardSteadyClock.GetCurrentTimePoint(null);
- string deviceLocationName = SanityCheckDeviceLocationName();
+ string deviceLocationName = SanityCheckDeviceLocationName(device.Configuration.TimeZone);
ResultCode result = GetTimeZoneBinary(deviceLocationName, out Stream timeZoneBinaryStream, out LocalStorage ncaFile);
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
index 37e603dc..5f161bee 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
@@ -61,16 +61,16 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
// ListDisplays() -> (u64, buffer<nn::vi::DisplayInfo, 6>)
public ResultCode ListDisplays(ServiceCtx context)
{
- long recBuffPtr = context.Request.ReceiveBuff[0].Position;
+ ulong recBuffPtr = context.Request.ReceiveBuff[0].Position;
MemoryHelper.FillWithZeros(context.Memory, recBuffPtr, 0x60);
// Add only the default display to buffer
- context.Memory.Write((ulong)recBuffPtr, Encoding.ASCII.GetBytes("Default"));
- context.Memory.Write((ulong)recBuffPtr + 0x40, 0x1L);
- context.Memory.Write((ulong)recBuffPtr + 0x48, 0x1L);
- context.Memory.Write((ulong)recBuffPtr + 0x50, 1280L);
- context.Memory.Write((ulong)recBuffPtr + 0x58, 720L);
+ context.Memory.Write(recBuffPtr, Encoding.ASCII.GetBytes("Default"));
+ context.Memory.Write(recBuffPtr + 0x40, 0x1UL);
+ context.Memory.Write(recBuffPtr + 0x48, 0x1UL);
+ context.Memory.Write(recBuffPtr + 0x50, 1280UL);
+ context.Memory.Write(recBuffPtr + 0x58, 720UL);
context.ResponseData.Write(1L);
@@ -120,9 +120,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
// TODO: support multi display.
byte[] displayName = context.RequestData.ReadBytes(0x40);
- long layerId = context.RequestData.ReadInt64();
- long userId = context.RequestData.ReadInt64();
- long parcelPtr = context.Request.ReceiveBuff[0].Position;
+ long layerId = context.RequestData.ReadInt64();
+ long userId = context.RequestData.ReadInt64();
+ ulong parcelPtr = context.Request.ReceiveBuff[0].Position;
IBinder producer = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId);
@@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
ReadOnlySpan<byte> parcelData = parcel.Finish();
- context.Memory.Write((ulong)parcelPtr, parcelData);
+ context.Memory.Write(parcelPtr, parcelData);
context.ResponseData.Write((long)parcelData.Length);
@@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
long layerFlags = context.RequestData.ReadInt64();
long displayId = context.RequestData.ReadInt64();
- long parcelPtr = context.Request.ReceiveBuff[0].Position;
+ ulong parcelPtr = context.Request.ReceiveBuff[0].Position;
// TODO: support multi display.
Display disp = _displays.GetData<Display>((int)displayId);
@@ -174,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
ReadOnlySpan<byte> parcelData = parcel.Finish();
- context.Memory.Write((ulong)parcelPtr, parcelData);
+ context.Memory.Write(parcelPtr, parcelData);
context.ResponseData.Write(layerId);
context.ResponseData.Write((long)parcelData.Length);
@@ -250,11 +250,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
// The size of the layer buffer should be an aligned multiple of width * height
// because it was created using GetIndirectLayerImageRequiredMemoryInfo as a guide.
- long layerBuffPosition = context.Request.ReceiveBuff[0].Position;
- long layerBuffSize = context.Request.ReceiveBuff[0].Size;
+ ulong layerBuffPosition = context.Request.ReceiveBuff[0].Position;
+ ulong layerBuffSize = context.Request.ReceiveBuff[0].Size;
// Fill the layer with zeros.
- context.Memory.Fill((ulong)layerBuffPosition, (ulong)layerBuffSize, 0x00);
+ context.Memory.Fill(layerBuffPosition, layerBuffSize, 0x00);
Logger.Stub?.PrintStub(LogClass.ServiceVi);
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index b88bdbfe..ee359dda 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -1,22 +1,14 @@
-using LibHac.FsSystem;
using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration;
-using Ryujinx.Common;
-using Ryujinx.Configuration;
-using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Host1x;
using Ryujinx.Graphics.Nvdec;
using Ryujinx.Graphics.Vic;
using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
-using Ryujinx.HLE.HOS.Services;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
-using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Memory;
using System;
@@ -24,69 +16,60 @@ namespace Ryujinx.HLE
{
public class Switch : IDisposable
{
- private MemoryConfiguration _memoryConfiguration;
+ public HLEConfiguration Configuration { get; }
- public IHardwareDeviceDriver AudioDeviceDriver { get; private set; }
+ public IHardwareDeviceDriver AudioDeviceDriver { get; }
- internal MemoryBlock Memory { get; private set; }
+ internal MemoryBlock Memory { get; }
- public GpuContext Gpu { get; private set; }
+ public GpuContext Gpu { get; }
- internal NvMemoryAllocator MemoryAllocator { get; private set; }
+ internal NvMemoryAllocator MemoryAllocator { get; }
internal Host1xDevice Host1x { get; }
- public VirtualFileSystem FileSystem { get; private set; }
+ public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem;
- public Horizon System { get; private set; }
+ public Horizon System { get; }
public ApplicationLoader Application { get; }
- public PerformanceStatistics Statistics { get; private set; }
+ public PerformanceStatistics Statistics { get; }
- public UserChannelPersistence UserChannelPersistence { get; }
+ public Hid Hid { get; }
- public Hid Hid { get; private set; }
+ public TamperMachine TamperMachine { get; }
- public TamperMachine TamperMachine { get; private set; }
-
- public IHostUiHandler UiHandler { get; set; }
+ public IHostUiHandler UiHandler { get; }
public bool EnableDeviceVsync { get; set; } = true;
- public Switch(
- VirtualFileSystem fileSystem,
- ContentManager contentManager,
- AccountManager accountManager,
- UserChannelPersistence userChannelPersistence,
- IRenderer renderer,
- IHardwareDeviceDriver audioDeviceDriver,
- MemoryConfiguration memoryConfiguration)
+ public Switch(HLEConfiguration configuration)
{
- if (renderer == null)
+ if (configuration.GpuRenderer == null)
{
- throw new ArgumentNullException(nameof(renderer));
+ throw new ArgumentNullException(nameof(configuration.GpuRenderer));
}
- if (audioDeviceDriver == null)
+ if (configuration.AudioDeviceDriver == null)
{
- throw new ArgumentNullException(nameof(audioDeviceDriver));
+ throw new ArgumentNullException(nameof(configuration.AudioDeviceDriver));
}
- if (userChannelPersistence == null)
+ if (configuration.UserChannelPersistence== null)
{
- throw new ArgumentNullException(nameof(userChannelPersistence));
+ throw new ArgumentNullException(nameof(configuration.UserChannelPersistence));
}
- UserChannelPersistence = userChannelPersistence;
+ Configuration = configuration;
- _memoryConfiguration = memoryConfiguration;
+ UiHandler = configuration.HostUiHandler;
- AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(audioDeviceDriver);
+ AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(configuration.AudioDeviceDriver);
- Memory = new MemoryBlock(memoryConfiguration.ToDramSize());
+ Memory = new MemoryBlock(configuration.MemoryConfiguration.ToDramSize());
- Gpu = new GpuContext(renderer);
+ Gpu = new GpuContext(configuration.GpuRenderer);
MemoryAllocator = new NvMemoryAllocator();
@@ -111,9 +94,7 @@ namespace Ryujinx.HLE
}
};
- FileSystem = fileSystem;
-
- System = new Horizon(this, contentManager, accountManager, memoryConfiguration);
+ System = new Horizon(this);
System.InitializeServices();
Statistics = new PerformanceStatistics();
@@ -121,45 +102,30 @@ namespace Ryujinx.HLE
Hid = new Hid(this, System.HidBaseAddress);
Hid.InitDevices();
- Application = new ApplicationLoader(this, fileSystem, contentManager);
+ Application = new ApplicationLoader(this);
TamperMachine = new TamperMachine();
+
+ Initialize();
}
- public void Initialize()
+ private void Initialize()
{
- System.State.SetLanguage((SystemLanguage)ConfigurationState.Instance.System.Language.Value);
+ System.State.SetLanguage(Configuration.SystemLanguage);
- System.State.SetRegion((RegionCode)ConfigurationState.Instance.System.Region.Value);
+ System.State.SetRegion(Configuration.Region);
- EnableDeviceVsync = ConfigurationState.Instance.Graphics.EnableVsync;
+ EnableDeviceVsync = Configuration.EnableVsync;
- System.State.DockedMode = ConfigurationState.Instance.System.EnableDockedMode;
+ System.State.DockedMode = Configuration.EnableDockedMode;
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
- System.EnablePtc = ConfigurationState.Instance.System.EnablePtc;
+ System.EnablePtc = Configuration.EnablePtc;
- System.FsIntegrityCheckLevel = GetIntegrityCheckLevel();
+ System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
- System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
-
- ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices;
- ConfigurationState.Instance.System.IgnoreMissingServices.Event += (object _, ReactiveEventArgs<bool> args) =>
- {
- ServiceConfiguration.IgnoreMissingServices = args.NewValue;
- };
-
- // Configure controllers
- Hid.RefreshInputConfig(ConfigurationState.Instance.Hid.InputConfig.Value);
- ConfigurationState.Instance.Hid.InputConfig.Event += Hid.RefreshInputConfigEvent;
- }
-
- public static IntegrityCheckLevel GetIntegrityCheckLevel()
- {
- return ConfigurationState.Instance.System.EnableFsIntegrityChecks
- ? IntegrityCheckLevel.ErrorOnInvalid
- : IntegrityCheckLevel.None;
+ System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
}
public void LoadCart(string exeFsDir, string romFsFile = null)
@@ -223,8 +189,6 @@ namespace Ryujinx.HLE
{
if (disposing)
{
- ConfigurationState.Instance.Hid.InputConfig.Event -= Hid.RefreshInputConfigEvent;
-
System.Dispose();
Host1x.Dispose();
AudioDeviceDriver.Dispose();
diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs
index 4399da21..0259a4fd 100644
--- a/Ryujinx.HLE/Utilities/StringUtils.cs
+++ b/Ryujinx.HLE/Utilities/StringUtils.cs
@@ -53,14 +53,14 @@ namespace Ryujinx.HLE.Utilities
public static string ReadUtf8String(ServiceCtx context, int index = 0)
{
- long position = context.Request.PtrBuff[index].Position;
- long size = context.Request.PtrBuff[index].Size;
+ ulong position = context.Request.PtrBuff[index].Position;
+ ulong size = context.Request.PtrBuff[index].Size;
using (MemoryStream ms = new MemoryStream())
{
while (size-- > 0)
{
- byte value = context.Memory.Read<byte>((ulong)position++);
+ byte value = context.Memory.Read<byte>(position++);
if (value == 0)
{
@@ -86,8 +86,8 @@ namespace Ryujinx.HLE.Utilities
public static string ReadUtf8StringSend(ServiceCtx context, int index = 0)
{
- long position = context.Request.SendBuff[index].Position;
- long size = context.Request.SendBuff[index].Size;
+ ulong position = context.Request.SendBuff[index].Position;
+ ulong size = context.Request.SendBuff[index].Size;
using (MemoryStream ms = new MemoryStream())
{
diff --git a/Ryujinx.HLE/Utilities/StructReader.cs b/Ryujinx.HLE/Utilities/StructReader.cs
index 3bde1c77..81e758a8 100644
--- a/Ryujinx.HLE/Utilities/StructReader.cs
+++ b/Ryujinx.HLE/Utilities/StructReader.cs
@@ -1,5 +1,6 @@
using Ryujinx.Cpu;
using Ryujinx.Memory;
+using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.Utilities
@@ -8,39 +9,30 @@ namespace Ryujinx.HLE.Utilities
{
private IVirtualMemoryManager _memory;
- public long Position { get; private set; }
+ public ulong Position { get; private set; }
- public StructReader(IVirtualMemoryManager memory, long position)
+ public StructReader(IVirtualMemoryManager memory, ulong position)
{
_memory = memory;
Position = position;
}
- public T Read<T>() where T : struct
+ public T Read<T>() where T : unmanaged
{
T value = MemoryHelper.Read<T>(_memory, Position);
- Position += Marshal.SizeOf<T>();
+ Position += (uint)Marshal.SizeOf<T>();
return value;
}
- public T[] Read<T>(int size) where T : struct
+ public ReadOnlySpan<T> Read<T>(int size) where T : unmanaged
{
- int structSize = Marshal.SizeOf<T>();
+ ReadOnlySpan<byte> data = _memory.GetSpan(Position, size);
- int count = size / structSize;
+ Position += (uint)size;
- T[] output = new T[count];
-
- for (int index = 0; index < count; index++)
- {
- output[index] = MemoryHelper.Read<T>(_memory, Position);
-
- Position += structSize;
- }
-
- return output;
+ return MemoryMarshal.Cast<byte, T>(data);
}
}
}
diff --git a/Ryujinx.HLE/Utilities/StructWriter.cs b/Ryujinx.HLE/Utilities/StructWriter.cs
index 6b1e0838..39644db5 100644
--- a/Ryujinx.HLE/Utilities/StructWriter.cs
+++ b/Ryujinx.HLE/Utilities/StructWriter.cs
@@ -8,9 +8,9 @@ namespace Ryujinx.HLE.Utilities
{
private IVirtualMemoryManager _memory;
- public long Position { get; private set; }
+ public ulong Position { get; private set; }
- public StructWriter(IVirtualMemoryManager memory, long position)
+ public StructWriter(IVirtualMemoryManager memory, ulong position)
{
_memory = memory;
Position = position;
@@ -20,10 +20,10 @@ namespace Ryujinx.HLE.Utilities
{
MemoryHelper.Write(_memory, Position, value);
- Position += Marshal.SizeOf<T>();
+ Position += (ulong)Marshal.SizeOf<T>();
}
- public void SkipBytes(long count)
+ public void SkipBytes(ulong count)
{
Position += count;
}
diff --git a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj
index cee18996..2d61dfb8 100644
--- a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj
+++ b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj
@@ -6,11 +6,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="ppy.SDL2-CS" Version="1.0.225-alpha" />
- </ItemGroup>
-
- <ItemGroup>
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
+ <ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
</ItemGroup>
</Project>
diff --git a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs
index 62098383..927d7fe6 100644
--- a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs
+++ b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs
@@ -1,4 +1,5 @@
-using System;
+using Ryujinx.SDL2.Common;
+using System;
using System.Collections.Generic;
using static SDL2.SDL;
diff --git a/Ryujinx.Input/HLE/NpadController.cs b/Ryujinx.Input/HLE/NpadController.cs
index d3553d64..e1a8e2d7 100644
--- a/Ryujinx.Input/HLE/NpadController.cs
+++ b/Ryujinx.Input/HLE/NpadController.cs
@@ -456,14 +456,14 @@ namespace Ryujinx.Input.HLE
KeyboardInput hidKeyboard = new KeyboardInput
{
Modifier = 0,
- Keys = new int[0x8]
+ Keys = new ulong[0x4]
};
foreach (HLEKeyboardMappingEntry entry in KeyMapping)
{
- int value = keyboardState.IsPressed(entry.TargetKey) ? 1 : 0;
+ ulong value = keyboardState.IsPressed(entry.TargetKey) ? 1UL : 0UL;
- hidKeyboard.Keys[entry.Target / 0x20] |= (value << (entry.Target % 0x20));
+ hidKeyboard.Keys[entry.Target / 0x40] |= (value << (entry.Target % 0x40));
}
foreach (HLEKeyboardMappingEntry entry in KeyModifierMapping)
diff --git a/Ryujinx.Input/HLE/NpadManager.cs b/Ryujinx.Input/HLE/NpadManager.cs
index fdb87f9b..abb820b0 100644
--- a/Ryujinx.Input/HLE/NpadManager.cs
+++ b/Ryujinx.Input/HLE/NpadManager.cs
@@ -1,8 +1,6 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Keyboard;
-using Ryujinx.Configuration;
-using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Collections.Generic;
@@ -10,6 +8,7 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client;
+using Switch = Ryujinx.HLE.Switch;
namespace Ryujinx.Input.HLE
{
@@ -31,30 +30,41 @@ namespace Ryujinx.Input.HLE
private bool _isDisposed;
private List<InputConfig> _inputConfig;
+ private bool _enableKeyboard;
+ private Switch _device;
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver)
{
_controllers = new NpadController[MaxControllers];
- _cemuHookClient = new CemuHookClient();
+ _cemuHookClient = new CemuHookClient(this);
_keyboardDriver = keyboardDriver;
_gamepadDriver = gamepadDriver;
- _inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value;
+ _inputConfig = new List<InputConfig>();
+ _enableKeyboard = false;
_gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
}
+ private void RefreshInputConfigForHLE()
+ {
+ lock (_lock)
+ {
+ _device.Hid.RefreshInputConfig(_inputConfig);
+ }
+ }
+
private void HandleOnGamepadDisconnected(string obj)
{
// Force input reload
- ReloadConfiguration(ConfigurationState.Instance.Hid.InputConfig.Value);
+ ReloadConfiguration(_inputConfig, _enableKeyboard);
}
private void HandleOnGamepadConnected(string id)
{
// Force input reload
- ReloadConfiguration(ConfigurationState.Instance.Hid.InputConfig.Value);
+ ReloadConfiguration(_inputConfig, _enableKeyboard);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -83,7 +93,7 @@ namespace Ryujinx.Input.HLE
}
}
- public void ReloadConfiguration(List<InputConfig> inputConfig)
+ public void ReloadConfiguration(List<InputConfig> inputConfig, bool enableKeyboard)
{
lock (_lock)
{
@@ -110,10 +120,9 @@ namespace Ryujinx.Input.HLE
}
_inputConfig = inputConfig;
+ _enableKeyboard = enableKeyboard;
- // Enforce an update of the property that will be updated by HLE.
- // TODO: Move that in the input manager maybe?
- ConfigurationState.Instance.Hid.InputConfig.Value = inputConfig;
+ _device.Hid.RefreshInputConfig(inputConfig);
}
}
@@ -133,13 +142,23 @@ namespace Ryujinx.Input.HLE
}
}
- public void Update(Hid hleHid, TamperMachine tamperMachine)
+ public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard)
+ {
+ _device = device;
+ _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE;
+
+ ReloadConfiguration(inputConfig, enableKeyboard);
+ }
+
+ public void Update()
{
lock (_lock)
{
List<GamepadInput> hleInputStates = new List<GamepadInput>();
List<SixAxisInput> hleMotionStates = new List<SixAxisInput>(NpadDevices.MaxControllers);
+ KeyboardInput? hleKeyboardInput = null;
+
foreach (InputConfig inputConfig in _inputConfig)
{
GamepadInput inputState = default;
@@ -157,9 +176,14 @@ namespace Ryujinx.Input.HLE
inputState = controller.GetHLEInputState();
- inputState.Buttons |= hleHid.UpdateStickButtons(inputState.LStick, inputState.RStick);
+ inputState.Buttons |= _device.Hid.UpdateStickButtons(inputState.LStick, inputState.RStick);
motionState = controller.GetHLEMotionState();
+
+ if (_enableKeyboard)
+ {
+ hleKeyboardInput = controller.GetHLEKeyboardInput();
+ }
}
else
{
@@ -172,21 +196,25 @@ namespace Ryujinx.Input.HLE
hleInputStates.Add(inputState);
hleMotionStates.Add(motionState);
+ }
- if (ConfigurationState.Instance.Hid.EnableKeyboard)
- {
- KeyboardInput? hleKeyboardInput = controller.GetHLEKeyboardInput();
+ _device.Hid.Npads.Update(hleInputStates);
+ _device.Hid.Npads.UpdateSixAxis(hleMotionStates);
- if (hleKeyboardInput.HasValue)
- {
- hleHid.Keyboard.Update(hleKeyboardInput.Value);
- }
- }
+ if (hleKeyboardInput.HasValue)
+ {
+ _device.Hid.Keyboard.Update(hleKeyboardInput.Value);
}
- hleHid.Npads.Update(hleInputStates);
- hleHid.Npads.UpdateSixAxis(hleMotionStates);
- tamperMachine.UpdateInput(hleInputStates);
+ _device.TamperMachine.UpdateInput(hleInputStates);
+ }
+ }
+
+ internal InputConfig GetPlayerInputConfigByIndex(int index)
+ {
+ lock (_lock)
+ {
+ return _inputConfig.Find(x => x.PlayerIndex == (Ryujinx.Common.Configuration.Hid.PlayerIndex)index);
}
}
diff --git a/Ryujinx.Input/Motion/CemuHook/Client.cs b/Ryujinx.Input/Motion/CemuHook/Client.cs
index 395bd0b3..214e23f0 100644
--- a/Ryujinx.Input/Motion/CemuHook/Client.cs
+++ b/Ryujinx.Input/Motion/CemuHook/Client.cs
@@ -4,7 +4,7 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging;
-using Ryujinx.Configuration;
+using Ryujinx.Input.HLE;
using Ryujinx.Input.Motion.CemuHook.Protocol;
using System;
using System.Collections.Generic;
@@ -29,12 +29,14 @@ namespace Ryujinx.Input.Motion.CemuHook
private readonly bool[] _clientErrorStatus = new bool[Enum.GetValues(typeof(PlayerIndex)).Length];
private readonly long[] _clientRetryTimer = new long[Enum.GetValues(typeof(PlayerIndex)).Length];
+ private NpadManager _npadManager;
- public Client()
+ public Client(NpadManager npadManager)
{
- _hosts = new Dictionary<int, IPEndPoint>();
- _motionData = new Dictionary<int, Dictionary<int, MotionInput>>();
- _clients = new Dictionary<int, UdpClient>();
+ _npadManager = npadManager;
+ _hosts = new Dictionary<int, IPEndPoint>();
+ _motionData = new Dictionary<int, Dictionary<int, MotionInput>>();
+ _clients = new Dictionary<int, UdpClient>();
CloseClients();
}
@@ -323,7 +325,7 @@ namespace Ryujinx.Input.Motion.CemuHook
ulong timestamp = inputData.MotionTimestamp;
- InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(x => x.PlayerIndex == (PlayerIndex)clientId);
+ InputConfig config = _npadManager.GetPlayerInputConfigByIndex(clientId);
lock (_motionData)
{
diff --git a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj
new file mode 100644
index 00000000..a35f8743
--- /dev/null
+++ b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="ppy.SDL2-CS" Version="1.0.225-alpha" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Ryujinx.Input.SDL2/SDL2Driver.cs b/Ryujinx.SDL2.Common/SDL2Driver.cs
index f77bb1d5..edd634ee 100644
--- a/Ryujinx.Input.SDL2/SDL2Driver.cs
+++ b/Ryujinx.SDL2.Common/SDL2Driver.cs
@@ -4,9 +4,9 @@ using System.IO;
using System.Threading;
using static SDL2.SDL;
-namespace Ryujinx.Input.SDL2
+namespace Ryujinx.SDL2.Common
{
- class SDL2Driver : IDisposable
+ public class SDL2Driver : IDisposable
{
private static SDL2Driver _instance;
@@ -25,7 +25,7 @@ namespace Ryujinx.Input.SDL2
}
}
- private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK;
+ private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO;
private bool _isRunning;
private uint _refereceCount;
diff --git a/Ryujinx.sln b/Ryujinx.sln
index bd00f000..f4eec573 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -59,9 +59,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.Open
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input", "Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input.SDL2", "Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.SDL2.Common", "Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Audio.Backends.SDL2", "Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -177,6 +181,14 @@ Global
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 29460614..cf21656a 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,5 +1,6 @@
{
"version": 24,
+ "enable_file_log": true,
"res_scale": 1,
"res_scale_custom": 1,
"max_anisotropy": -1,
@@ -14,7 +15,6 @@
"logging_enable_fs_access_log": false,
"logging_filtered_classes": [],
"logging_graphics_debug_level": "None",
- "enable_file_log": true,
"system_language": "AmericanEnglish",
"system_region": "USA",
"system_time_zone": "UTC",
diff --git a/Ryujinx.Common/Configuration/AudioBackend.cs b/Ryujinx/Configuration/AudioBackend.cs
index 28233354..e42df039 100644
--- a/Ryujinx.Common/Configuration/AudioBackend.cs
+++ b/Ryujinx/Configuration/AudioBackend.cs
@@ -4,6 +4,7 @@
{
Dummy,
OpenAl,
- SoundIo
+ SoundIo,
+ SDL2
}
} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx/Configuration/ConfigurationFileFormat.cs
index 1d47051a..be9c6864 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx/Configuration/ConfigurationFileFormat.cs
@@ -14,11 +14,16 @@ namespace Ryujinx.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
- public const int CurrentVersion = 24;
+ public const int CurrentVersion = 25;
public int Version { get; set; }
/// <summary>
+ /// Enables or disables logging to a file on disk
+ /// </summary>
+ public bool EnableFileLog { get; set; }
+
+ /// <summary>
/// Resolution Scale. An integer scale applied to applicable render targets. Values 1-4, or -1 to use a custom floating point scale instead.
/// </summary>
public int ResScale { get; set; }
@@ -88,10 +93,6 @@ namespace Ryujinx.Configuration
/// </summary>
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
- /// <summary>
- /// Enables or disables logging to a file on disk
- /// </summary>
- public bool EnableFileLog { get; set; }
/// <summary>
/// Change System Language
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx/Configuration/ConfigurationState.cs
index 7063110f..9ea5c282 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx/Configuration/ConfigurationState.cs
@@ -408,6 +408,7 @@ namespace Ryujinx.Configuration
ConfigurationFileFormat configurationFile = new ConfigurationFileFormat
{
Version = ConfigurationFileFormat.CurrentVersion,
+ EnableFileLog = Logger.EnableFileLog,
ResScale = Graphics.ResScale,
ResScaleCustom = Graphics.ResScaleCustom,
MaxAnisotropy = Graphics.MaxAnisotropy,
@@ -422,7 +423,6 @@ namespace Ryujinx.Configuration
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
LoggingFilteredClasses = Logger.FilteredClasses,
LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
- EnableFileLog = Logger.EnableFileLog,
SystemLanguage = System.Language,
SystemRegion = System.Region,
SystemTimeZone = System.TimeZone,
@@ -474,6 +474,7 @@ namespace Ryujinx.Configuration
public void LoadDefault()
{
+ Logger.EnableFileLog.Value = true;
Graphics.ResScale.Value = 1;
Graphics.ResScaleCustom.Value = 1.0f;
Graphics.MaxAnisotropy.Value = -1.0f;
@@ -488,7 +489,6 @@ namespace Ryujinx.Configuration
Logger.EnableFsAccessLog.Value = false;
Logger.FilteredClasses.Value = Array.Empty<LogClass>();
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
- Logger.EnableFileLog.Value = true;
System.Language.Value = Language.AmericanEnglish;
System.Region.Value = Region.USA;
System.TimeZone.Value = "UTC";
@@ -803,6 +803,14 @@ namespace Ryujinx.Configuration
configurationFileUpdated = true;
}
+ if (configurationFileFormat.Version < 25)
+ {
+ Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 25.");
+
+ configurationFileUpdated = true;
+ }
+
+ Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy;
@@ -817,7 +825,6 @@ namespace Ryujinx.Configuration
Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog;
Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses;
Logger.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel;
- Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
System.Language.Value = configurationFileFormat.SystemLanguage;
System.Region.Value = configurationFileFormat.SystemRegion;
System.TimeZone.Value = configurationFileFormat.SystemTimeZone;
diff --git a/Ryujinx.Common/Configuration/LoggerModule.cs b/Ryujinx/Configuration/LoggerModule.cs
index 20c0fb46..44631ea0 100644
--- a/Ryujinx.Common/Configuration/LoggerModule.cs
+++ b/Ryujinx/Configuration/LoggerModule.cs
@@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
-using System.IO;
namespace Ryujinx.Configuration
{
diff --git a/Ryujinx.Common/Configuration/System/Language.cs b/Ryujinx/Configuration/System/Language.cs
index d3af296b..d3af296b 100644
--- a/Ryujinx.Common/Configuration/System/Language.cs
+++ b/Ryujinx/Configuration/System/Language.cs
diff --git a/Ryujinx.Common/Configuration/System/Region.cs b/Ryujinx/Configuration/System/Region.cs
index 54b1c36f..54b1c36f 100644
--- a/Ryujinx.Common/Configuration/System/Region.cs
+++ b/Ryujinx/Configuration/System/Region.cs
diff --git a/Ryujinx.Common/Configuration/Ui/ColumnSort.cs b/Ryujinx/Configuration/Ui/ColumnSort.cs
index fd8b5da1..fd8b5da1 100644
--- a/Ryujinx.Common/Configuration/Ui/ColumnSort.cs
+++ b/Ryujinx/Configuration/Ui/ColumnSort.cs
diff --git a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs b/Ryujinx/Configuration/Ui/GuiColumns.cs
index de4f7369..de4f7369 100644
--- a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
+++ b/Ryujinx/Configuration/Ui/GuiColumns.cs
diff --git a/Ryujinx/Input/GTK3/GTK3MappingHelper.cs b/Ryujinx/Input/GTK3/GTK3MappingHelper.cs
index 5e68aa40..c6be528e 100644
--- a/Ryujinx/Input/GTK3/GTK3MappingHelper.cs
+++ b/Ryujinx/Input/GTK3/GTK3MappingHelper.cs
@@ -48,7 +48,7 @@ namespace Ryujinx.Input.GTK3
GtkKey.F27,
GtkKey.F28,
GtkKey.F29,
- GtkKey.F29,
+ GtkKey.F30,
GtkKey.F31,
GtkKey.F32,
GtkKey.F33,
@@ -128,6 +128,7 @@ namespace Ryujinx.Input.GTK3
GtkKey.Key_8,
GtkKey.Key_9,
GtkKey.grave,
+ GtkKey.grave,
GtkKey.minus,
GtkKey.plus,
GtkKey.bracketleft,
@@ -138,7 +139,6 @@ namespace Ryujinx.Input.GTK3
GtkKey.period,
GtkKey.slash,
GtkKey.backslash,
- GtkKey.backslash,
// NOTE: invalid
GtkKey.blank,
diff --git a/Ryujinx/Modules/DiscordIntegrationModule.cs b/Ryujinx/Modules/DiscordIntegrationModule.cs
index 3a91cf53..d890a4c8 100644
--- a/Ryujinx/Modules/DiscordIntegrationModule.cs
+++ b/Ryujinx/Modules/DiscordIntegrationModule.cs
@@ -2,41 +2,48 @@
using Ryujinx.Common;
using Ryujinx.Configuration;
using System;
-using System.Linq;
namespace Ryujinx.Modules
{
static class DiscordIntegrationModule
{
- private static DiscordRpcClient _discordClient;
-
- private const string LargeDescription = "Ryujinx is a Nintendo Switch emulator.";
+ private const string Description = "A simple, experimental Nintendo Switch emulator.";
+ private const string CliendId = "568815339807309834";
- public static RichPresence DiscordPresence { get; private set; }
+ private static DiscordRpcClient _discordClient;
+ private static RichPresence _discordPresenceMain;
public static void Initialize()
{
- DiscordPresence = new RichPresence
+ _discordPresenceMain = new RichPresence
{
Assets = new Assets
{
LargeImageKey = "ryujinx",
- LargeImageText = LargeDescription
+ LargeImageText = Description
},
Details = "Main Menu",
State = "Idling",
- Timestamps = new Timestamps(DateTime.UtcNow)
+ Timestamps = Timestamps.Now,
+ Buttons = new Button[]
+ {
+ new Button()
+ {
+ Label = "Website",
+ Url = "https://ryujinx.org/"
+ }
+ }
};
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
}
- private static void Update(object sender, ReactiveEventArgs<bool> e)
+ private static void Update(object sender, ReactiveEventArgs<bool> evnt)
{
- if (e.OldValue != e.NewValue)
+ if (evnt.OldValue != evnt.NewValue)
{
// If the integration was active, disable it and unload everything
- if (e.OldValue)
+ if (evnt.OldValue)
{
_discordClient?.Dispose();
@@ -44,130 +51,49 @@ namespace Ryujinx.Modules
}
// If we need to activate it and the client isn't active, initialize it
- if (e.NewValue && _discordClient == null)
+ if (evnt.NewValue && _discordClient == null)
{
- _discordClient = new DiscordRpcClient("568815339807309834");
+ _discordClient = new DiscordRpcClient(CliendId);
_discordClient.Initialize();
- _discordClient.SetPresence(DiscordPresence);
+ _discordClient.SetPresence(_discordPresenceMain);
}
}
}
public static void SwitchToPlayingState(string titleId, string titleName)
{
- if (SupportedTitles.Contains(titleId))
- {
- DiscordPresence.Assets.LargeImageKey = titleId;
- }
-
- string state = titleId;
-
- if (state == null)
- {
- state = "Ryujinx";
- }
- else
- {
- state = state.ToUpper();
- }
-
- string details = "Idling";
-
- if (titleName != null)
+ _discordClient?.SetPresence(new RichPresence
{
- details = $"Playing {titleName}";
- }
-
- DiscordPresence.Details = details;
- DiscordPresence.State = state;
- DiscordPresence.Assets.LargeImageText = titleName;
- DiscordPresence.Assets.SmallImageKey = "ryujinx";
- DiscordPresence.Assets.SmallImageText = LargeDescription;
- DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
-
- _discordClient?.SetPresence(DiscordPresence);
+ Assets = new Assets
+ {
+ LargeImageKey = "game",
+ LargeImageText = titleName,
+ SmallImageKey = "ryujinx",
+ SmallImageText = Description,
+ },
+ Details = $"Playing {titleName}",
+ State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(),
+ Timestamps = Timestamps.Now,
+ Buttons = new Button[]
+ {
+ new Button()
+ {
+ Label = "Website",
+ Url = "https://ryujinx.org/"
+ }
+ }
+ });
}
public static void SwitchToMainMenu()
{
- DiscordPresence.Details = "Main Menu";
- DiscordPresence.State = "Idling";
- DiscordPresence.Assets.LargeImageKey = "ryujinx";
- DiscordPresence.Assets.LargeImageText = LargeDescription;
- DiscordPresence.Assets.SmallImageKey = null;
- DiscordPresence.Assets.SmallImageText = null;
- DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
-
- _discordClient?.SetPresence(DiscordPresence);
+ _discordClient?.SetPresence(_discordPresenceMain);
}
public static void Exit()
{
_discordClient?.Dispose();
}
-
- private static readonly string[] SupportedTitles =
- {
- "0100000000010000", // Super Mario Odyssey™
- "01000b900d8b0000", // Cadence of Hyrule – Crypt of the NecroDancer Featuring The Legend of Zelda
- "01000d200ac0c000", // Bud Spencer & Terence Hill - Slaps And Beans
- "01000d700be88000", // My Girlfriend is a Mermaid!?
- "01000dc007e90000", // Sparkle Unleashed
- "01000e2003fa0000", // MIGHTY GUNVOLT BURST
- "0100225000fee000", // Blaster Master Zero
- "010028d0045ce000", // Sparkle 2
- "01002b30028f6000", // Celeste
- "01002fc00c6d0000", // Witch Thief
- "010034e005c9c000", // Code of Princess EX
- "010036b0034e4000", // Super Mario Party™
- "01003d200baa2000", // Pokémon Mystery Dungeon™: Rescue Team DX
- "01004f8006a78000", // Super Meat Boy
- "010051f00ac5e000", // SEGA AGES Sonic The Hedgehog
- "010055d009f78000", // Fire Emblem™: Three Houses
- "010056e00853a000", // A Hat in Time
- "0100574009f9e000", // 嘘つき姫と盲目王子
- "01005d700e742000", // DOOM 64
- "0100628004bce000", // Nights of Azure 2: Bride of the New Moon
- "0100633007d48000", // Hollow Knight
- "010065500b218000", // メモリーズオフ -Innocent Fille-
- "010068f00aa78000", // FINAL FANTASY XV POCKET EDITION HD
- "01006bb00c6f0000", // The Legend of Zelda™: Link’s Awakening
- "01006f8002326000", // Animal Crossing™: New Horizons
- "01006a800016e000", // Super Smash Bros.™ Ultimate
- "010072800cbe8000", // PC Building Simulator
- "01007300020fa000", // ASTRAL CHAIN
- "01007330027ee000", // Ultra Street Fighter® II: The Final Challengers
- "0100749009844000", // 20XX
- "01007a4008486000", // Enchanting Mahjong Match
- "01007ef00011e000", // The Legend of Zelda™: Breath of the Wild
- "010080b00ad66000", // Undertale
- "010082400bcc6000", // Untitled Goose Game
- "01008db008c2c000", // Pokémon™ Shield
- "010094e00b52e000", // Capcom Beat 'Em Up Bundle
- "01009aa000faa000", // Sonic Mania
- "01009b90006dc000", // Super Mario Maker™ 2
- "01009cc00c97c000", // DEAD OR ALIVE Xtreme 3 Scarlet 基本無料版
- "0100ea80032ea000", // New Super Mario Bros.™ U Deluxe
- "0100a4200a284000", // LUMINES REMASTERED
- "0100a5c00d162000", // Cuphead
- "0100abf008968000", // Pokémon™ Sword
- "0100ae000aebc000", // Angels of Death
- "0100b3f000be2000", // Pokkén Tournament™ DX
- "0100bc2004ff4000", // Owlboy
- "0100cf3007578000", // Atari Flashback Classics
- "0100d5d00c6be000", // Our World Is Ended.
- "0100d6b00cd88000", // YUMENIKKI -DREAM DIARY-
- "0100d870045b6000", // Nintendo Entertainment System™ - Nintendo Switch Online
- "0100e0c00adac000", // SENRAN KAGURA Reflexions
- "0100e46006708000", // Terraria
- "0100e7200b272000", // Lanota
- "0100e9f00b882000", // null
- "0100eab00605c000", // Poly Bridge
- "0100efd00a4fa000", // Shantae and the Pirate's Curse
- "0100f6a00a684000", // ひぐらしのなく頃に奉
- "0100f9f00c696000", // Crash™ Team Racing Nitro-Fueled
- "051337133769a000", // RGB-Seizure
- };
}
} \ No newline at end of file
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index b1fe41a7..9600f9dc 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -1,4 +1,5 @@
using ARMeilleure.Translation.PTC;
+using FFmpeg.AutoGen;
using Gtk;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
@@ -9,6 +10,7 @@ using Ryujinx.Configuration;
using Ryujinx.Modules;
using Ryujinx.Ui;
using Ryujinx.Ui.Widgets;
+using SixLabors.ImageSharp.Formats.Jpeg;
using System;
using System.IO;
using System.Reflection;
@@ -76,6 +78,9 @@ namespace Ryujinx
if (OperatingSystem.IsLinux())
{
XInitThreads();
+
+ // Configure FFmpeg search path
+ ffmpeg.RootPath = "/lib";
}
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
@@ -98,6 +103,12 @@ namespace Ryujinx
// Initialize Discord integration.
DiscordIntegrationModule.Initialize();
+ // Sets ImageSharp Jpeg Encoder Quality.
+ SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
+ {
+ Quality = 100
+ });
+
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index 59edc919..1cd9595b 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -12,13 +12,13 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="DiscordRichPresence" Version="1.0.166" />
+ <PackageReference Include="DiscordRichPresence" Version="1.0.175" />
<PackageReference Include="GtkSharp" Version="3.22.25.128" />
<PackageReference Include="GtkSharp.Dependencies" Version="1.1.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
- <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.3.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
+ <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build7" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
<PackageReference Include="OpenTK.Graphics" Version="4.5.0" />
- <PackageReference Include="SPB" Version="0.0.2" />
+ <PackageReference Include="SPB" Version="0.0.3-build15" />
<PackageReference Include="SharpZipLib" Version="1.3.0" />
</ItemGroup>
@@ -26,6 +26,7 @@
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
+ <ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
diff --git a/Ryujinx/Ui/Applet/GtkHostUiHandler.cs b/Ryujinx/Ui/Applet/GtkHostUiHandler.cs
index 804a1a27..c227ebd3 100644
--- a/Ryujinx/Ui/Applet/GtkHostUiHandler.cs
+++ b/Ryujinx/Ui/Applet/GtkHostUiHandler.cs
@@ -131,8 +131,8 @@ namespace Ryujinx.Ui.Applet
public void ExecuteProgram(HLE.Switch device, ProgramSpecifyKind kind, ulong value)
{
- device.UserChannelPersistence.ExecuteProgram(kind, value);
- ((MainWindow)_parent).GlRendererWidget?.Exit();
+ device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value);
+ ((MainWindow)_parent).RendererWidget?.Exit();
}
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index 99c4698a..4c3f8ce4 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -1,10 +1,10 @@
using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
using Gdk;
+using Gtk;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Logging;
using Ryujinx.Configuration;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.HLE.HOS.Services.Hid;
@@ -13,9 +13,14 @@ using Ryujinx.Input.HLE;
using Ryujinx.Ui.Widgets;
using SPB.Graphics;
using SPB.Graphics.OpenGL;
+using SPB.Platform;
+using SPB.Platform.GLX;
+using SPB.Platform.WGL;
+using SPB.Windowing;
using System;
using System.Diagnostics;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Threading;
using Key = Ryujinx.Input.Key;
@@ -24,606 +29,123 @@ namespace Ryujinx.Ui
{
using Switch = HLE.Switch;
- public class GlRenderer : GLWidget
+ public class GlRenderer : RendererWidgetBase
{
- private const int SwitchPanelWidth = 1280;
- private const int SwitchPanelHeight = 720;
- private const int TargetFps = 60;
-
- public ManualResetEvent WaitEvent { get; set; }
- public NpadManager NpadManager { get; }
-
- public static event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
-
- private bool _isActive;
- private bool _isStopped;
- private bool _isFocused;
-
- private double _mouseX;
- private double _mouseY;
- private bool _mousePressed;
-
- private bool _toggleFullscreen;
- private bool _toggleDockedMode;
-
- private readonly long _ticksPerFrame;
-
- private long _ticks = 0;
-
- private readonly Stopwatch _chrono;
-
- private readonly Switch _device;
-
- private Renderer _renderer;
-
- private KeyboardHotkeyState _prevHotkeyState;
-
private GraphicsDebugLevel _glLogLevel;
- private readonly ManualResetEvent _exitEvent;
-
- // Hide Cursor
- const int CursorHideIdleTime = 8; // seconds
- private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor);
- private long _lastCursorMoveTime;
- private bool _hideCursorOnIdle;
- private InputManager _inputManager;
- private IKeyboard _keyboardInterface;
+ private bool _initializedOpenGL;
- public GlRenderer(Switch device, InputManager inputManager, GraphicsDebugLevel glLogLevel)
- : base (GetGraphicsMode(),
- 3, 3,
- glLogLevel == GraphicsDebugLevel.None
- ? OpenGLContextFlags.Compat
- : OpenGLContextFlags.Compat | OpenGLContextFlags.Debug)
- {
- _inputManager = inputManager;
- NpadManager = _inputManager.CreateNpadManager();
- _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
-
- NpadManager.ReloadConfiguration(ConfigurationState.Instance.Hid.InputConfig.Value.ToList());
-
- WaitEvent = new ManualResetEvent(false);
-
- _device = device;
-
- Initialized += GLRenderer_Initialized;
- Destroyed += GLRenderer_Destroyed;
- ShuttingDown += GLRenderer_ShuttingDown;
-
- Initialize();
-
- _chrono = new Stopwatch();
-
- _ticksPerFrame = Stopwatch.Frequency / TargetFps;
-
- AddEvents((int)(EventMask.ButtonPressMask
- | EventMask.ButtonReleaseMask
- | EventMask.PointerMotionMask
- | EventMask.KeyPressMask
- | EventMask.KeyReleaseMask));
-
- Shown += Renderer_Shown;
+ private OpenGLContextBase _openGLContext;
+ private SwappableNativeWindowBase _nativeWindow;
+ public GlRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel)
+ {
_glLogLevel = glLogLevel;
-
- _exitEvent = new ManualResetEvent(false);
-
- _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
- _lastCursorMoveTime = Stopwatch.GetTimestamp();
-
- ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged;
}
- private void HideCursorStateChanged(object sender, ReactiveEventArgs<bool> state)
+ protected override bool OnDrawn(Cairo.Context cr)
{
- Gtk.Application.Invoke(delegate
+ if (!_initializedOpenGL)
{
- _hideCursorOnIdle = state.NewValue;
-
- if (_hideCursorOnIdle)
- {
- _lastCursorMoveTime = Stopwatch.GetTimestamp();
- }
- else
- {
- Window.Cursor = null;
- }
- });
- }
-
- private static FramebufferFormat GetGraphicsMode()
- {
- return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default;
- }
-
- private void GLRenderer_ShuttingDown(object sender, EventArgs args)
- {
- _device.DisposeGpu();
- NpadManager.Dispose();
- }
-
- private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args)
- {
- _isFocused = false;
- }
-
- private void Parent_FocusInEvent(object o, Gtk.FocusInEventArgs args)
- {
- _isFocused = true;
- }
-
- private void GLRenderer_Destroyed(object sender, EventArgs e)
- {
- ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
-
- NpadManager.Dispose();
- Dispose();
- }
+ IntializeOpenGL();
+ }
- protected void Renderer_Shown(object sender, EventArgs e)
- {
- _isFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused);
+ return true;
}
- public void HandleScreenState(KeyboardStateSnapshot keyboard)
+ private void IntializeOpenGL()
{
- bool toggleFullscreen = keyboard.IsPressed(Key.F11)
- || ((keyboard.IsPressed(Key.AltLeft)
- || keyboard.IsPressed(Key.AltRight))
- && keyboard.IsPressed(Key.Enter))
- || keyboard.IsPressed(Key.Escape);
-
- bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen);
-
- if (toggleFullscreen != _toggleFullscreen)
- {
- if (toggleFullscreen)
- {
- if (fullScreenToggled)
- {
- ParentWindow.Unfullscreen();
- (Toplevel as MainWindow)?.ToggleExtraWidgets(true);
- }
- else
- {
- if (keyboard.IsPressed(Key.Escape))
- {
- if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog())
- {
- Exit();
- }
- }
- else
- {
- ParentWindow.Fullscreen();
- (Toplevel as MainWindow)?.ToggleExtraWidgets(false);
- }
- }
- }
- }
+ _nativeWindow = RetrieveNativeWindow();
- _toggleFullscreen = toggleFullscreen;
+ Window.EnsureNative();
- bool toggleDockedMode = keyboard.IsPressed(Key.F9);
-
- if (toggleDockedMode != _toggleDockedMode)
- {
- if (toggleDockedMode)
- {
- ConfigurationState.Instance.System.EnableDockedMode.Value =
- !ConfigurationState.Instance.System.EnableDockedMode.Value;
- }
- }
-
- _toggleDockedMode = toggleDockedMode;
-
- if (_hideCursorOnIdle)
- {
- long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;
- Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null;
- }
- }
+ _openGLContext = PlatformHelper.CreateOpenGLContext(GetGraphicsMode(), 3, 3, _glLogLevel == GraphicsDebugLevel.None ? OpenGLContextFlags.Compat : OpenGLContextFlags.Compat | OpenGLContextFlags.Debug);
+ _openGLContext.Initialize(_nativeWindow);
+ _openGLContext.MakeCurrent(_nativeWindow);
- private void GLRenderer_Initialized(object sender, EventArgs e)
- {
// Release the GL exclusivity that SPB gave us as we aren't going to use it in GTK Thread.
- OpenGLContext.MakeCurrent(null);
+ _openGLContext.MakeCurrent(null);
WaitEvent.Set();
- }
-
- protected override bool OnConfigureEvent(EventConfigure evnt)
- {
- bool result = base.OnConfigureEvent(evnt);
-
- Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
-
- _renderer.Window.SetSize(evnt.Width * monitor.ScaleFactor, evnt.Height * monitor.ScaleFactor);
-
- return result;
- }
-
- public void Start()
- {
- _chrono.Restart();
- _isActive = true;
-
- Gtk.Window parent = this.Toplevel as Gtk.Window;
-
- parent.FocusInEvent += Parent_FocusInEvent;
- parent.FocusOutEvent += Parent_FocusOutEvent;
-
- Gtk.Application.Invoke(delegate
- {
- parent.Present();
-
- string titleNameSection = string.IsNullOrWhiteSpace(_device.Application.TitleName) ? string.Empty
- : $" - {_device.Application.TitleName}";
-
- string titleVersionSection = string.IsNullOrWhiteSpace(_device.Application.DisplayVersion) ? string.Empty
- : $" v{_device.Application.DisplayVersion}";
-
- string titleIdSection = string.IsNullOrWhiteSpace(_device.Application.TitleIdText) ? string.Empty
- : $" ({_device.Application.TitleIdText.ToUpper()})";
-
- string titleArchSection = _device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)";
-
- parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
- });
-
- Thread renderLoopThread = new Thread(Render)
- {
- Name = "GUI.RenderLoop"
- };
- renderLoopThread.Start();
-
- Thread nvStutterWorkaround = new Thread(NVStutterWorkaround)
- {
- Name = "GUI.NVStutterWorkaround"
- };
- nvStutterWorkaround.Start();
-
- MainLoop();
-
- renderLoopThread.Join();
- nvStutterWorkaround.Join();
-
- Exit();
+ _initializedOpenGL = true;
}
- private void NVStutterWorkaround()
+ private SwappableNativeWindowBase RetrieveNativeWindow()
{
- while (_isActive)
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- // When NVIDIA Threaded Optimization is on, the driver will snapshot all threads in the system whenever the application creates any new ones.
- // The ThreadPool has something called a "GateThread" which terminates itself after some inactivity.
- // However, it immediately starts up again, since the rules regarding when to terminate and when to start differ.
- // This creates a new thread every second or so.
- // The main problem with this is that the thread snapshot can take 70ms, is on the OpenGL thread and will delay rendering any graphics.
- // This is a little over budget on a frame time of 16ms, so creates a large stutter.
- // The solution is to keep the ThreadPool active so that it never has a reason to terminate the GateThread.
-
- // TODO: This should be removed when the issue with the GateThread is resolved.
+ IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle);
- ThreadPool.QueueUserWorkItem((state) => { });
- Thread.Sleep(300);
+ return new WGLWindow(new NativeHandle(windowHandle));
}
- }
-
- protected override bool OnButtonPressEvent(EventButton evnt)
- {
- _mouseX = evnt.X;
- _mouseY = evnt.Y;
-
- if (evnt.Button == 1)
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
- _mousePressed = true;
- }
-
- return false;
- }
+ IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle);
+ IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle);
- protected override bool OnButtonReleaseEvent(EventButton evnt)
- {
- if (evnt.Button == 1)
- {
- _mousePressed = false;
- }
-
- return false;
- }
-
- protected override bool OnMotionNotifyEvent(EventMotion evnt)
- {
- if (evnt.Device.InputSource == InputSource.Mouse)
- {
- _mouseX = evnt.X;
- _mouseY = evnt.Y;
- }
-
- if (_hideCursorOnIdle)
- {
- _lastCursorMoveTime = Stopwatch.GetTimestamp();
- }
-
- return false;
- }
-
- protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight)
- {
- Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
-
- // If the monitor is at least 1080p, use the Switch panel size as minimal size.
- if (monitor.Geometry.Height >= 1080)
- {
- minimumHeight = SwitchPanelHeight;
- }
- // Otherwise, we default minimal size to 480p 16:9.
- else
- {
- minimumHeight = 480;
- }
-
- naturalHeight = minimumHeight;
- }
-
- protected override void OnGetPreferredWidth(out int minimumWidth, out int naturalWidth)
- {
- Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
-
- // If the monitor is at least 1080p, use the Switch panel size as minimal size.
- if (monitor.Geometry.Height >= 1080)
- {
- minimumWidth = SwitchPanelWidth;
- }
- // Otherwise, we default minimal size to 480p 16:9.
- else
- {
- minimumWidth = 854;
+ return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle));
}
- naturalWidth = minimumWidth;
+ throw new NotImplementedException();
}
- public void Exit()
- {
- NpadManager?.Dispose();
-
- if (_isStopped)
- {
- return;
- }
+ [DllImport("libgdk-3-0.dll")]
+ private static extern IntPtr gdk_win32_window_get_handle(IntPtr d);
- _isStopped = true;
- _isActive = false;
+ [DllImport("libgdk-3.so.0")]
+ private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay);
- _exitEvent.WaitOne();
- _exitEvent.Dispose();
- }
+ [DllImport("libgdk-3.so.0")]
+ private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow);
- public void Initialize()
+ private static FramebufferFormat GetGraphicsMode()
{
- if (!(_device.Gpu.Renderer is Renderer))
- {
- throw new NotSupportedException($"GPU renderer must be an OpenGL renderer when using {typeof(Renderer).Name}!");
- }
-
- _renderer = (Renderer)_device.Gpu.Renderer;
+ return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default;
}
- public void Render()
+ public override void InitializeRenderer()
{
// First take exclusivity on the OpenGL context.
- _renderer.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(OpenGLContext));
-
- Gtk.Window parent = Toplevel as Gtk.Window;
- parent.Present();
+ ((Renderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext));
- OpenGLContext.MakeCurrent(NativeWindow);
+ _openGLContext.MakeCurrent(_nativeWindow);
- _device.Gpu.Renderer.Initialize(_glLogLevel);
-
- // Make sure the first frame is not transparent.
GL.ClearColor(0, 0, 0, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
SwapBuffers();
-
- _device.Gpu.InitializeShaderCache();
- Translator.IsReadyForTranslation.Set();
-
- while (_isActive)
- {
- if (_isStopped)
- {
- return;
- }
-
- _ticks += _chrono.ElapsedTicks;
-
- _chrono.Restart();
-
- if (_device.WaitFifo())
- {
- _device.Statistics.RecordFifoStart();
- _device.ProcessFrame();
- _device.Statistics.RecordFifoEnd();
- }
-
- while (_device.ConsumeFrameAvailable())
- {
- _device.PresentFrame(SwapBuffers);
- }
-
- if (_ticks >= _ticksPerFrame)
- {
- string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld";
- float scale = Graphics.Gpu.GraphicsConfig.ResScale;
- if (scale != 1)
- {
- dockedMode += $" ({scale}x)";
- }
-
- StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
- _device.EnableDeviceVsync,
- dockedMode,
- ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
- $"Game: {_device.Statistics.GetGameFrameRate():00.00} FPS",
- $"FIFO: {_device.Statistics.GetFifoPercent():0.00} %",
- $"GPU: {_renderer.GpuVendor}"));
-
- _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
- }
- }
}
- public void SwapBuffers()
+ public override void SwapBuffers()
{
- NativeWindow.SwapBuffers();
+ _nativeWindow.SwapBuffers();
}
- public void MainLoop()
+ public override string GetGpuVendorName()
{
- while (_isActive)
- {
- UpdateFrame();
-
- // Polling becomes expensive if it's not slept
- Thread.Sleep(1);
- }
-
- _exitEvent.Set();
+ return ((Renderer)Renderer).GpuVendor;
}
- private bool UpdateFrame()
+ protected override void Dispose(bool disposing)
{
- if (!_isActive)
- {
- return true;
- }
-
- if (_isStopped)
- {
- return false;
- }
-
- if (_isFocused)
- {
- Gtk.Application.Invoke(delegate
- {
- KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot();
-
- HandleScreenState(keyboard);
-
- if (keyboard.IsPressed(Key.Delete))
- {
- if (!ParentWindow.State.HasFlag(WindowState.Fullscreen))
- {
- Ptc.Continue();
- }
- }
- });
- }
-
- NpadManager.Update(_device.Hid, _device.TamperMachine);
-
- if(_isFocused)
- {
- KeyboardHotkeyState currentHotkeyState = GetHotkeyState();
-
- if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync) &&
- !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync))
- {
- _device.EnableDeviceVsync = !_device.EnableDeviceVsync;
- }
-
- _prevHotkeyState = currentHotkeyState;
- }
-
- //Touchscreen
- bool hasTouch = false;
-
- // Get screen touch position from left mouse click
- // OpenTK always captures mouse events, even if out of focus, so check if window is focused.
- if (_isFocused && _mousePressed)
- {
- float aspectWidth = SwitchPanelHeight * ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat();
-
- int screenWidth = AllocatedWidth;
- int screenHeight = AllocatedHeight;
-
- if (AllocatedWidth > AllocatedHeight * aspectWidth / SwitchPanelHeight)
- {
- screenWidth = (int)(AllocatedHeight * aspectWidth) / SwitchPanelHeight;
- }
- else
- {
- screenHeight = (AllocatedWidth * SwitchPanelHeight) / (int)aspectWidth;
- }
-
- int startX = (AllocatedWidth - screenWidth) >> 1;
- int startY = (AllocatedHeight - screenHeight) >> 1;
-
- int endX = startX + screenWidth;
- int endY = startY + screenHeight;
-
-
- if (_mouseX >= startX &&
- _mouseY >= startY &&
- _mouseX < endX &&
- _mouseY < endY)
- {
- int screenMouseX = (int)_mouseX - startX;
- int screenMouseY = (int)_mouseY - startY;
-
- int mX = (screenMouseX * (int)aspectWidth) / screenWidth;
- int mY = (screenMouseY * SwitchPanelHeight) / screenHeight;
-
- TouchPoint currentPoint = new TouchPoint
- {
- X = (uint)mX,
- Y = (uint)mY,
-
- // Placeholder values till more data is acquired
- DiameterX = 10,
- DiameterY = 10,
- Angle = 90
- };
-
- hasTouch = true;
-
- _device.Hid.Touchscreen.Update(currentPoint);
- }
- }
-
- if (!hasTouch)
+ // Try to bind the OpenGL context before calling the shutdown event
+ try
{
- _device.Hid.Touchscreen.Update();
+ _openGLContext?.MakeCurrent(_nativeWindow);
}
+ catch (Exception) { }
- _device.Hid.DebugPad.Update();
-
- return true;
- }
-
- [Flags]
- private enum KeyboardHotkeyState
- {
- None,
- ToggleVSync
- }
-
- private KeyboardHotkeyState GetHotkeyState()
- {
- KeyboardHotkeyState state = KeyboardHotkeyState.None;
+ Device.DisposeGpu();
+ NpadManager.Dispose();
- if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync))
+ // Unbind context and destroy everything
+ try
{
- state |= KeyboardHotkeyState.ToggleVSync;
+ _openGLContext?.MakeCurrent(null);
}
+ catch (Exception) { }
- return state;
+ _openGLContext.Dispose();
}
}
}
diff --git a/Ryujinx/Ui/GLWidget.cs b/Ryujinx/Ui/GLWidget.cs
deleted file mode 100644
index a465aeef..00000000
--- a/Ryujinx/Ui/GLWidget.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using Gtk;
-using SPB.Graphics;
-using SPB.Graphics.OpenGL;
-using SPB.Platform;
-using SPB.Platform.GLX;
-using SPB.Platform.WGL;
-using SPB.Windowing;
-using System;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Ui
-{
- [ToolboxItem(true)]
- public class GLWidget : DrawingArea
- {
- private bool _initialized;
-
- public event EventHandler Initialized;
- public event EventHandler ShuttingDown;
-
- public OpenGLContextBase OpenGLContext { get; private set; }
- public NativeWindowBase NativeWindow { get; private set; }
-
- public FramebufferFormat FramebufferFormat { get; }
- public int GLVersionMajor { get; }
- public int GLVersionMinor { get; }
- public OpenGLContextFlags ContextFlags { get; }
-
- public bool DirectRendering { get; }
- public OpenGLContextBase SharedContext { get; }
-
- public GLWidget(FramebufferFormat framebufferFormat, int major, int minor, OpenGLContextFlags flags = OpenGLContextFlags.Default, bool directRendering = true, OpenGLContextBase sharedContext = null)
- {
- FramebufferFormat = framebufferFormat;
- GLVersionMajor = major;
- GLVersionMinor = minor;
- ContextFlags = flags;
- DirectRendering = directRendering;
- SharedContext = sharedContext;
- }
-
- protected override bool OnDrawn(Cairo.Context cr)
- {
- if (!_initialized)
- {
- Intialize();
- }
-
- return true;
- }
-
- private NativeWindowBase RetrieveNativeWindow()
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle);
-
- return new WGLWindow(new NativeHandle(windowHandle));
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle);
- IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle);
-
- return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle));
- }
-
- throw new NotImplementedException();
- }
-
- [DllImport("libgdk-3-0.dll")]
- private static extern IntPtr gdk_win32_window_get_handle(IntPtr d);
-
- [DllImport("libgdk-3.so.0")]
- private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay);
-
- [DllImport("libgdk-3.so.0")]
- private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow);
-
- private void Intialize()
- {
- NativeWindow = RetrieveNativeWindow();
-
- Window.EnsureNative();
-
- OpenGLContext = PlatformHelper.CreateOpenGLContext(FramebufferFormat, GLVersionMajor, GLVersionMinor, ContextFlags, DirectRendering, SharedContext);
-
- OpenGLContext.Initialize(NativeWindow);
- OpenGLContext.MakeCurrent(NativeWindow);
-
- _initialized = true;
-
- Initialized?.Invoke(this, EventArgs.Empty);
- }
-
- protected override void Dispose(bool disposing)
- {
- // Try to bind the OpenGL context before calling the shutdown event
- try
- {
- OpenGLContext?.MakeCurrent(NativeWindow);
- }
- catch (Exception) { }
-
- ShuttingDown?.Invoke(this, EventArgs.Empty);
-
- // Unbind context and destroy everything
- try
- {
- OpenGLContext?.MakeCurrent(null);
- }
- catch (Exception) { }
-
- OpenGLContext.Dispose();
- }
- }
-}
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index 433d23dc..1eef7554 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -2,11 +2,14 @@ using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
using Gtk;
using LibHac.Common;
+using LibHac.FsSystem;
using LibHac.Ns;
using Ryujinx.Audio.Backends.Dummy;
using Ryujinx.Audio.Backends.OpenAL;
+using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Audio.Integration;
+using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.System;
@@ -17,6 +20,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input.GTK3;
using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2;
@@ -67,9 +71,11 @@ namespace Ryujinx.Ui
private string _lastScannedAmiiboId = "";
private bool _lastScannedAmiiboShowAll = false;
- public GlRenderer GlRendererWidget;
+ public RendererWidgetBase RendererWidget;
public InputManager InputManager;
+ private static bool UseVulkan = false;
+
#pragma warning disable CS0169, CS0649, IDE0044
[GUI] public MenuItem ExitMenuItem;
@@ -78,6 +84,7 @@ namespace Ryujinx.Ui
[GUI] Box _footerBox;
[GUI] Box _statusBar;
[GUI] MenuItem _optionMenu;
+ [GUI] MenuItem _manageUserProfiles;
[GUI] MenuItem _actionMenu;
[GUI] MenuItem _stopEmulation;
[GUI] MenuItem _simulateWakeUpMessage;
@@ -140,7 +147,7 @@ namespace Ryujinx.Ui
// Instanciate HLE objects.
_virtualFileSystem = VirtualFileSystem.CreateInstance();
_contentManager = new ContentManager(_virtualFileSystem);
- _accountManager = new AccountManager();
+ _accountManager = new AccountManager(_virtualFileSystem);
_userChannelPersistence = new UserChannelPersistence();
// Instanciate GUI objects.
@@ -155,11 +162,16 @@ namespace Ryujinx.Ui
_applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
_actionMenu.StateChanged += ActionMenu_StateChanged;
+ _optionMenu.StateChanged += OptionMenu_StateChanged;
_gameTable.ButtonReleaseEvent += Row_Clicked;
_fullScreen.Activated += FullScreen_Toggled;
- GlRenderer.StatusUpdatedEvent += Update_StatusBar;
+ RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar;
+
+ ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
+ ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
+ ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
if (ConfigurationState.Instance.Ui.StartFullscreen)
{
@@ -231,6 +243,30 @@ namespace Ryujinx.Ui
InputManager = new InputManager(new GTK3KeyboardDriver(this), new SDL2GamepadDriver());
}
+ private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs<bool> args)
+ {
+ if (_emulationContext != null)
+ {
+ _emulationContext.Configuration.IgnoreMissingServices = args.NewValue;
+ }
+ }
+
+ private void UpdateAspectRatioState(object sender, ReactiveEventArgs<AspectRatio> args)
+ {
+ if (_emulationContext != null)
+ {
+ _emulationContext.Configuration.AspectRatio = args.NewValue;
+ }
+ }
+
+ private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e)
+ {
+ if (_emulationContext != null)
+ {
+ _emulationContext.System.ChangeDockedModeState(e.NewValue);
+ }
+ }
+
private void WindowStateEvent_Changed(object o, WindowStateEventArgs args)
{
_fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen";
@@ -310,10 +346,31 @@ namespace Ryujinx.Ui
{
_virtualFileSystem.Reload();
- IRenderer renderer = new Renderer();
+ IRenderer renderer;
+
+ if (UseVulkan)
+ {
+ throw new NotImplementedException();
+ }
+ else
+ {
+ renderer = new Renderer();
+ }
+
IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver();
- if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo)
+ if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SDL2)
+ {
+ if (SDL2HardwareDeviceDriver.IsSupported)
+ {
+ deviceDriver = new SDL2HardwareDeviceDriver();
+ }
+ else
+ {
+ Logger.Warning?.Print(LogClass.Audio, "SDL2 audio is not supported, falling back to dummy audio out.");
+ }
+ }
+ else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo)
{
if (SoundIoHardwareDeviceDriver.IsSupported)
{
@@ -354,19 +411,29 @@ namespace Ryujinx.Ui
? HLE.MemoryConfiguration.MemoryConfiguration6GB
: HLE.MemoryConfiguration.MemoryConfiguration4GB;
- _emulationContext = new HLE.Switch(
- _virtualFileSystem,
- _contentManager,
- _accountManager,
- _userChannelPersistence,
- renderer,
- deviceDriver,
- memoryConfiguration)
- {
- UiHandler = _uiHandler
- };
-
- _emulationContext.Initialize();
+ IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
+
+ HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(_virtualFileSystem,
+ _contentManager,
+ _accountManager,
+ _userChannelPersistence,
+ renderer,
+ deviceDriver,
+ memoryConfiguration,
+ _uiHandler,
+ (SystemLanguage)ConfigurationState.Instance.System.Language.Value,
+ (RegionCode)ConfigurationState.Instance.System.Region.Value,
+ ConfigurationState.Instance.Graphics.EnableVsync,
+ ConfigurationState.Instance.System.EnableDockedMode,
+ ConfigurationState.Instance.System.EnablePtc,
+ fsIntegrityCheckLevel,
+ ConfigurationState.Instance.System.FsGlobalAccessLogMode,
+ ConfigurationState.Instance.System.SystemTimeOffset,
+ ConfigurationState.Instance.System.TimeZone,
+ ConfigurationState.Instance.System.IgnoreMissingServices,
+ ConfigurationState.Instance.Graphics.AspectRatio);
+
+ _emulationContext = new HLE.Switch(configuration);
}
private void SetupProgressUiHandlers()
@@ -480,6 +547,10 @@ namespace Ryujinx.Ui
Logger.RestartTime();
+ RendererWidget = CreateRendererWidget();
+
+ SwitchToRenderWidget();
+
InitializeSwitchInstance();
UpdateGraphicsConfig();
@@ -505,6 +576,8 @@ namespace Ryujinx.Ui
UserErrorDialog.CreateUserErrorDialog(userError);
_emulationContext.Dispose();
+ SwitchToGameTable();
+ RendererWidget.Dispose();
return;
}
@@ -515,6 +588,8 @@ namespace Ryujinx.Ui
UserErrorDialog.CreateUserErrorDialog(userError);
_emulationContext.Dispose();
+ SwitchToGameTable();
+ RendererWidget.Dispose();
return;
}
@@ -536,6 +611,8 @@ namespace Ryujinx.Ui
UserErrorDialog.CreateUserErrorDialog(userError);
_emulationContext.Dispose();
+ SwitchToGameTable();
+ RendererWidget.Dispose();
return;
}
@@ -598,6 +675,7 @@ namespace Ryujinx.Ui
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
_emulationContext.Dispose();
+ RendererWidget.Dispose();
return;
}
@@ -638,91 +716,108 @@ namespace Ryujinx.Ui
}
}
- private void CreateGameWindow()
+ private RendererWidgetBase CreateRendererWidget()
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (UseVulkan)
{
- _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1);
+ return new VKRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
}
+ else
+ {
+ return new GlRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
+ }
+ }
- DisplaySleep.Prevent();
+ private void SwitchToRenderWidget()
+ {
+ _viewBox.Remove(_gameTableWindow);
+ RendererWidget.Expand = true;
+ _viewBox.Child = RendererWidget;
- GlRendererWidget = new GlRenderer(_emulationContext, InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
+ RendererWidget.ShowAll();
+ EditFooterForGameRenderer();
- Application.Invoke(delegate
+ if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
+ {
+ ToggleExtraWidgets(false);
+ }
+ else if (ConfigurationState.Instance.Ui.StartFullscreen.Value)
{
- _viewBox.Remove(_gameTableWindow);
- GlRendererWidget.Expand = true;
- _viewBox.Child = GlRendererWidget;
+ FullScreen_Toggled(null, null);
+ }
+ }
- GlRendererWidget.ShowAll();
- EditFooterForGameRenderer();
+ private void SwitchToGameTable()
+ {
+ if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
+ {
+ ToggleExtraWidgets(true);
+ }
- if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
- {
- ToggleExtraWidgets(false);
- }
- else if (ConfigurationState.Instance.Ui.StartFullscreen.Value)
- {
- FullScreen_Toggled(null, null);
- }
- });
+ RendererWidget.Exit();
+
+ if (RendererWidget.Window != Window && RendererWidget.Window != null)
+ {
+ RendererWidget.Window.Dispose();
+ }
- GlRendererWidget.WaitEvent.WaitOne();
+ RendererWidget.Dispose();
- GlRendererWidget.Start();
+ _windowsMultimediaTimerResolution?.Dispose();
+ _windowsMultimediaTimerResolution = null;
+ DisplaySleep.Restore();
- Ptc.Close();
- PtcProfiler.Stop();
+ _viewBox.Remove(RendererWidget);
+ _viewBox.Add(_gameTableWindow);
- _emulationContext.Dispose();
- _deviceExitStatus.Set();
+ _gameTableWindow.Expand = true;
- // NOTE: Everything that is here will not be executed when you close the UI.
- Application.Invoke(delegate
- {
- if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
- {
- ToggleExtraWidgets(true);
- }
+ Window.Title = $"Ryujinx {Program.Version}";
- GlRendererWidget.Exit();
+ _emulationContext = null;
+ _gameLoaded = false;
+ RendererWidget = null;
- if (GlRendererWidget.Window != Window && GlRendererWidget.Window != null)
- {
- GlRendererWidget.Window.Dispose();
- }
+ DiscordIntegrationModule.SwitchToMainMenu();
+
+ RecreateFooterForMenu();
- GlRendererWidget.Dispose();
+ UpdateColumns();
+ UpdateGameTable();
- _windowsMultimediaTimerResolution?.Dispose();
- _windowsMultimediaTimerResolution = null;
- DisplaySleep.Restore();
+ Task.Run(RefreshFirmwareLabel);
+ Task.Run(HandleRelaunch);
- _viewBox.Remove(GlRendererWidget);
- _viewBox.Add(_gameTableWindow);
+ _actionMenu.Sensitive = false;
+ _firmwareInstallFile.Sensitive = true;
+ _firmwareInstallDirectory.Sensitive = true;
+ }
- _gameTableWindow.Expand = true;
+ private void CreateGameWindow()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1);
+ }
- Window.Title = $"Ryujinx {Program.Version}";
+ DisplaySleep.Prevent();
- _emulationContext = null;
- _gameLoaded = false;
- GlRendererWidget = null;
+ RendererWidget.Initialize(_emulationContext);
- DiscordIntegrationModule.SwitchToMainMenu();
+ RendererWidget.WaitEvent.WaitOne();
- RecreateFooterForMenu();
+ RendererWidget.Start();
- UpdateColumns();
- UpdateGameTable();
+ Ptc.Close();
+ PtcProfiler.Stop();
- Task.Run(RefreshFirmwareLabel);
- Task.Run(HandleRelaunch);
+ _emulationContext.Dispose();
+ _deviceExitStatus.Set();
- _actionMenu.Sensitive = false;
- _firmwareInstallFile.Sensitive = true;
- _firmwareInstallDirectory.Sensitive = true;
+ // NOTE: Everything that is here will not be executed when you close the UI.
+ Application.Invoke(delegate
+ {
+ SwitchToGameTable();
});
}
@@ -740,7 +835,7 @@ namespace Ryujinx.Ui
public void ToggleExtraWidgets(bool show)
{
- if (GlRendererWidget != null)
+ if (RendererWidget != null)
{
if (show)
{
@@ -799,14 +894,14 @@ namespace Ryujinx.Ui
{
UpdateGameMetadata(_emulationContext.Application.TitleIdText);
- if (GlRendererWidget != null)
+ if (RendererWidget != null)
{
// We tell the widget that we are exiting.
- GlRendererWidget.Exit();
+ RendererWidget.Exit();
// Wait for the other thread to dispose the HLE context before exiting.
_deviceExitStatus.WaitOne();
- GlRendererWidget.Dispose();
+ RendererWidget.Dispose();
}
}
@@ -1025,7 +1120,7 @@ namespace Ryujinx.Ui
private void StopEmulation_Pressed(object sender, EventArgs args)
{
- GlRendererWidget?.Exit();
+ RendererWidget?.Exit();
}
private void Installer_File_Pressed(object o, EventArgs args)
@@ -1192,6 +1287,11 @@ namespace Ryujinx.Ui
SaveConfig();
}
+ private void OptionMenu_StateChanged(object o, StateChangedArgs args)
+ {
+ _manageUserProfiles.Sensitive = _emulationContext == null;
+ }
+
private void Settings_Pressed(object sender, EventArgs args)
{
SettingsWindow settingsWindow = new SettingsWindow(this, _virtualFileSystem, _contentManager);
@@ -1200,6 +1300,14 @@ namespace Ryujinx.Ui
settingsWindow.Show();
}
+ private void ManageUserProfiles_Pressed(object sender, EventArgs args)
+ {
+ UserProfilesManagerWindow userProfilesManagerWindow = new UserProfilesManagerWindow(_accountManager, _contentManager, _virtualFileSystem);
+
+ userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor));
+ userProfilesManagerWindow.Show();
+ }
+
private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args)
{
if (_emulationContext != null)
diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade
index beeed265..129b768e 100644
--- a/Ryujinx/Ui/MainWindow.glade
+++ b/Ryujinx/Ui/MainWindow.glade
@@ -248,6 +248,16 @@
<signal name="activate" handler="Settings_Pressed" swapped="no"/>
</object>
</child>
+ <child>
+ <object class="GtkMenuItem" id="_manageUserProfiles">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Open User Profiles Manager window</property>
+ <property name="label" translatable="yes">Manage User Profiles</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="ManageUserProfiles_Pressed" swapped="no"/>
+ </object>
+ </child>
</object>
</child>
</object>
diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs
new file mode 100644
index 00000000..00882ba0
--- /dev/null
+++ b/Ryujinx/Ui/RendererWidgetBase.cs
@@ -0,0 +1,591 @@
+using ARMeilleure.Translation;
+using ARMeilleure.Translation.PTC;
+using Gdk;
+using Gtk;
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Configuration;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.HLE.HOS.Services.Hid;
+using Ryujinx.Input;
+using Ryujinx.Input.HLE;
+using Ryujinx.Ui.Widgets;
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+
+namespace Ryujinx.Ui
+{
+ using Key = Input.Key;
+ using Switch = HLE.Switch;
+
+ public abstract class RendererWidgetBase : DrawingArea
+ {
+ private const int SwitchPanelWidth = 1280;
+ private const int SwitchPanelHeight = 720;
+ private const int TargetFps = 60;
+
+ public ManualResetEvent WaitEvent { get; set; }
+ public NpadManager NpadManager { get; }
+ public Switch Device { get; private set; }
+ public IRenderer Renderer { get; private set; }
+
+ public static event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
+
+ private bool _isActive;
+ private bool _isStopped;
+ private bool _isFocused;
+
+ private double _mouseX;
+ private double _mouseY;
+ private bool _mousePressed;
+
+ private bool _toggleFullscreen;
+ private bool _toggleDockedMode;
+
+ private readonly long _ticksPerFrame;
+
+ private long _ticks = 0;
+
+ private readonly Stopwatch _chrono;
+
+ private KeyboardHotkeyState _prevHotkeyState;
+
+ private readonly ManualResetEvent _exitEvent;
+
+ // Hide Cursor
+ const int CursorHideIdleTime = 8; // seconds
+ private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor);
+ private long _lastCursorMoveTime;
+ private bool _hideCursorOnIdle;
+ private InputManager _inputManager;
+ private IKeyboard _keyboardInterface;
+ private GraphicsDebugLevel _glLogLevel;
+ private string _gpuVendorName;
+
+ private int _windowHeight;
+ private int _windowWidth;
+
+ public RendererWidgetBase(InputManager inputManager, GraphicsDebugLevel glLogLevel)
+ {
+ _inputManager = inputManager;
+ NpadManager = _inputManager.CreateNpadManager();
+ _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
+
+ WaitEvent = new ManualResetEvent(false);
+
+ _glLogLevel = glLogLevel;
+
+ Destroyed += Renderer_Destroyed;
+
+ _chrono = new Stopwatch();
+
+ _ticksPerFrame = Stopwatch.Frequency / TargetFps;
+
+ AddEvents((int)(EventMask.ButtonPressMask
+ | EventMask.ButtonReleaseMask
+ | EventMask.PointerMotionMask
+ | EventMask.KeyPressMask
+ | EventMask.KeyReleaseMask));
+
+ Shown += Renderer_Shown;
+
+ _exitEvent = new ManualResetEvent(false);
+
+ _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
+ _lastCursorMoveTime = Stopwatch.GetTimestamp();
+
+ ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged;
+ }
+
+ public abstract void InitializeRenderer();
+
+ public abstract void SwapBuffers();
+
+ public abstract string GetGpuVendorName();
+
+ private void HideCursorStateChanged(object sender, ReactiveEventArgs<bool> state)
+ {
+ Gtk.Application.Invoke(delegate
+ {
+ _hideCursorOnIdle = state.NewValue;
+
+ if (_hideCursorOnIdle)
+ {
+ _lastCursorMoveTime = Stopwatch.GetTimestamp();
+ }
+ else
+ {
+ Window.Cursor = null;
+ }
+ });
+ }
+
+ private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args)
+ {
+ _isFocused = false;
+ }
+
+ private void Parent_FocusInEvent(object o, Gtk.FocusInEventArgs args)
+ {
+ _isFocused = true;
+ }
+
+ private void Renderer_Destroyed(object sender, EventArgs e)
+ {
+ ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
+
+ NpadManager.Dispose();
+ Dispose();
+ }
+
+ private void Renderer_Shown(object sender, EventArgs e)
+ {
+ _isFocused = ParentWindow.State.HasFlag(Gdk.WindowState.Focused);
+ }
+
+ protected override bool OnButtonPressEvent(EventButton evnt)
+ {
+ _mouseX = evnt.X;
+ _mouseY = evnt.Y;
+
+ if (evnt.Button == 1)
+ {
+ _mousePressed = true;
+ }
+
+ return false;
+ }
+
+ protected override bool OnButtonReleaseEvent(EventButton evnt)
+ {
+ if (evnt.Button == 1)
+ {
+ _mousePressed = false;
+ }
+
+ return false;
+ }
+
+ protected override bool OnMotionNotifyEvent(EventMotion evnt)
+ {
+ if (evnt.Device.InputSource == InputSource.Mouse)
+ {
+ _mouseX = evnt.X;
+ _mouseY = evnt.Y;
+ }
+
+ if (_hideCursorOnIdle)
+ {
+ _lastCursorMoveTime = Stopwatch.GetTimestamp();
+ }
+
+ return false;
+ }
+
+ protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight)
+ {
+ Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
+
+ // If the monitor is at least 1080p, use the Switch panel size as minimal size.
+ if (monitor.Geometry.Height >= 1080)
+ {
+ minimumHeight = SwitchPanelHeight;
+ }
+ // Otherwise, we default minimal size to 480p 16:9.
+ else
+ {
+ minimumHeight = 480;
+ }
+
+ naturalHeight = minimumHeight;
+ }
+
+ protected override void OnGetPreferredWidth(out int minimumWidth, out int naturalWidth)
+ {
+ Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
+
+ // If the monitor is at least 1080p, use the Switch panel size as minimal size.
+ if (monitor.Geometry.Height >= 1080)
+ {
+ minimumWidth = SwitchPanelWidth;
+ }
+ // Otherwise, we default minimal size to 480p 16:9.
+ else
+ {
+ minimumWidth = 854;
+ }
+
+ naturalWidth = minimumWidth;
+ }
+
+ protected override bool OnConfigureEvent(EventConfigure evnt)
+ {
+ bool result = base.OnConfigureEvent(evnt);
+
+ Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
+
+ _windowWidth = evnt.Width * monitor.ScaleFactor;
+ _windowHeight = evnt.Height * monitor.ScaleFactor;
+
+ Renderer?.Window.SetSize(_windowWidth, _windowHeight);
+
+ return result;
+ }
+
+ private void HandleScreenState(KeyboardStateSnapshot keyboard)
+ {
+ bool toggleFullscreen = keyboard.IsPressed(Key.F11)
+ || ((keyboard.IsPressed(Key.AltLeft)
+ || keyboard.IsPressed(Key.AltRight))
+ && keyboard.IsPressed(Key.Enter))
+ || keyboard.IsPressed(Key.Escape);
+
+ bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen);
+
+ if (toggleFullscreen != _toggleFullscreen)
+ {
+ if (toggleFullscreen)
+ {
+ if (fullScreenToggled)
+ {
+ ParentWindow.Unfullscreen();
+ (Toplevel as MainWindow)?.ToggleExtraWidgets(true);
+ }
+ else
+ {
+ if (keyboard.IsPressed(Key.Escape))
+ {
+ if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog())
+ {
+ Exit();
+ }
+ }
+ else
+ {
+ ParentWindow.Fullscreen();
+ (Toplevel as MainWindow)?.ToggleExtraWidgets(false);
+ }
+ }
+ }
+ }
+
+ _toggleFullscreen = toggleFullscreen;
+
+ bool toggleDockedMode = keyboard.IsPressed(Key.F9);
+
+ if (toggleDockedMode != _toggleDockedMode)
+ {
+ if (toggleDockedMode)
+ {
+ ConfigurationState.Instance.System.EnableDockedMode.Value =
+ !ConfigurationState.Instance.System.EnableDockedMode.Value;
+ }
+ }
+
+ _toggleDockedMode = toggleDockedMode;
+
+ if (_hideCursorOnIdle)
+ {
+ long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;
+ Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null;
+ }
+ }
+
+ public void Initialize(Switch device)
+ {
+ Device = device;
+ Renderer = Device.Gpu.Renderer;
+ Renderer?.Window.SetSize(_windowWidth, _windowHeight);
+
+ NpadManager.Initialize(device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard);
+ }
+
+ public void Render()
+ {
+ Gtk.Window parent = Toplevel as Gtk.Window;
+ parent.Present();
+
+ InitializeRenderer();
+
+ Device.Gpu.Renderer.Initialize(_glLogLevel);
+
+ _gpuVendorName = GetGpuVendorName();
+
+ Device.Gpu.InitializeShaderCache();
+ Translator.IsReadyForTranslation.Set();
+
+ while (_isActive)
+ {
+ if (_isStopped)
+ {
+ return;
+ }
+
+ _ticks += _chrono.ElapsedTicks;
+
+ _chrono.Restart();
+
+ if (Device.WaitFifo())
+ {
+ Device.Statistics.RecordFifoStart();
+ Device.ProcessFrame();
+ Device.Statistics.RecordFifoEnd();
+ }
+
+ while (Device.ConsumeFrameAvailable())
+ {
+ Device.PresentFrame(SwapBuffers);
+ }
+
+ if (_ticks >= _ticksPerFrame)
+ {
+ string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld";
+ float scale = Graphics.Gpu.GraphicsConfig.ResScale;
+ if (scale != 1)
+ {
+ dockedMode += $" ({scale}x)";
+ }
+
+ StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
+ Device.EnableDeviceVsync,
+ dockedMode,
+ ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
+ $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS",
+ $"FIFO: {Device.Statistics.GetFifoPercent():0.00} %",
+ $"GPU: {_gpuVendorName}"));
+
+ _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
+ }
+ }
+ }
+
+ public void Start()
+ {
+ _chrono.Restart();
+
+ _isActive = true;
+
+ Gtk.Window parent = this.Toplevel as Gtk.Window;
+
+ parent.FocusInEvent += Parent_FocusInEvent;
+ parent.FocusOutEvent += Parent_FocusOutEvent;
+
+ Application.Invoke(delegate
+ {
+ parent.Present();
+
+ string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty
+ : $" - {Device.Application.TitleName}";
+
+ string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty
+ : $" v{Device.Application.DisplayVersion}";
+
+ string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty
+ : $" ({Device.Application.TitleIdText.ToUpper()})";
+
+ string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)";
+
+ parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
+ });
+
+ Thread renderLoopThread = new Thread(Render)
+ {
+ Name = "GUI.RenderLoop"
+ };
+ renderLoopThread.Start();
+
+ Thread nvStutterWorkaround = new Thread(NVStutterWorkaround)
+ {
+ Name = "GUI.NVStutterWorkaround"
+ };
+ nvStutterWorkaround.Start();
+
+ MainLoop();
+
+ renderLoopThread.Join();
+ nvStutterWorkaround.Join();
+
+ Exit();
+ }
+
+ public void Exit()
+ {
+ NpadManager?.Dispose();
+
+ if (_isStopped)
+ {
+ return;
+ }
+
+ _isStopped = true;
+ _isActive = false;
+
+ _exitEvent.WaitOne();
+ _exitEvent.Dispose();
+ }
+
+ private void NVStutterWorkaround()
+ {
+ while (_isActive)
+ {
+ // When NVIDIA Threaded Optimization is on, the driver will snapshot all threads in the system whenever the application creates any new ones.
+ // The ThreadPool has something called a "GateThread" which terminates itself after some inactivity.
+ // However, it immediately starts up again, since the rules regarding when to terminate and when to start differ.
+ // This creates a new thread every second or so.
+ // The main problem with this is that the thread snapshot can take 70ms, is on the OpenGL thread and will delay rendering any graphics.
+ // This is a little over budget on a frame time of 16ms, so creates a large stutter.
+ // The solution is to keep the ThreadPool active so that it never has a reason to terminate the GateThread.
+
+ // TODO: This should be removed when the issue with the GateThread is resolved.
+
+ ThreadPool.QueueUserWorkItem((state) => { });
+ Thread.Sleep(300);
+ }
+ }
+
+ public void MainLoop()
+ {
+ while (_isActive)
+ {
+ UpdateFrame();
+
+ // Polling becomes expensive if it's not slept
+ Thread.Sleep(1);
+ }
+
+ _exitEvent.Set();
+ }
+
+ private bool UpdateFrame()
+ {
+ if (!_isActive)
+ {
+ return true;
+ }
+
+ if (_isStopped)
+ {
+ return false;
+ }
+
+ if (_isFocused)
+ {
+ Gtk.Application.Invoke(delegate
+ {
+ KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot();
+
+ HandleScreenState(keyboard);
+
+ if (keyboard.IsPressed(Key.Delete))
+ {
+ if (!ParentWindow.State.HasFlag(WindowState.Fullscreen))
+ {
+ Ptc.Continue();
+ }
+ }
+ });
+ }
+
+ NpadManager.Update();
+
+ if (_isFocused)
+ {
+ KeyboardHotkeyState currentHotkeyState = GetHotkeyState();
+
+ if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync) &&
+ !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync))
+ {
+ Device.EnableDeviceVsync = !Device.EnableDeviceVsync;
+ }
+
+ _prevHotkeyState = currentHotkeyState;
+ }
+
+ // Touchscreen
+ bool hasTouch = false;
+
+ // Get screen touch position from left mouse click
+ // OpenTK always captures mouse events, even if out of focus, so check if window is focused.
+ if (_isFocused && _mousePressed)
+ {
+ float aspectWidth = SwitchPanelHeight * ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat();
+
+ int screenWidth = AllocatedWidth;
+ int screenHeight = AllocatedHeight;
+
+ if (AllocatedWidth > AllocatedHeight * aspectWidth / SwitchPanelHeight)
+ {
+ screenWidth = (int)(AllocatedHeight * aspectWidth) / SwitchPanelHeight;
+ }
+ else
+ {
+ screenHeight = (AllocatedWidth * SwitchPanelHeight) / (int)aspectWidth;
+ }
+
+ int startX = (AllocatedWidth - screenWidth) >> 1;
+ int startY = (AllocatedHeight - screenHeight) >> 1;
+
+ int endX = startX + screenWidth;
+ int endY = startY + screenHeight;
+
+ if (_mouseX >= startX &&
+ _mouseY >= startY &&
+ _mouseX < endX &&
+ _mouseY < endY)
+ {
+ int screenMouseX = (int)_mouseX - startX;
+ int screenMouseY = (int)_mouseY - startY;
+
+ int mX = (screenMouseX * (int)aspectWidth) / screenWidth;
+ int mY = (screenMouseY * SwitchPanelHeight) / screenHeight;
+
+ TouchPoint currentPoint = new TouchPoint
+ {
+ X = (uint)mX,
+ Y = (uint)mY,
+
+ // Placeholder values till more data is acquired
+ DiameterX = 10,
+ DiameterY = 10,
+ Angle = 90
+ };
+
+ hasTouch = true;
+
+ Device.Hid.Touchscreen.Update(currentPoint);
+ }
+ }
+
+ if (!hasTouch)
+ {
+ Device.Hid.Touchscreen.Update();
+ }
+
+ Device.Hid.DebugPad.Update();
+
+ return true;
+ }
+
+
+ [Flags]
+ private enum KeyboardHotkeyState
+ {
+ None,
+ ToggleVSync
+ }
+
+ private KeyboardHotkeyState GetHotkeyState()
+ {
+ KeyboardHotkeyState state = KeyboardHotkeyState.None;
+
+ if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync))
+ {
+ state |= KeyboardHotkeyState.ToggleVSync;
+ }
+
+ return state;
+ }
+ }
+}
diff --git a/Ryujinx/Ui/SPBOpenGLContext.cs b/Ryujinx/Ui/SPBOpenGLContext.cs
index c2b5d638..e1a315c9 100644
--- a/Ryujinx/Ui/SPBOpenGLContext.cs
+++ b/Ryujinx/Ui/SPBOpenGLContext.cs
@@ -34,7 +34,7 @@ namespace Ryujinx.Ui
public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext)
{
OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext);
- NativeWindowBase window = PlatformHelper.CreateWindow(FramebufferFormat.Default, 0, 0, 100, 100);
+ NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100);
context.Initialize(window);
context.MakeCurrent(window);
diff --git a/Ryujinx/Ui/VKRenderer.cs b/Ryujinx/Ui/VKRenderer.cs
new file mode 100644
index 00000000..7b01f709
--- /dev/null
+++ b/Ryujinx/Ui/VKRenderer.cs
@@ -0,0 +1,80 @@
+using Gdk;
+using Gtk;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Input.HLE;
+using SPB.Graphics.Vulkan;
+using SPB.Platform.Win32;
+using SPB.Platform.X11;
+using SPB.Windowing;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Ui
+{
+ public class VKRenderer : RendererWidgetBase
+ {
+ public NativeWindowBase NativeWindow { get; private set; }
+
+ public VKRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { }
+
+ private NativeWindowBase RetrieveNativeWindow()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle);
+
+ return new SimpleWin32Window(new NativeHandle(windowHandle));
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle);
+ IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle);
+
+ return new SimpleX11Window(new NativeHandle(displayHandle), new NativeHandle(windowHandle));
+ }
+
+ throw new NotImplementedException();
+ }
+
+ [DllImport("libgdk-3-0.dll")]
+ private static extern IntPtr gdk_win32_window_get_handle(IntPtr d);
+
+ [DllImport("libgdk-3.so.0")]
+ private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay);
+
+ [DllImport("libgdk-3.so.0")]
+ private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow);
+
+ protected override bool OnConfigureEvent(EventConfigure evnt)
+ {
+ if (NativeWindow == null)
+ {
+ NativeWindow = RetrieveNativeWindow();
+
+ WaitEvent.Set();
+ }
+
+ return base.OnConfigureEvent(evnt);
+ }
+
+ public unsafe IntPtr CreateWindowSurface(IntPtr instance)
+ {
+ return VulkanHelper.CreateWindowSurface(instance, NativeWindow);
+ }
+
+ public override void InitializeRenderer() { }
+
+ public override void SwapBuffers() { }
+
+ public override string GetGpuVendorName()
+ {
+ return "Vulkan (Unknown)";
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Device.DisposeGpu();
+ NpadManager.Dispose();
+ }
+ }
+}
diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
index 4fdc666a..eb3150ce 100644
--- a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
+++ b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
@@ -115,7 +115,7 @@ namespace Ryujinx.Ui.Widgets
Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
}
- Uid user = new Uid(1, 0); // TODO: Remove Hardcoded value.
+ Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
result = EnsureApplicationSaveData(_virtualFileSystem.FsClient, out _, new LibHac.Ncm.ApplicationId(titleId), ref control, ref user);
diff --git a/Ryujinx/Ui/Widgets/GtkDialog.cs b/Ryujinx/Ui/Widgets/GtkDialog.cs
index 3d19724d..83f6bb2b 100644
--- a/Ryujinx/Ui/Widgets/GtkDialog.cs
+++ b/Ryujinx/Ui/Widgets/GtkDialog.cs
@@ -1,6 +1,7 @@
using Gtk;
using System.Reflection;
using Ryujinx.Common.Logging;
+using System.Collections.Generic;
namespace Ryujinx.Ui.Widgets
{
@@ -76,6 +77,34 @@ namespace Ryujinx.Ui.Widgets
return response == ResponseType.Yes;
}
+ internal static ResponseType CreateCustomDialog(string title, string mainText, string secondaryText, Dictionary<int, string> buttons, MessageType messageType = MessageType.Other)
+ {
+ GtkDialog gtkDialog = new GtkDialog(title, mainText, secondaryText, messageType, ButtonsType.None);
+
+ foreach (var button in buttons)
+ {
+ gtkDialog.AddButton(button.Value, button.Key);
+ }
+
+ return (ResponseType)gtkDialog.Run();
+ }
+
+ internal static string CreateInputDialog(Window parent, string title, string mainText, uint inputMax)
+ {
+ GtkInputDialog gtkDialog = new GtkInputDialog(parent, title, mainText, inputMax);
+ ResponseType response = (ResponseType)gtkDialog.Run();
+ string responseText = gtkDialog.InputEntry.Text.TrimEnd();
+
+ gtkDialog.Dispose();
+
+ if (response == ResponseType.Ok)
+ {
+ return responseText;
+ }
+
+ return "";
+ }
+
internal static bool CreateExitDialog()
{
return CreateChoiceDialog("Ryujinx - Exit", "Are you sure you want to close Ryujinx?", "All unsaved data will be lost!");
diff --git a/Ryujinx/Ui/Widgets/GtkInputDialog.cs b/Ryujinx/Ui/Widgets/GtkInputDialog.cs
new file mode 100644
index 00000000..21b34937
--- /dev/null
+++ b/Ryujinx/Ui/Widgets/GtkInputDialog.cs
@@ -0,0 +1,37 @@
+using Gtk;
+
+namespace Ryujinx.Ui.Widgets
+{
+ public class GtkInputDialog : MessageDialog
+ {
+ public Entry InputEntry { get; }
+
+ public GtkInputDialog(Window parent, string title, string mainText, uint inputMax) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.OkCancel, null)
+ {
+ SetDefaultSize(300, 0);
+
+ Title = title;
+
+ Label mainTextLabel = new Label
+ {
+ Text = mainText
+ };
+
+ InputEntry = new Entry
+ {
+ MaxLength = (int)inputMax
+ };
+
+ Label inputMaxTextLabel = new Label
+ {
+ Text = $"(Max length: {inputMax})"
+ };
+
+ ((Box)MessageArea).PackStart(mainTextLabel, true, true, 0);
+ ((Box)MessageArea).PackStart(InputEntry, true, true, 5);
+ ((Box)MessageArea).PackStart(inputMaxTextLabel, true, true, 0);
+
+ ShowAll();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx/Ui/Windows/AvatarWindow.cs b/Ryujinx/Ui/Windows/AvatarWindow.cs
new file mode 100644
index 00000000..52e03d30
--- /dev/null
+++ b/Ryujinx/Ui/Windows/AvatarWindow.cs
@@ -0,0 +1,289 @@
+using Gtk;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSystem;
+using LibHac.FsSystem.NcaUtils;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.FileSystem.Content;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+using Image = SixLabors.ImageSharp.Image;
+
+namespace Ryujinx.Ui.Windows
+{
+ public class AvatarWindow : Window
+ {
+ public byte[] SelectedProfileImage;
+ public bool NewUser;
+
+ private static Dictionary<string, byte[]> _avatarDict = new Dictionary<string, byte[]>();
+
+ private ListStore _listStore;
+ private IconView _iconView;
+ private Button _setBackgroungColorButton;
+ private Gdk.RGBA _backgroundColor;
+
+ public AvatarWindow() : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar")
+ {
+ Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png");
+
+ CanFocus = false;
+ Resizable = false;
+ Modal = true;
+ TypeHint = Gdk.WindowTypeHint.Dialog;
+
+ SetDefaultSize(740, 400);
+ SetPosition(WindowPosition.Center);
+
+ VBox vbox = new VBox(false, 0);
+ Add(vbox);
+
+ ScrolledWindow scrolledWindow = new ScrolledWindow
+ {
+ ShadowType = ShadowType.EtchedIn
+ };
+ scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic);
+
+ HBox hbox = new HBox(false, 0);
+
+ Button chooseButton = new Button()
+ {
+ Label = "Choose",
+ CanFocus = true,
+ ReceivesDefault = true
+ };
+ chooseButton.Clicked += ChooseButton_Pressed;
+
+ _setBackgroungColorButton = new Button()
+ {
+ Label = "Set Background Color",
+ CanFocus = true
+ };
+ _setBackgroungColorButton.Clicked += SetBackgroungColorButton_Pressed;
+
+ _backgroundColor.Red = 1;
+ _backgroundColor.Green = 1;
+ _backgroundColor.Blue = 1;
+ _backgroundColor.Alpha = 1;
+
+ Button closeButton = new Button()
+ {
+ Label = "Close",
+ CanFocus = true
+ };
+ closeButton.Clicked += CloseButton_Pressed;
+
+ vbox.PackStart(scrolledWindow, true, true, 0);
+ hbox.PackStart(chooseButton, true, true, 0);
+ hbox.PackStart(_setBackgroungColorButton, true, true, 0);
+ hbox.PackStart(closeButton, true, true, 0);
+ vbox.PackStart(hbox, false, false, 0);
+
+ _listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf));
+ _listStore.SetSortColumnId(0, SortType.Ascending);
+
+ _iconView = new IconView(_listStore);
+ _iconView.ItemWidth = 64;
+ _iconView.ItemPadding = 10;
+ _iconView.PixbufColumn = 1;
+
+ _iconView.SelectionChanged += IconView_SelectionChanged;
+
+ scrolledWindow.Add(_iconView);
+
+ _iconView.GrabFocus();
+
+ ProcessAvatars();
+
+ ShowAll();
+ }
+
+ public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem)
+ {
+ if (_avatarDict.Count > 0)
+ {
+ return;
+ }
+
+ string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.NandSystem, NcaContentType.Data);
+ string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
+
+ if (!string.IsNullOrWhiteSpace(avatarPath))
+ {
+ using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open))
+ {
+ Nca nca = new Nca(virtualFileSystem.KeySet, ncaFileStream);
+ IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
+
+ foreach (var item in romfs.EnumerateEntries())
+ {
+ // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
+
+ if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs"))
+ {
+ romfs.OpenFile(out IFile file, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+ using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream streamPng = new MemoryStream())
+ {
+ file.AsStream().CopyTo(stream);
+
+ stream.Position = 0;
+
+ Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
+
+ avatarImage.SaveAsPng(streamPng);
+
+ _avatarDict.Add(item.FullPath, streamPng.ToArray());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void ProcessAvatars()
+ {
+ _listStore.Clear();
+
+ foreach (var avatar in _avatarDict)
+ {
+ _listStore.AppendValues(avatar.Key, new Gdk.Pixbuf(ProcessImage(avatar.Value), 96, 96));
+ }
+
+ _iconView.SelectPath(new TreePath(new int[] { 0 }));
+ }
+
+ private byte[] ProcessImage(byte[] data)
+ {
+ using (MemoryStream streamJpg = new MemoryStream())
+ {
+ Image avatarImage = Image.Load(data, new PngDecoder());
+
+ avatarImage.Mutate(x => x.BackgroundColor(new Rgba32((byte)(_backgroundColor.Red * 255),
+ (byte)(_backgroundColor.Green * 255),
+ (byte)(_backgroundColor.Blue * 255),
+ (byte)(_backgroundColor.Alpha * 255))));
+ avatarImage.SaveAsJpeg(streamJpg);
+
+ return streamJpg.ToArray();
+ }
+ }
+
+ private void CloseButton_Pressed(object sender, EventArgs e)
+ {
+ SelectedProfileImage = null;
+
+ Close();
+ }
+
+ private void IconView_SelectionChanged(object sender, EventArgs e)
+ {
+ if (_iconView.SelectedItems.Length > 0)
+ {
+ _listStore.GetIter(out TreeIter iter, _iconView.SelectedItems[0]);
+
+ SelectedProfileImage = ProcessImage(_avatarDict[(string)_listStore.GetValue(iter, 0)]);
+ }
+ }
+
+ private void SetBackgroungColorButton_Pressed(object sender, EventArgs e)
+ {
+ using (ColorChooserDialog colorChooserDialog = new ColorChooserDialog("Set Background Color", this))
+ {
+ colorChooserDialog.UseAlpha = false;
+ colorChooserDialog.Rgba = _backgroundColor;
+
+ if (colorChooserDialog.Run() == (int)ResponseType.Ok)
+ {
+ _backgroundColor = colorChooserDialog.Rgba;
+
+ ProcessAvatars();
+ }
+
+ colorChooserDialog.Hide();
+ }
+ }
+
+ private void ChooseButton_Pressed(object sender, EventArgs e)
+ {
+ Close();
+ }
+
+ private static byte[] DecompressYaz0(Stream stream)
+ {
+ using (BinaryReader reader = new BinaryReader(stream))
+ {
+ reader.ReadInt32(); // Magic
+
+ uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
+
+ reader.ReadInt64(); // Padding
+
+ byte[] input = new byte[stream.Length - stream.Position];
+ stream.Read(input, 0, input.Length);
+
+ long inputOffset = 0;
+
+ byte[] output = new byte[decodedLength];
+ long outputOffset = 0;
+
+ ushort mask = 0;
+ byte header = 0;
+
+ while (outputOffset < decodedLength)
+ {
+ if ((mask >>= 1) == 0)
+ {
+ header = input[inputOffset++];
+ mask = 0x80;
+ }
+
+ if ((header & mask) > 0)
+ {
+ if (outputOffset == output.Length)
+ {
+ break;
+ }
+
+ output[outputOffset++] = input[inputOffset++];
+ }
+ else
+ {
+ byte byte1 = input[inputOffset++];
+ byte byte2 = input[inputOffset++];
+
+ int dist = ((byte1 & 0xF) << 8) | byte2;
+ int position = (int)outputOffset - (dist + 1);
+
+ int length = byte1 >> 4;
+ if (length == 0)
+ {
+ length = input[inputOffset++] + 0x12;
+ }
+ else
+ {
+ length += 2;
+ }
+
+ while (length-- > 0)
+ {
+ output[outputOffset++] = output[position++];
+ }
+ }
+ }
+
+ return output;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs
index 6e876ad9..0a3deec1 100644
--- a/Ryujinx/Ui/Windows/ControllerWindow.cs
+++ b/Ryujinx/Ui/Windows/ControllerWindow.cs
@@ -187,9 +187,9 @@ namespace Ryujinx.Ui.Windows
mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
- if (_mainWindow.GlRendererWidget != null)
+ if (_mainWindow.RendererWidget != null)
{
- _mainWindow.GlRendererWidget.NpadManager.BlockInputUpdates();
+ _mainWindow.RendererWidget.NpadManager.BlockInputUpdates();
}
}
@@ -219,9 +219,9 @@ namespace Ryujinx.Ui.Windows
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
- if (_mainWindow.GlRendererWidget != null)
+ if (_mainWindow.RendererWidget != null)
{
- _mainWindow.GlRendererWidget.NpadManager.UnblockInputUpdates();
+ _mainWindow.RendererWidget.NpadManager.UnblockInputUpdates();
}
_selectedGamepad?.Dispose();
@@ -1141,9 +1141,9 @@ namespace Ryujinx.Ui.Windows
}
}
- if (_mainWindow.GlRendererWidget != null)
+ if (_mainWindow.RendererWidget != null)
{
- _mainWindow.GlRendererWidget.NpadManager.ReloadConfiguration(newConfig);
+ _mainWindow.RendererWidget.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard);
}
// Atomically replace and signal input change.
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index be9dc271..43fea4e2 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -1,5 +1,6 @@
using Gtk;
using Ryujinx.Audio.Backends.OpenAL;
+using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
@@ -262,7 +263,7 @@ namespace Ryujinx.Ui.Windows
}
_systemTimeZoneEntry.WidthChars = Math.Max(20, maxLocationLength + 1); // Ensure minimum Entry width
- _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName();
+ _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone);
_systemTimeZoneCompletion.MatchFunc = TimeZoneMatchFunc;
@@ -302,6 +303,7 @@ namespace Ryujinx.Ui.Windows
TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl);
TreeIter soundIoIter = _audioBackendStore.AppendValues("SoundIO", AudioBackend.SoundIo);
+ TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2);
TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy);
_audioBackendSelect = ComboBox.NewWithModelAndEntry(_audioBackendStore);
@@ -316,6 +318,9 @@ namespace Ryujinx.Ui.Windows
case AudioBackend.SoundIo:
_audioBackendSelect.SetActiveIter(soundIoIter);
break;
+ case AudioBackend.SDL2:
+ _audioBackendSelect.SetActiveIter(sdl2Iter);
+ break;
case AudioBackend.Dummy:
_audioBackendSelect.SetActiveIter(dummyIter);
break;
@@ -328,11 +333,13 @@ namespace Ryujinx.Ui.Windows
bool openAlIsSupported = false;
bool soundIoIsSupported = false;
+ bool sdl2IsSupported = false;
Task.Run(() =>
{
openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported;
soundIoIsSupported = SoundIoHardwareDeviceDriver.IsSupported;
+ sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported;
});
// This function runs whenever the dropdown is opened
@@ -342,6 +349,7 @@ namespace Ryujinx.Ui.Windows
{
AudioBackend.OpenAl => openAlIsSupported,
AudioBackend.SoundIo => soundIoIsSupported,
+ AudioBackend.SDL2 => sdl2IsSupported,
AudioBackend.Dummy => true,
_ => throw new ArgumentOutOfRangeException()
};
@@ -454,7 +462,7 @@ namespace Ryujinx.Ui.Windows
{
if (!_validTzRegions.Contains(_systemTimeZoneEntry.Text))
{
- _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName();
+ _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone);
}
}
diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs
new file mode 100644
index 00000000..70291290
--- /dev/null
+++ b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs
@@ -0,0 +1,255 @@
+using Gtk;
+using Pango;
+
+namespace Ryujinx.Ui.Windows
+{
+ public partial class UserProfilesManagerWindow : Window
+ {
+ private Box _mainBox;
+ private Label _selectedLabel;
+ private Box _selectedUserBox;
+ private Image _selectedUserImage;
+ private VBox _selectedUserInfoBox;
+ private Entry _selectedUserNameEntry;
+ private Label _selectedUserIdLabel;
+ private VBox _selectedUserButtonsBox;
+ private Button _saveProfileNameButton;
+ private Button _changeProfileImageButton;
+ private Box _usersTreeViewBox;
+ private Label _availableUsersLabel;
+ private ScrolledWindow _usersTreeViewWindow;
+ private ListStore _tableStore;
+ private TreeView _usersTreeView;
+ private Box _bottomBox;
+ private Button _addButton;
+ private Button _deleteButton;
+ private Button _closeButton;
+
+ private void InitializeComponent()
+ {
+
+#pragma warning disable CS0612
+
+ //
+ // UserProfilesManagerWindow
+ //
+ CanFocus = false;
+ Resizable = false;
+ Modal = true;
+ WindowPosition = WindowPosition.Center;
+ DefaultWidth = 620;
+ DefaultHeight = 548;
+ TypeHint = Gdk.WindowTypeHint.Dialog;
+
+ //
+ // _mainBox
+ //
+ _mainBox = new Box(Orientation.Vertical, 0);
+
+ //
+ // _selectedLabel
+ //
+ _selectedLabel = new Label("Selected User Profile:")
+ {
+ Margin = 15,
+ Attributes = new AttrList()
+ };
+ _selectedLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
+
+ //
+ // _viewBox
+ //
+ _usersTreeViewBox = new Box(Orientation.Vertical, 0);
+
+ //
+ // _SelectedUserBox
+ //
+ _selectedUserBox = new Box(Orientation.Horizontal, 0)
+ {
+ MarginLeft = 30
+ };
+
+ //
+ // _selectedUserImage
+ //
+ _selectedUserImage = new Image();
+
+ //
+ // _selectedUserInfoBox
+ //
+ _selectedUserInfoBox = new VBox(true, 0);
+
+ //
+ // _selectedUserNameEntry
+ //
+ _selectedUserNameEntry = new Entry("")
+ {
+ MarginLeft = 15,
+ MaxLength = (int)MaxProfileNameLength
+ };
+ _selectedUserNameEntry.KeyReleaseEvent += SelectedUserNameEntry_KeyReleaseEvent;
+
+ //
+ // _selectedUserIdLabel
+ //
+ _selectedUserIdLabel = new Label("")
+ {
+ MarginTop = 15,
+ MarginLeft = 15
+ };
+
+ //
+ // _selectedUserButtonsBox
+ //
+ _selectedUserButtonsBox = new VBox()
+ {
+ MarginRight = 30
+ };
+
+ //
+ // _saveProfileNameButton
+ //
+ _saveProfileNameButton = new Button()
+ {
+ Label = "Save Profile Name",
+ CanFocus = true,
+ ReceivesDefault = true,
+ Sensitive = false
+ };
+ _saveProfileNameButton.Clicked += EditProfileNameButton_Pressed;
+
+ //
+ // _changeProfileImageButton
+ //
+ _changeProfileImageButton = new Button()
+ {
+ Label = "Change Profile Image",
+ CanFocus = true,
+ ReceivesDefault = true,
+ MarginTop = 10
+ };
+ _changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed;
+
+ //
+ // _availableUsersLabel
+ //
+ _availableUsersLabel = new Label("Available User Profiles:")
+ {
+ Margin = 15,
+ Attributes = new AttrList()
+ };
+ _availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
+
+ //
+ // _usersTreeViewWindow
+ //
+ _usersTreeViewWindow = new ScrolledWindow()
+ {
+ ShadowType = ShadowType.In,
+ CanFocus = true,
+ Expand = true,
+ MarginLeft = 30,
+ MarginRight = 30,
+ MarginBottom = 15
+ };
+
+ //
+ // _tableStore
+ //
+ _tableStore = new ListStore(typeof(bool), typeof(Gdk.Pixbuf), typeof(string), typeof(Gdk.RGBA));
+
+ //
+ // _usersTreeView
+ //
+ _usersTreeView = new TreeView(_tableStore)
+ {
+ HoverSelection = true,
+ HeadersVisible = false,
+ };
+ _usersTreeView.RowActivated += UsersTreeView_Activated;
+
+ //
+ // _bottomBox
+ //
+ _bottomBox = new Box(Orientation.Horizontal, 0)
+ {
+ MarginLeft = 30,
+ MarginRight = 30,
+ MarginBottom = 15
+ };
+
+ //
+ // _addButton
+ //
+ _addButton = new Button()
+ {
+ Label = "Add New Profile",
+ CanFocus = true,
+ ReceivesDefault = true,
+ HeightRequest = 35
+ };
+ _addButton.Clicked += AddButton_Pressed;
+
+ //
+ // _deleteButton
+ //
+ _deleteButton = new Button()
+ {
+ Label = "Delete Selected Profile",
+ CanFocus = true,
+ ReceivesDefault = true,
+ HeightRequest = 35,
+ MarginLeft = 10
+ };
+ _deleteButton.Clicked += DeleteButton_Pressed;
+
+ //
+ // _closeButton
+ //
+ _closeButton = new Button()
+ {
+ Label = "Close",
+ CanFocus = true,
+ ReceivesDefault = true,
+ HeightRequest = 35,
+ WidthRequest = 80
+ };
+ _closeButton.Clicked += CloseButton_Pressed;
+
+#pragma warning restore CS0612
+
+ ShowComponent();
+ }
+
+ private void ShowComponent()
+ {
+ _usersTreeViewWindow.Add(_usersTreeView);
+
+ _usersTreeViewBox.Add(_usersTreeViewWindow);
+
+ _bottomBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _addButton }, false, false, 0);
+ _bottomBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _deleteButton }, false, false, 0);
+ _bottomBox.PackEnd(new Gtk.Alignment(1, 0, 0, 0) { _closeButton }, false, false, 0);
+
+ _selectedUserInfoBox.Add(_selectedUserNameEntry);
+ _selectedUserInfoBox.Add(_selectedUserIdLabel);
+
+ _selectedUserButtonsBox.Add(_saveProfileNameButton);
+ _selectedUserButtonsBox.Add(_changeProfileImageButton);
+
+ _selectedUserBox.Add(_selectedUserImage);
+ _selectedUserBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _selectedUserInfoBox }, true, true, 0);
+ _selectedUserBox.Add(_selectedUserButtonsBox);
+
+ _mainBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _selectedLabel }, false, false, 0);
+ _mainBox.PackStart(_selectedUserBox, false, true, 0);
+ _mainBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _availableUsersLabel }, false, false, 0);
+ _mainBox.Add(_usersTreeViewBox);
+ _mainBox.Add(_bottomBox);
+
+ Add(_mainBox);
+
+ ShowAll();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
new file mode 100644
index 00000000..6a4788b1
--- /dev/null
+++ b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
@@ -0,0 +1,327 @@
+using Gtk;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.FileSystem.Content;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.Ui.Widgets;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Processing;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Image = SixLabors.ImageSharp.Image;
+using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
+
+namespace Ryujinx.Ui.Windows
+{
+ public partial class UserProfilesManagerWindow : Window
+ {
+ private const uint MaxProfileNameLength = 0x20;
+
+ private readonly AccountManager _accountManager;
+ private readonly ContentManager _contentManager;
+
+ private byte[] _bufferImageProfile;
+ private string _tempNewProfileName;
+
+ private Gdk.RGBA _selectedColor;
+
+ private ManualResetEvent _avatarsPreloadingEvent = new ManualResetEvent(false);
+
+ public UserProfilesManagerWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles")
+ {
+ Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png");
+
+ InitializeComponent();
+
+ _selectedColor.Red = 0.212;
+ _selectedColor.Green = 0.843;
+ _selectedColor.Blue = 0.718;
+ _selectedColor.Alpha = 1;
+
+ _accountManager = accountManager;
+ _contentManager = contentManager;
+
+ CellRendererToggle userSelectedToggle = new CellRendererToggle();
+ userSelectedToggle.Toggled += UserSelectedToggle_Toggled;
+
+ // NOTE: Uncomment following line when multiple selection of user profiles is supported.
+ //_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0);
+ _usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1);
+ _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3);
+
+ _tableStore.SetSortColumnId(0, SortType.Descending);
+
+ RefreshList();
+
+ if (_contentManager.GetCurrentFirmwareVersion() != null)
+ {
+ Task.Run(() =>
+ {
+ AvatarWindow.PreloadAvatars(contentManager, virtualFileSystem);
+ _avatarsPreloadingEvent.Set();
+ });
+ }
+ }
+
+ public void RefreshList()
+ {
+ _tableStore.Clear();
+
+ foreach (UserProfile userProfile in _accountManager.GetAllUsers())
+ {
+ _tableStore.AppendValues(userProfile.AccountState == AccountState.Open, new Gdk.Pixbuf(userProfile.Image, 96, 96), $"{userProfile.Name}\n{userProfile.UserId}", Gdk.RGBA.Zero);
+
+ if (userProfile.AccountState == AccountState.Open)
+ {
+ _selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96);
+ _selectedUserIdLabel.Text = userProfile.UserId.ToString();
+ _selectedUserNameEntry.Text = userProfile.Name;
+
+ _deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId;
+
+ _usersTreeView.Model.GetIterFirst(out TreeIter firstIter);
+ _tableStore.SetValue(firstIter, 3, _selectedColor);
+ }
+ }
+ }
+
+ //
+ // Events
+ //
+
+ private void UsersTreeView_Activated(object o, RowActivatedArgs args)
+ {
+ SelectUserTreeView();
+ }
+
+ private void UserSelectedToggle_Toggled(object o, ToggledArgs args)
+ {
+ SelectUserTreeView();
+ }
+
+ private void SelectUserTreeView()
+ {
+ // Get selected item informations.
+ _usersTreeView.Selection.GetSelected(out TreeIter selectedIter);
+
+ Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1);
+
+ string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0];
+ string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1];
+
+ // Unselect the first user.
+ _usersTreeView.Model.GetIterFirst(out TreeIter firstIter);
+ _tableStore.SetValue(firstIter, 0, false);
+ _tableStore.SetValue(firstIter, 3, Gdk.RGBA.Zero);
+
+ // Set new informations.
+ _tableStore.SetValue(selectedIter, 0, true);
+
+ _selectedUserImage.Pixbuf = userPicture;
+ _selectedUserNameEntry.Text = userName;
+ _selectedUserIdLabel.Text = userId;
+ _saveProfileNameButton.Sensitive = false;
+
+ // Open the selected one.
+ _accountManager.OpenUser(new UserId(userId));
+
+ _deleteButton.Sensitive = userId != AccountManager.DefaultUserId.ToString();
+
+ _tableStore.SetValue(selectedIter, 3, _selectedColor);
+ }
+
+ private void SelectedUserNameEntry_KeyReleaseEvent(object o, KeyReleaseEventArgs args)
+ {
+ if (_saveProfileNameButton.Sensitive == false)
+ {
+ _saveProfileNameButton.Sensitive = true;
+ }
+ }
+
+ private void AddButton_Pressed(object sender, EventArgs e)
+ {
+ _tempNewProfileName = GtkDialog.CreateInputDialog(this, "Choose the Profile Name", "Please Enter a Profile Name", MaxProfileNameLength);
+
+ if (_tempNewProfileName != "")
+ {
+ SelectProfileImage(true);
+
+ if (_bufferImageProfile != null)
+ {
+ AddUser();
+ }
+ }
+ }
+
+ private void DeleteButton_Pressed(object sender, EventArgs e)
+ {
+ if (GtkDialog.CreateChoiceDialog("Delete User Profile", "Are you sure you want to delete the profile ?", "Deleting this profile will also delete all associated save data."))
+ {
+ _accountManager.DeleteUser(GetSelectedUserId());
+
+ RefreshList();
+ }
+ }
+
+ private void EditProfileNameButton_Pressed(object sender, EventArgs e)
+ {
+ _saveProfileNameButton.Sensitive = false;
+
+ _accountManager.SetUserName(GetSelectedUserId(), _selectedUserNameEntry.Text);
+
+ RefreshList();
+ }
+
+ private void ProcessProfileImage(byte[] buffer)
+ {
+ using (Image image = Image.Load(buffer))
+ {
+ image.Mutate(x => x.Resize(256, 256));
+
+ using (MemoryStream streamJpg = new MemoryStream())
+ {
+ image.SaveAsJpeg(streamJpg);
+
+ _bufferImageProfile = streamJpg.ToArray();
+ }
+ }
+ }
+
+ private void ProfileImageFileChooser()
+ {
+ FileChooserDialog fileChooser = new FileChooserDialog("Import Custom Profile Image", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Import", ResponseType.Accept)
+ {
+ SelectMultiple = false,
+ Filter = new FileFilter()
+ };
+
+ fileChooser.SetPosition(WindowPosition.Center);
+ fileChooser.Filter.AddPattern("*.jpg");
+ fileChooser.Filter.AddPattern("*.jpeg");
+ fileChooser.Filter.AddPattern("*.png");
+ fileChooser.Filter.AddPattern("*.bmp");
+
+ if (fileChooser.Run() == (int)ResponseType.Accept)
+ {
+ ProcessProfileImage(File.ReadAllBytes(fileChooser.Filename));
+ }
+
+ fileChooser.Dispose();
+ }
+
+ private void SelectProfileImage(bool newUser = false)
+ {
+ if (_contentManager.GetCurrentFirmwareVersion() == null)
+ {
+ ProfileImageFileChooser();
+ }
+ else
+ {
+ Dictionary<int, string> buttons = new Dictionary<int, string>()
+ {
+ { 0, "Import Image File" },
+ { 1, "Select Firmware Avatar" }
+ };
+
+ ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection",
+ "Choose a Profile Image",
+ "You may import a custom profile image, or select an avatar from the system firmware.",
+ buttons, MessageType.Question);
+
+ if (responseDialog == 0)
+ {
+ ProfileImageFileChooser();
+ }
+ else if (responseDialog == (ResponseType)1)
+ {
+ AvatarWindow avatarWindow = new AvatarWindow()
+ {
+ NewUser = newUser
+ };
+
+ avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent;
+
+ avatarWindow.SetSizeRequest((int)(avatarWindow.DefaultWidth * Program.WindowScaleFactor), (int)(avatarWindow.DefaultHeight * Program.WindowScaleFactor));
+ avatarWindow.Show();
+ }
+ }
+ }
+
+ private void ChangeProfileImageButton_Pressed(object sender, EventArgs e)
+ {
+ if (_contentManager.GetCurrentFirmwareVersion() != null)
+ {
+ _avatarsPreloadingEvent.WaitOne();
+ }
+
+ SelectProfileImage();
+
+ if (_bufferImageProfile != null)
+ {
+ SetUserImage();
+ }
+ }
+
+ private void AvatarWindow_DeleteEvent(object sender, DeleteEventArgs args)
+ {
+ _bufferImageProfile = ((AvatarWindow)sender).SelectedProfileImage;
+
+ if (_bufferImageProfile != null)
+ {
+ if (((AvatarWindow)sender).NewUser)
+ {
+ AddUser();
+ }
+ else
+ {
+ SetUserImage();
+ }
+ }
+ }
+
+ private void AddUser()
+ {
+ _accountManager.AddUser(_tempNewProfileName, _bufferImageProfile);
+
+ _bufferImageProfile = null;
+ _tempNewProfileName = "";
+
+ RefreshList();
+ }
+
+ private void SetUserImage()
+ {
+ _accountManager.SetUserImage(GetSelectedUserId(), _bufferImageProfile);
+
+ _bufferImageProfile = null;
+
+ RefreshList();
+ }
+
+ private UserId GetSelectedUserId()
+ {
+ if (_usersTreeView.Model.GetIterFirst(out TreeIter iter))
+ {
+ do
+ {
+ if ((bool)_tableStore.GetValue(iter, 0))
+ {
+ break;
+ }
+ }
+ while (_usersTreeView.Model.IterNext(ref iter));
+ }
+
+ return new UserId(_tableStore.GetValue(iter, 2).ToString().Split("\n")[1]);
+ }
+
+ private void CloseButton_Pressed(object sender, EventArgs e)
+ {
+ Close();
+ }
+ }
+} \ No newline at end of file