Move solution and projects to src
This commit is contained in:
556
src/Ryujinx.HLE/HOS/Horizon.cs
Normal file
556
src/Ryujinx.HLE/HOS/Horizon.cs
Normal file
@ -0,0 +1,556 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Audio.Input;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Audio.Output;
|
||||
using Ryujinx.Audio.Renderer.Device;
|
||||
using Ryujinx.Audio.Renderer.Server;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
|
||||
using Ryujinx.HLE.HOS.Services.Apm;
|
||||
using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer;
|
||||
using Ryujinx.HLE.HOS.Services.Caps;
|
||||
using Ryujinx.HLE.HOS.Services.Mii;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||
using Ryujinx.HLE.HOS.Services.Nv;
|
||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
||||
using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
|
||||
using Ryujinx.HLE.HOS.Services.Sdb.Pl;
|
||||
using Ryujinx.HLE.HOS.Services.Settings;
|
||||
using Ryujinx.HLE.HOS.Services.Sm;
|
||||
using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
|
||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.Horizon;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using TimeSpanType = Ryujinx.HLE.HOS.Services.Time.Clock.TimeSpanType;
|
||||
|
||||
namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
using TimeServiceManager = Services.Time.TimeManager;
|
||||
|
||||
public class Horizon : IDisposable
|
||||
{
|
||||
internal const int HidSize = 0x40000;
|
||||
internal const int FontSize = 0x1100000;
|
||||
internal const int IirsSize = 0x8000;
|
||||
internal const int TimeSize = 0x1000;
|
||||
internal const int AppletCaptureBufferSize = 0x384000;
|
||||
|
||||
internal KernelContext KernelContext { get; }
|
||||
|
||||
internal Switch Device { get; private set; }
|
||||
|
||||
internal ITickSource TickSource { get; }
|
||||
|
||||
internal SurfaceFlinger SurfaceFlinger { get; private set; }
|
||||
internal AudioManager AudioManager { get; private set; }
|
||||
internal AudioOutputManager AudioOutputManager { get; private set; }
|
||||
internal AudioInputManager AudioInputManager { get; private set; }
|
||||
internal AudioRendererManager AudioRendererManager { get; private set; }
|
||||
internal VirtualDeviceSessionRegistry AudioDeviceSessionRegistry { get; private set; }
|
||||
|
||||
public SystemStateMgr State { get; private set; }
|
||||
|
||||
internal PerformanceState PerformanceState { get; private set; }
|
||||
|
||||
internal AppletStateMgr AppletState { get; private set; }
|
||||
|
||||
internal List<NfpDevice> NfpDevices { get; private set; }
|
||||
|
||||
internal SmRegistry SmRegistry { get; private set; }
|
||||
|
||||
internal ServerBase SmServer { get; private set; }
|
||||
internal ServerBase BsdServer { get; private set; }
|
||||
internal ServerBase AudRenServer { get; private set; }
|
||||
internal ServerBase AudOutServer { get; private set; }
|
||||
internal ServerBase FsServer { get; private set; }
|
||||
internal ServerBase HidServer { get; private set; }
|
||||
internal ServerBase NvDrvServer { get; private set; }
|
||||
internal ServerBase TimeServer { get; private set; }
|
||||
internal ServerBase ViServer { get; private set; }
|
||||
internal ServerBase ViServerM { get; private set; }
|
||||
internal ServerBase ViServerS { get; private set; }
|
||||
|
||||
internal KSharedMemory HidSharedMem { get; private set; }
|
||||
internal KSharedMemory FontSharedMem { get; private set; }
|
||||
internal KSharedMemory IirsSharedMem { get; private set; }
|
||||
|
||||
internal KTransferMemory AppletCaptureBufferTransfer { get; private set; }
|
||||
|
||||
internal SharedFontManager SharedFontManager { get; private set; }
|
||||
internal AccountManager AccountManager { get; private set; }
|
||||
internal ContentManager ContentManager { get; private set; }
|
||||
internal CaptureManager CaptureManager { get; private set; }
|
||||
|
||||
internal KEvent VsyncEvent { get; private set; }
|
||||
|
||||
internal KEvent DisplayResolutionChangeEvent { get; private set; }
|
||||
|
||||
public KeySet KeySet => Device.FileSystem.KeySet;
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
public bool EnablePtc { get; set; }
|
||||
|
||||
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
|
||||
|
||||
public int GlobalAccessLogMode { get; set; }
|
||||
|
||||
internal SharedMemoryStorage HidStorage { get; private set; }
|
||||
|
||||
internal NvHostSyncpt HostSyncpoint { get; private set; }
|
||||
|
||||
internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
||||
|
||||
internal ServiceTable ServiceTable { get; private set; }
|
||||
|
||||
public bool IsPaused { get; private set; }
|
||||
|
||||
public Horizon(Switch device)
|
||||
{
|
||||
TickSource = new TickSource(KernelConstants.CounterFrequency);
|
||||
|
||||
KernelContext = new KernelContext(
|
||||
TickSource,
|
||||
device,
|
||||
device.Memory,
|
||||
device.Configuration.MemoryConfiguration.ToKernelMemorySize(),
|
||||
device.Configuration.MemoryConfiguration.ToKernelMemoryArrange());
|
||||
|
||||
Device = device;
|
||||
|
||||
State = new SystemStateMgr();
|
||||
|
||||
PerformanceState = new PerformanceState();
|
||||
|
||||
NfpDevices = new List<NfpDevice>();
|
||||
|
||||
// Note: This is not really correct, but with HLE of services, the only memory
|
||||
// region used that is used is Application, so we can use the other ones for anything.
|
||||
KMemoryRegionManager region = KernelContext.MemoryManager.MemoryRegions[(int)MemoryRegion.NvServices];
|
||||
|
||||
ulong hidPa = region.Address;
|
||||
ulong fontPa = region.Address + HidSize;
|
||||
ulong iirsPa = region.Address + HidSize + FontSize;
|
||||
ulong timePa = region.Address + HidSize + FontSize + IirsSize;
|
||||
ulong appletCaptureBufferPa = region.Address + HidSize + FontSize + IirsSize + TimeSize;
|
||||
|
||||
KPageList hidPageList = new KPageList();
|
||||
KPageList fontPageList = new KPageList();
|
||||
KPageList iirsPageList = new KPageList();
|
||||
KPageList timePageList = new KPageList();
|
||||
KPageList appletCaptureBufferPageList = new KPageList();
|
||||
|
||||
hidPageList.AddRange(hidPa, HidSize / KPageTableBase.PageSize);
|
||||
fontPageList.AddRange(fontPa, FontSize / KPageTableBase.PageSize);
|
||||
iirsPageList.AddRange(iirsPa, IirsSize / KPageTableBase.PageSize);
|
||||
timePageList.AddRange(timePa, TimeSize / KPageTableBase.PageSize);
|
||||
appletCaptureBufferPageList.AddRange(appletCaptureBufferPa, AppletCaptureBufferSize / KPageTableBase.PageSize);
|
||||
|
||||
var hidStorage = new SharedMemoryStorage(KernelContext, hidPageList);
|
||||
var fontStorage = new SharedMemoryStorage(KernelContext, fontPageList);
|
||||
var iirsStorage = new SharedMemoryStorage(KernelContext, iirsPageList);
|
||||
var timeStorage = new SharedMemoryStorage(KernelContext, timePageList);
|
||||
var appletCaptureBufferStorage = new SharedMemoryStorage(KernelContext, appletCaptureBufferPageList);
|
||||
|
||||
HidStorage = hidStorage;
|
||||
|
||||
HidSharedMem = new KSharedMemory(KernelContext, hidStorage, 0, 0, KMemoryPermission.Read);
|
||||
FontSharedMem = new KSharedMemory(KernelContext, fontStorage, 0, 0, KMemoryPermission.Read);
|
||||
IirsSharedMem = new KSharedMemory(KernelContext, iirsStorage, 0, 0, KMemoryPermission.Read);
|
||||
|
||||
KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timeStorage, 0, 0, KMemoryPermission.Read);
|
||||
|
||||
TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timeStorage, TimeSize);
|
||||
|
||||
AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage);
|
||||
|
||||
AppletState = new AppletStateMgr(this);
|
||||
|
||||
AppletState.SetFocus(true);
|
||||
|
||||
VsyncEvent = new KEvent(KernelContext);
|
||||
|
||||
DisplayResolutionChangeEvent = new KEvent(KernelContext);
|
||||
|
||||
SharedFontManager = new SharedFontManager(device, fontStorage);
|
||||
AccountManager = device.Configuration.AccountManager;
|
||||
ContentManager = device.Configuration.ContentManager;
|
||||
CaptureManager = new CaptureManager(device);
|
||||
|
||||
LibHacHorizonManager = device.Configuration.LibHacHorizonManager;
|
||||
|
||||
// TODO: use set:sys (and get external clock source id from settings)
|
||||
// TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate.
|
||||
UInt128 clockSourceId = UInt128Utils.CreateRandom();
|
||||
IRtcManager.GetExternalRtcValue(out ulong rtcValue);
|
||||
|
||||
// We assume the rtc is system time.
|
||||
TimeSpanType systemTime = TimeSpanType.FromSeconds((long)rtcValue);
|
||||
|
||||
// Configure and setup internal offset
|
||||
TimeSpanType internalOffset = TimeSpanType.FromSeconds(device.Configuration.SystemTimeOffset);
|
||||
|
||||
TimeSpanType systemTimeOffset = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds);
|
||||
|
||||
if (systemTime.IsDaylightSavingTime() && !systemTimeOffset.IsDaylightSavingTime())
|
||||
{
|
||||
internalOffset = internalOffset.AddSeconds(3600L);
|
||||
}
|
||||
else if (!systemTime.IsDaylightSavingTime() && systemTimeOffset.IsDaylightSavingTime())
|
||||
{
|
||||
internalOffset = internalOffset.AddSeconds(-3600L);
|
||||
}
|
||||
|
||||
internalOffset = new TimeSpanType(-internalOffset.NanoSeconds);
|
||||
|
||||
// First init the standard steady clock
|
||||
TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
|
||||
TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds());
|
||||
|
||||
if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
|
||||
{
|
||||
TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
|
||||
|
||||
// The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
|
||||
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
|
||||
TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
|
||||
}
|
||||
|
||||
TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom());
|
||||
|
||||
// FIXME: TimeZone should be init here but it's actually done in ContentManager
|
||||
|
||||
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
||||
|
||||
DatabaseImpl.Instance.InitializeDatabase(TickSource, LibHacHorizonManager.SdbClient);
|
||||
|
||||
HostSyncpoint = new NvHostSyncpt(device);
|
||||
|
||||
SurfaceFlinger = new SurfaceFlinger(device);
|
||||
|
||||
InitializeAudioRenderer(TickSource);
|
||||
InitializeServices();
|
||||
}
|
||||
|
||||
private void InitializeAudioRenderer(ITickSource tickSource)
|
||||
{
|
||||
AudioManager = new AudioManager();
|
||||
AudioOutputManager = new AudioOutputManager();
|
||||
AudioInputManager = new AudioInputManager();
|
||||
AudioRendererManager = new AudioRendererManager(tickSource);
|
||||
AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
|
||||
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
|
||||
|
||||
IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];
|
||||
|
||||
for (int i = 0; i < audioOutputRegisterBufferEvents.Length; i++)
|
||||
{
|
||||
KEvent registerBufferEvent = new KEvent(KernelContext);
|
||||
|
||||
audioOutputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
|
||||
}
|
||||
|
||||
AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents);
|
||||
AudioOutputManager.SetVolume(Device.Configuration.AudioVolume);
|
||||
|
||||
IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax];
|
||||
|
||||
for (int i = 0; i < audioInputRegisterBufferEvents.Length; i++)
|
||||
{
|
||||
KEvent registerBufferEvent = new KEvent(KernelContext);
|
||||
|
||||
audioInputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
|
||||
}
|
||||
|
||||
AudioInputManager.Initialize(Device.AudioDeviceDriver, audioInputRegisterBufferEvents);
|
||||
|
||||
IWritableEvent[] systemEvents = new IWritableEvent[Constants.AudioRendererSessionCountMax];
|
||||
|
||||
for (int i = 0; i < systemEvents.Length; i++)
|
||||
{
|
||||
KEvent systemEvent = new KEvent(KernelContext);
|
||||
|
||||
systemEvents[i] = new AudioKernelEvent(systemEvent);
|
||||
}
|
||||
|
||||
AudioManager.Initialize(Device.AudioDeviceDriver.GetUpdateRequiredEvent(), AudioOutputManager.Update, AudioInputManager.Update);
|
||||
|
||||
AudioRendererManager.Initialize(systemEvents, Device.AudioDeviceDriver);
|
||||
|
||||
AudioManager.Start();
|
||||
}
|
||||
|
||||
private void InitializeServices()
|
||||
{
|
||||
SmRegistry = new SmRegistry();
|
||||
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry));
|
||||
|
||||
// Wait until SM server thread is done with initialization,
|
||||
// only then doing connections to SM is safe.
|
||||
SmServer.InitDone.WaitOne();
|
||||
|
||||
BsdServer = new ServerBase(KernelContext, "BsdServer");
|
||||
AudRenServer = new ServerBase(KernelContext, "AudioRendererServer");
|
||||
AudOutServer = new ServerBase(KernelContext, "AudioOutServer");
|
||||
FsServer = new ServerBase(KernelContext, "FsServer");
|
||||
HidServer = new ServerBase(KernelContext, "HidServer");
|
||||
NvDrvServer = new ServerBase(KernelContext, "NvservicesServer");
|
||||
TimeServer = new ServerBase(KernelContext, "TimeServer");
|
||||
ViServer = new ServerBase(KernelContext, "ViServerU");
|
||||
ViServerM = new ServerBase(KernelContext, "ViServerM");
|
||||
ViServerS = new ServerBase(KernelContext, "ViServerS");
|
||||
|
||||
StartNewServices();
|
||||
}
|
||||
|
||||
private void StartNewServices()
|
||||
{
|
||||
ServiceTable = new ServiceTable();
|
||||
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices));
|
||||
|
||||
foreach (var service in services)
|
||||
{
|
||||
const ProcessCreationFlags flags =
|
||||
ProcessCreationFlags.EnableAslr |
|
||||
ProcessCreationFlags.AddressSpace64Bit |
|
||||
ProcessCreationFlags.Is64Bit |
|
||||
ProcessCreationFlags.PoolPartitionSystem;
|
||||
|
||||
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
||||
|
||||
uint[] defaultCapabilities = new uint[]
|
||||
{
|
||||
0x030363F7,
|
||||
0x1FFFFFCF,
|
||||
0x207FFFEF,
|
||||
0x47E0060F,
|
||||
0x0048BFFF,
|
||||
0x01007FFF
|
||||
};
|
||||
|
||||
// TODO:
|
||||
// - Pass enough information (capabilities, process creation info, etc) on ServiceEntry for proper initialization.
|
||||
// - Have the ThreadStart function take the syscall, address space and thread context parameters instead of passing them here.
|
||||
KernelStatic.StartInitialProcess(KernelContext, creationInfo, defaultCapabilities, 44, () =>
|
||||
{
|
||||
service.Start(KernelContext.Syscall, KernelStatic.GetCurrentProcess().CpuMemory, KernelStatic.GetCurrentThread().ThreadContext);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public bool LoadKip(string kipPath)
|
||||
{
|
||||
using var kipFile = new SharedRef<IStorage>(new LocalStorage(kipPath, FileAccess.Read));
|
||||
|
||||
return ProcessLoaderHelper.LoadKip(KernelContext, new KipExecutable(in kipFile));
|
||||
}
|
||||
|
||||
public void ChangeDockedModeState(bool newState)
|
||||
{
|
||||
if (newState != State.DockedMode)
|
||||
{
|
||||
State.DockedMode = newState;
|
||||
PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||
|
||||
AppletState.Messages.Enqueue(AppletMessage.OperationModeChanged);
|
||||
AppletState.Messages.Enqueue(AppletMessage.PerformanceModeChanged);
|
||||
AppletState.MessageEvent.ReadableEvent.Signal();
|
||||
|
||||
SignalDisplayResolutionChange();
|
||||
|
||||
Device.Configuration.RefreshInputConfig?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetVolume(float volume)
|
||||
{
|
||||
AudioOutputManager.SetVolume(volume);
|
||||
AudioRendererManager.SetVolume(volume);
|
||||
}
|
||||
|
||||
public float GetVolume()
|
||||
{
|
||||
return AudioOutputManager.GetVolume() == 0 ? AudioRendererManager.GetVolume() : AudioOutputManager.GetVolume();
|
||||
}
|
||||
|
||||
public void ReturnFocus()
|
||||
{
|
||||
AppletState.SetFocus(true);
|
||||
}
|
||||
|
||||
public void SimulateWakeUpMessage()
|
||||
{
|
||||
AppletState.Messages.Enqueue(AppletMessage.Resume);
|
||||
AppletState.MessageEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
|
||||
{
|
||||
if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag)
|
||||
{
|
||||
NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
|
||||
NfpDevices[nfpDeviceId].AmiiboId = amiiboId;
|
||||
NfpDevices[nfpDeviceId].UseRandomUuid = useRandomUuid;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SearchingForAmiibo(out int nfpDeviceId)
|
||||
{
|
||||
nfpDeviceId = default;
|
||||
|
||||
for (int i = 0; i < NfpDevices.Count; i++)
|
||||
{
|
||||
if (NfpDevices[i].State == NfpDeviceState.SearchingForTag)
|
||||
{
|
||||
nfpDeviceId = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SignalDisplayResolutionChange()
|
||||
{
|
||||
DisplayResolutionChangeEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
public void SignalVsync()
|
||||
{
|
||||
VsyncEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed && disposing)
|
||||
{
|
||||
_isDisposed = true;
|
||||
|
||||
// "Soft" stops AudioRenderer and AudioManager to avoid some sound between resume and stop.
|
||||
if (IsPaused)
|
||||
{
|
||||
AudioManager.StopUpdates();
|
||||
|
||||
TogglePauseEmulation(false);
|
||||
|
||||
AudioRendererManager.StopSendingCommands();
|
||||
}
|
||||
|
||||
KProcess terminationProcess = new KProcess(KernelContext);
|
||||
KThread terminationThread = new KThread(KernelContext);
|
||||
|
||||
terminationThread.Initialize(0, 0, 0, 3, 0, terminationProcess, ThreadType.Kernel, () =>
|
||||
{
|
||||
// Force all threads to exit.
|
||||
lock (KernelContext.Processes)
|
||||
{
|
||||
// Terminate application.
|
||||
foreach (KProcess process in KernelContext.Processes.Values.Where(x => x.IsApplication))
|
||||
{
|
||||
process.Terminate();
|
||||
process.DecrementReferenceCount();
|
||||
}
|
||||
|
||||
// The application existed, now surface flinger can exit too.
|
||||
SurfaceFlinger.Dispose();
|
||||
|
||||
// Terminate HLE services (must be done after the application is already terminated,
|
||||
// otherwise the application will receive errors due to service termination).
|
||||
foreach (KProcess process in KernelContext.Processes.Values.Where(x => !x.IsApplication))
|
||||
{
|
||||
process.Terminate();
|
||||
process.DecrementReferenceCount();
|
||||
}
|
||||
|
||||
KernelContext.Processes.Clear();
|
||||
}
|
||||
|
||||
// Exit ourself now!
|
||||
KernelStatic.GetCurrentThread().Exit();
|
||||
});
|
||||
|
||||
terminationThread.Start();
|
||||
|
||||
// Wait until the thread is actually started.
|
||||
while (terminationThread.HostThread.ThreadState == ThreadState.Unstarted)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
// Wait until the termination thread is done terminating all the other threads.
|
||||
terminationThread.HostThread.Join();
|
||||
|
||||
// Destroy nvservices channels as KThread could be waiting on some user events.
|
||||
// This is safe as KThread that are likely to call ioctls are going to be terminated by the post handler hook on the SVC facade.
|
||||
INvDrvServices.Destroy();
|
||||
|
||||
AudioManager.Dispose();
|
||||
AudioOutputManager.Dispose();
|
||||
AudioInputManager.Dispose();
|
||||
|
||||
AudioRendererManager.Dispose();
|
||||
|
||||
if (LibHacHorizonManager.ApplicationClient != null)
|
||||
{
|
||||
LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure();
|
||||
}
|
||||
|
||||
KernelContext.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void TogglePauseEmulation(bool pause)
|
||||
{
|
||||
lock (KernelContext.Processes)
|
||||
{
|
||||
foreach (KProcess process in KernelContext.Processes.Values)
|
||||
{
|
||||
if (process.IsApplication)
|
||||
{
|
||||
// Only game process should be paused.
|
||||
process.SetActivity(pause);
|
||||
}
|
||||
}
|
||||
|
||||
if (pause && !IsPaused)
|
||||
{
|
||||
Device.AudioDeviceDriver.GetPauseEvent().Reset();
|
||||
TickSource.Suspend();
|
||||
}
|
||||
else if (!pause && IsPaused)
|
||||
{
|
||||
Device.AudioDeviceDriver.GetPauseEvent().Set();
|
||||
TickSource.Resume();
|
||||
}
|
||||
}
|
||||
IsPaused = pause;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user