mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Implement PatrolReader
This commit is contained in:
parent
4ffc834427
commit
0964ecb2ee
6 changed files with 523 additions and 18 deletions
|
@ -25,7 +25,7 @@ public class DefaultFsServerObjects
|
||||||
var sdCard = new EmulatedSdCard();
|
var sdCard = new EmulatedSdCard();
|
||||||
|
|
||||||
var gameCardNew = new GameCardDummy();
|
var gameCardNew = new GameCardDummy();
|
||||||
var sdmmcNew = new SdmmcApi();
|
var sdmmcNew = new SdmmcApi(fsServer);
|
||||||
|
|
||||||
var gcStorageCreator = new EmulatedGameCardStorageCreator(gameCard);
|
var gcStorageCreator = new EmulatedGameCardStorageCreator(gameCard);
|
||||||
|
|
||||||
|
|
73
src/LibHac/Os/Event.cs
Normal file
73
src/LibHac/Os/Event.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.Os;
|
||||||
|
|
||||||
|
public struct EventType
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Event : IDisposable
|
||||||
|
{
|
||||||
|
private EventType _event;
|
||||||
|
|
||||||
|
private readonly OsState _os;
|
||||||
|
|
||||||
|
public Event(OsState os, EventClearMode clearMode)
|
||||||
|
{
|
||||||
|
_os = os;
|
||||||
|
_os.InitializeEvent(ref _event, signaled: false, clearMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => _os.FinalizeEvent(ref _event);
|
||||||
|
public void Wait() => _os.WaitEvent(ref _event);
|
||||||
|
public bool TryWait() => _os.TryWaitEvent(ref _event);
|
||||||
|
public bool TimedWait(TimeSpan timeout) => _os.TimedWaitEvent(ref _event, timeout);
|
||||||
|
public void Signal() => _os.SignalEvent(ref _event);
|
||||||
|
public void Clear() => _os.ClearEvent(ref _event);
|
||||||
|
public ref EventType GetBase() => ref _event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EventApi
|
||||||
|
{
|
||||||
|
public static void InitializeEvent(this OsState os, ref EventType eventType, bool signaled,
|
||||||
|
EventClearMode clearMode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FinalizeEvent(this OsState os, ref EventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SignalEvent(this OsState os, ref EventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WaitEvent(this OsState os, ref EventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryWaitEvent(this OsState os, ref EventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TimedWaitEvent(this OsState os, ref EventType eventType, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearEvent(this OsState os, ref EventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeMultiWaitHolder(this OsState os, ref MultiWaitHolderType multiWaitHolder,
|
||||||
|
ref EventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
7
src/LibHac/Os/EventCommon.cs
Normal file
7
src/LibHac/Os/EventCommon.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace LibHac.Os;
|
||||||
|
|
||||||
|
public enum EventClearMode
|
||||||
|
{
|
||||||
|
ManualClear = 0,
|
||||||
|
AutoClear = 1
|
||||||
|
}
|
85
src/LibHac/Os/TimerEvent.cs
Normal file
85
src/LibHac/Os/TimerEvent.cs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.Os;
|
||||||
|
|
||||||
|
public struct TimerEventType
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TimerEvent : IDisposable
|
||||||
|
{
|
||||||
|
private TimerEventType _event;
|
||||||
|
|
||||||
|
private readonly OsState _os;
|
||||||
|
|
||||||
|
public TimerEvent(OsState os, EventClearMode clearMode)
|
||||||
|
{
|
||||||
|
_os = os;
|
||||||
|
_os.InitializeTimerEvent(ref _event, clearMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => _os.FinalizeTimerEvent(ref _event);
|
||||||
|
public void StartOneShot(TimeSpan firstTime) => _os.StartOneShotTimerEvent(ref _event, firstTime);
|
||||||
|
public void StartPeriodic(TimeSpan firstTime, TimeSpan interval) => _os.StartPeriodicTimerEvent(ref _event, firstTime, interval);
|
||||||
|
public void Stop() => _os.StopTimerEvent(ref _event);
|
||||||
|
public void Wait() => _os.WaitTimerEvent(ref _event);
|
||||||
|
public bool TryWait() => _os.TryWaitTimerEvent(ref _event);
|
||||||
|
public void Signal() => _os.SignalTimerEvent(ref _event);
|
||||||
|
public void Clear() => _os.ClearTimerEvent(ref _event);
|
||||||
|
public ref TimerEventType GetBase() => ref _event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TimerEventApi
|
||||||
|
{
|
||||||
|
public static void InitializeTimerEvent(this OsState os, ref TimerEventType eventType, EventClearMode clearMode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FinalizeTimerEvent(this OsState os, ref TimerEventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StartOneShotTimerEvent(this OsState os, ref TimerEventType eventType, TimeSpan firstTime)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StartPeriodicTimerEvent(this OsState os, ref TimerEventType eventType, TimeSpan firstTime,
|
||||||
|
TimeSpan interval)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StopTimerEvent(this OsState os, ref TimerEventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SignalTimerEvent(this OsState os, ref TimerEventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearTimerEvent(this OsState os, ref TimerEventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WaitTimerEvent(this OsState os, ref TimerEventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryWaitTimerEvent(this OsState os, ref TimerEventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeMultiWaitHolder(this OsState os, ref MultiWaitHolderType multiWaitHolder,
|
||||||
|
ref TimerEventType eventType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using LibHac.FsSrv;
|
||||||
|
|
||||||
namespace LibHac.Sdmmc;
|
namespace LibHac.Sdmmc;
|
||||||
|
|
||||||
|
@ -60,6 +61,14 @@ public partial class SdmmcApi
|
||||||
public const int DeviceCidSize = 0x10;
|
public const int DeviceCidSize = 0x10;
|
||||||
public const int DeviceCsdSize = 0x10;
|
public const int DeviceCsdSize = 0x10;
|
||||||
|
|
||||||
|
private FileSystemServer _fsServer;
|
||||||
|
internal HorizonClient Hos => _fsServer.Hos;
|
||||||
|
|
||||||
|
public SdmmcApi(FileSystemServer fsServer)
|
||||||
|
{
|
||||||
|
_fsServer = fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
public void SwitchToPcvClockResetControl()
|
public void SwitchToPcvClockResetControl()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
|
@ -1,73 +1,404 @@
|
||||||
using System;
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Crypto;
|
||||||
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSystem;
|
||||||
using LibHac.Os;
|
using LibHac.Os;
|
||||||
using LibHac.Sdmmc;
|
using LibHac.Sdmmc;
|
||||||
|
using static LibHac.SdmmcSrv.Common;
|
||||||
|
using MmcPartition = LibHac.Sdmmc.MmcPartition;
|
||||||
|
|
||||||
namespace LibHac.SdmmcSrv;
|
namespace LibHac.SdmmcSrv;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs regular patrol reads on the internal MMC.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><para>The patrol reader will read sequential half-megabyte blocks of data from the internal MMC,
|
||||||
|
/// starting at the beginning of the storage, progressing to the end, and then looping back to the beginning.
|
||||||
|
/// This helps with data integrity by ensuring the entire MMC is regularly read.</para>
|
||||||
|
/// <para>Every 6 seconds the patrol reader will read a half-megabyte block from the MMC.
|
||||||
|
/// Every 2 hours it will save the current state of the patrol read to Boot Partition 1 on the MMC.
|
||||||
|
/// This state contains the next sector index to be read and the number of times the MMC has been patrolled
|
||||||
|
/// from start to finish.</para>
|
||||||
|
/// <para>Based on nnSdk 14.3.0 (FS 14.1.0)</para></remarks>
|
||||||
internal class PatrolReader
|
internal class PatrolReader
|
||||||
{
|
{
|
||||||
|
// Note: This class won't work until events and timer events are properly implemented.
|
||||||
|
|
||||||
|
private const int MacSize = HmacSha256.HashSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The patrol reader state that's stored in Boot Partition 1.
|
||||||
|
/// </summary>
|
||||||
|
public struct State
|
||||||
|
{
|
||||||
|
public uint CurrentSector;
|
||||||
|
public uint PatrolCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ThreadState
|
||||||
|
{
|
||||||
|
Stop,
|
||||||
|
Active,
|
||||||
|
Sleep
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly int _macSize;
|
||||||
|
private readonly MmcPartition _statePartition;
|
||||||
|
private readonly int _patrolStateOffset;
|
||||||
|
private readonly int _patrolStateSize;
|
||||||
|
private readonly int _saveStateIntervalSeconds;
|
||||||
|
private readonly int _patrolReadSize;
|
||||||
|
private readonly int _waitTimeAfterAllocationSuccess;
|
||||||
|
private readonly int _waitTimeAfterAllocationFailure;
|
||||||
|
private readonly int _firstRunDelaySeconds;
|
||||||
|
private uint _deviceCapacitySectors;
|
||||||
|
private State _patrolState;
|
||||||
|
private bool _isPatrolStateLoaded;
|
||||||
|
private SdkMutex _mutex;
|
||||||
|
private bool _areEventsInitialized;
|
||||||
|
private ThreadState _threadState;
|
||||||
|
// private Thread _patrolReaderThread;
|
||||||
|
private Event _stateChangeRequestedEvent;
|
||||||
|
private Event _stateChangeCompletedEvent;
|
||||||
|
private ulong _allocateSuccessCount;
|
||||||
|
private ulong _allocateFailureCount;
|
||||||
|
|
||||||
|
// LibHac addition
|
||||||
|
private SdmmcApi _sdmmc;
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> PatrolStateKey => "U{W5>1Kq#Gt`f6r86o`9|*||hTy9U2C\0"u8;
|
||||||
|
|
||||||
public PatrolReader(SdkMutex mutex, SdmmcApi sdmmc)
|
public PatrolReader(SdkMutex mutex, SdmmcApi sdmmc)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
_macSize = MacSize;
|
||||||
|
_statePartition = MmcPartition.BootPartition1;
|
||||||
|
_patrolStateOffset = 0x184000;
|
||||||
|
_patrolStateSize = 0x200;
|
||||||
|
_saveStateIntervalSeconds = 7200;
|
||||||
|
_patrolReadSize = 0x80000;
|
||||||
|
_waitTimeAfterAllocationSuccess = 6;
|
||||||
|
_waitTimeAfterAllocationFailure = 1;
|
||||||
|
_firstRunDelaySeconds = 18;
|
||||||
|
_isPatrolStateLoaded = false;
|
||||||
|
_mutex = mutex;
|
||||||
|
_areEventsInitialized = false;
|
||||||
|
_threadState = ThreadState.Stop;
|
||||||
|
_allocateSuccessCount = 0;
|
||||||
|
_allocateFailureCount = 0;
|
||||||
|
|
||||||
|
_sdmmc = sdmmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
FinalizeObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PatrolReaderThreadEntry(object args)
|
public static void PatrolReaderThreadEntry(object args)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (args is PatrolReader reader)
|
||||||
|
{
|
||||||
|
reader.PatrolReaderThread();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Abort.DoAbort($"Expected an argument of type {nameof(PatrolReader)} in {nameof(PatrolReaderThreadEntry)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FinalizeObject()
|
private void FinalizeObject()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (_areEventsInitialized)
|
||||||
|
{
|
||||||
|
if (_threadState == ThreadState.Sleep)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_threadState == ThreadState.Active)
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
_stateChangeRequestedEvent.Dispose();
|
||||||
|
_stateChangeCompletedEvent.Dispose();
|
||||||
|
|
||||||
|
_areEventsInitialized = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetPatrolCount(out uint outCount)
|
public Result GetPatrolCount(out uint outCount)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
UnsafeHelpers.SkipParamInit(out outCount);
|
||||||
|
|
||||||
|
if (!_isPatrolStateLoaded)
|
||||||
|
return ResultFs.HasNotGottenPatrolCount.Log();
|
||||||
|
|
||||||
|
outCount = _patrolState.PatrolCount;
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetAndClearAllocateCount(out ulong outSuccessCount, out ulong outFailureCount)
|
public void GetAndClearAllocateCount(out ulong outSuccessCount, out ulong outFailureCount)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
outSuccessCount = _allocateSuccessCount;
|
||||||
|
outFailureCount = _allocateFailureCount;
|
||||||
|
|
||||||
|
_allocateSuccessCount = 0;
|
||||||
|
_allocateFailureCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadState()
|
private bool LoadState()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
using var pooledBuffer = new PooledBuffer(_patrolStateSize, 1);
|
||||||
|
if (pooledBuffer.GetSize() < _patrolStateSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
Abort.DoAbortUnlessSuccess(_sdmmc.SelectMmcPartition(Port.Mmc0, _statePartition));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result res = _sdmmc.GetDeviceMemoryCapacity(out _deviceCapacitySectors, Port.Mmc0);
|
||||||
|
if (res.IsFailure())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> patrolStateBuffer = pooledBuffer.GetBuffer().Slice(0, _patrolStateSize);
|
||||||
|
|
||||||
|
res = _sdmmc.Read(patrolStateBuffer, Port.Mmc0, BytesToSectors(_patrolStateOffset),
|
||||||
|
BytesToSectors(_patrolStateSize));
|
||||||
|
|
||||||
|
if (res.IsFailure())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> mac = stackalloc byte[MacSize];
|
||||||
|
|
||||||
|
// Load an empty state if the verification fails.
|
||||||
|
_patrolState = default;
|
||||||
|
|
||||||
|
HmacSha256.GenerateHmacSha256(mac, patrolStateBuffer.Slice(_macSize), PatrolStateKey);
|
||||||
|
|
||||||
|
if (CryptoUtil.IsSameBytes(mac, patrolStateBuffer, _macSize))
|
||||||
|
{
|
||||||
|
ref State readState = ref SpanHelpers.AsStruct<State>(patrolStateBuffer.Slice(_macSize));
|
||||||
|
|
||||||
|
if (readState.CurrentSector + BytesToSectors(_patrolReadSize) <= _deviceCapacitySectors)
|
||||||
|
{
|
||||||
|
_patrolState = readState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Abort.DoAbortUnlessSuccess(_sdmmc.SelectMmcPartition(Port.Mmc0, MmcPartition.UserData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SaveState()
|
private void SaveState()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
using var pooledBuffer = new PooledBuffer(_patrolStateSize, 1);
|
||||||
|
|
||||||
|
if (pooledBuffer.GetSize() < _patrolStateSize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
Span<byte> patrolStateBuffer = pooledBuffer.GetBuffer().Slice(0, _patrolStateSize);
|
||||||
|
patrolStateBuffer.Clear();
|
||||||
|
|
||||||
|
SpanHelpers.AsStruct<State>(patrolStateBuffer) = _patrolState;
|
||||||
|
|
||||||
|
HmacSha256.GenerateHmacSha256(patrolStateBuffer.Slice(0, _macSize), patrolStateBuffer.Slice(_macSize),
|
||||||
|
PatrolStateKey);
|
||||||
|
|
||||||
|
Abort.DoAbortUnlessSuccess(_sdmmc.SelectMmcPartition(Port.Mmc0, _statePartition));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_sdmmc.Write(Port.Mmc0, BytesToSectors(_patrolStateOffset), BytesToSectors(_patrolStateSize),
|
||||||
|
patrolStateBuffer);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Abort.DoAbortUnlessSuccess(_sdmmc.SelectMmcPartition(Port.Mmc0, MmcPartition.UserData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoPatrolRead()
|
||||||
|
{
|
||||||
|
using var pooledBuffer = new PooledBuffer(_patrolReadSize, 1);
|
||||||
|
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
if (pooledBuffer.GetSize() < _patrolReadSize)
|
||||||
|
{
|
||||||
|
if (_allocateFailureCount != ulong.MaxValue)
|
||||||
|
_allocateFailureCount++;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_allocateSuccessCount != ulong.MaxValue)
|
||||||
|
_allocateSuccessCount++;
|
||||||
|
|
||||||
|
_sdmmc.Read(pooledBuffer.GetBuffer().Slice(0, _patrolReadSize), Port.Mmc0, _patrolState.CurrentSector,
|
||||||
|
BytesToSectors(_patrolReadSize)).IgnoreResult();
|
||||||
|
|
||||||
|
_patrolState.CurrentSector += BytesToSectors(_patrolReadSize);
|
||||||
|
|
||||||
|
if (_patrolState.CurrentSector + BytesToSectors(_patrolReadSize) > _deviceCapacitySectors)
|
||||||
|
{
|
||||||
|
_patrolState.CurrentSector = 0;
|
||||||
|
_patrolState.PatrolCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
Assert.SdkAssert(_threadState is ThreadState.Active or ThreadState.Stop);
|
||||||
|
|
||||||
|
if (_threadState == ThreadState.Stop)
|
||||||
|
{
|
||||||
|
if (!_areEventsInitialized)
|
||||||
|
{
|
||||||
|
_areEventsInitialized = true;
|
||||||
|
_threadState = ThreadState.Stop;
|
||||||
|
|
||||||
|
_stateChangeRequestedEvent = new Event(_sdmmc.Hos.Os, EventClearMode.AutoClear);
|
||||||
|
_stateChangeCompletedEvent = new Event(_sdmmc.Hos.Os, EventClearMode.AutoClear);
|
||||||
|
}
|
||||||
|
|
||||||
|
_threadState = ThreadState.Active;
|
||||||
|
|
||||||
|
// CreateThread(_patrolReaderThread, PatrolReaderThreadEntry, this, pPatrolReaderStack, priority);
|
||||||
|
// SetThreadNamePointer(_patrolReaderThread, "nn.fs.PatrolReader"u8);
|
||||||
|
// StartThread(_patrolReaderThread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
Assert.SdkAssert(_threadState is ThreadState.Active or ThreadState.Stop or ThreadState.Sleep);
|
||||||
|
|
||||||
|
if (_threadState != ThreadState.Stop)
|
||||||
|
{
|
||||||
|
_threadState = ThreadState.Stop;
|
||||||
|
_stateChangeRequestedEvent.Signal();
|
||||||
|
|
||||||
|
// WaitThread(_patrolReaderThread);
|
||||||
|
// DestroyThread(_patrolReaderThread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Sleep()
|
public void Sleep()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
Assert.SdkAssert(_threadState is ThreadState.Active or ThreadState.Sleep);
|
||||||
|
|
||||||
|
if (_threadState == ThreadState.Active)
|
||||||
|
{
|
||||||
|
_threadState = ThreadState.Sleep;
|
||||||
|
|
||||||
|
_stateChangeRequestedEvent.Signal();
|
||||||
|
_stateChangeCompletedEvent.Wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume()
|
public void Resume()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
Assert.SdkAssert(_threadState is ThreadState.Active or ThreadState.Sleep);
|
||||||
|
|
||||||
|
if (_threadState == ThreadState.Sleep)
|
||||||
|
{
|
||||||
|
_threadState = ThreadState.Active;
|
||||||
|
|
||||||
|
_stateChangeRequestedEvent.Signal();
|
||||||
|
_stateChangeCompletedEvent.Wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PatrolReaderThread()
|
private void PatrolReaderThread()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
// Missing: SetServiceContext()
|
||||||
|
|
||||||
|
using var timer = new TimerEvent(_sdmmc.Hos.Os, EventClearMode.AutoClear);
|
||||||
|
|
||||||
|
timer.StartPeriodic(TimeSpan.FromSeconds(_firstRunDelaySeconds), TimeSpan.FromSeconds(_saveStateIntervalSeconds));
|
||||||
|
|
||||||
|
int currentWaitTime = _waitTimeAfterAllocationSuccess;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Wait until the next thread state change or until the next patrol read should be done.
|
||||||
|
if (_stateChangeRequestedEvent.TimedWait(TimeSpan.FromSeconds(currentWaitTime)))
|
||||||
|
{
|
||||||
|
if (_threadState == ThreadState.Stop)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_threadState == ThreadState.Sleep)
|
||||||
|
{
|
||||||
|
// Acknowledge the request to sleep and wait for the next state change.
|
||||||
|
_stateChangeCompletedEvent.Signal();
|
||||||
|
_stateChangeRequestedEvent.Wait();
|
||||||
|
|
||||||
|
if (_threadState == ThreadState.Stop)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_threadState == ThreadState.Active)
|
||||||
|
{
|
||||||
|
_stateChangeCompletedEvent.Signal();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.SdkAssert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isPatrolStateLoaded)
|
||||||
|
{
|
||||||
|
// The patrol state will be loaded a single time when the console is booted.
|
||||||
|
// Don't load the patrol state or do patrol reads until the specified amount
|
||||||
|
// of time has past since creating the Patrol Reader.
|
||||||
|
if (!timer.TryWait() || !LoadState())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isPatrolStateLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DoPatrolRead())
|
||||||
|
{
|
||||||
|
currentWaitTime = _waitTimeAfterAllocationSuccess;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentWaitTime = _waitTimeAfterAllocationFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the patrol state periodically once the specified amount of time has passed since the last save.
|
||||||
|
if (timer.TryWait())
|
||||||
|
{
|
||||||
|
SaveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the patrol state if necessary when shutting down.
|
||||||
|
if (_isPatrolStateLoaded)
|
||||||
|
SaveState();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue