diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv
index f9c257ec..6df4f75b 100644
--- a/build/CodeGen/results.csv
+++ b/build/CodeGen/results.csv
@@ -310,8 +310,8 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,2955,,,,GameCardFsCheckHandleInGetDeviceCertFailure,
2,2956,,,,GameCardFsCheckHandleInGetCardImageHashFailure,
2,2957,,,,GameCardFsCheckHandleInChallengeCardExistence,
-2,2958,,,,GameCardFsCheckHandleInOnAcquireLock,
-2,2959,,,,GameCardFsCheckModeInOnAcquireSecureLock,
+2,2958,,,,GameCardFsCheckHandleInAcquireReadLock,
+2,2959,,,,GameCardFsCheckModeInAcquireSecureLock,
2,2960,,,,GameCardFsCheckHandleInCreateReadOnlyFailure,
2,2961,,,,GameCardFsCheckHandleInCreateSecureReadOnlyFailure,
2,2962,,,,GameCardFsInvalidCompatibilityType,
diff --git a/src/LibHac/Fs/GameCard.cs b/src/LibHac/Fs/GameCard.cs
index 3cf956fb..ba94d7c2 100644
--- a/src/LibHac/Fs/GameCard.cs
+++ b/src/LibHac/Fs/GameCard.cs
@@ -45,6 +45,12 @@ public enum GameCardSizeInternal : byte
Size32Gb = 0xE2
}
+public enum GameCardClockRate
+{
+ ClockRate25 = 25,
+ ClockRate50 = 50
+}
+
[Flags]
public enum GameCardAttribute : byte
{
diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs
index fb10f0dc..b9d97bcb 100644
--- a/src/LibHac/Fs/ResultFs.cs
+++ b/src/LibHac/Fs/ResultFs.cs
@@ -518,9 +518,9 @@ public static class ResultFs
/// Error code: 2002-2957; Inner value: 0x171a02
public static Result.Base GameCardFsCheckHandleInChallengeCardExistence => new Result.Base(ModuleFs, 2957);
/// Error code: 2002-2958; Inner value: 0x171c02
- public static Result.Base GameCardFsCheckHandleInOnAcquireLock => new Result.Base(ModuleFs, 2958);
+ public static Result.Base GameCardFsCheckHandleInAcquireReadLock => new Result.Base(ModuleFs, 2958);
/// Error code: 2002-2959; Inner value: 0x171e02
- public static Result.Base GameCardFsCheckModeInOnAcquireSecureLock => new Result.Base(ModuleFs, 2959);
+ public static Result.Base GameCardFsCheckModeInAcquireSecureLock => new Result.Base(ModuleFs, 2959);
/// Error code: 2002-2960; Inner value: 0x172002
public static Result.Base GameCardFsCheckHandleInCreateReadOnlyFailure => new Result.Base(ModuleFs, 2960);
/// Error code: 2002-2961; Inner value: 0x172202
diff --git a/src/LibHac/FsSrv/BaseStorageService.cs b/src/LibHac/FsSrv/BaseStorageService.cs
index ee1dae2c..bc030150 100644
--- a/src/LibHac/FsSrv/BaseStorageService.cs
+++ b/src/LibHac/FsSrv/BaseStorageService.cs
@@ -136,8 +136,11 @@ public readonly struct BaseStorageService
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
- res = _serviceImpl.OpenDeviceOperator(ref outDeviceOperator, programInfo.AccessControl);
- if (res.IsFailure()) return res.Miss();
+ using var deviceOperator =
+ new SharedRef(new DeviceOperator(_serviceImpl.FsServer, programInfo.AccessControl,
+ _processId));
+
+ outDeviceOperator.SetByMove(ref deviceOperator.Ref);
return Result.Success;
}
@@ -180,13 +183,11 @@ public class BaseStorageServiceImpl
{
private Configuration _config;
- // LibHac addition
- private SharedRef _deviceOperator;
+ internal FileSystemServer FsServer => _config.FsServer;
public BaseStorageServiceImpl(in Configuration configuration)
{
_config = configuration;
- _deviceOperator = new SharedRef(configuration.DeviceOperator);
}
public struct Configuration
@@ -196,8 +197,6 @@ public class BaseStorageServiceImpl
// LibHac additions
public FileSystemServer FsServer;
- // Todo: The DeviceOperator in FS uses mostly global state. Decide how to handle this.
- public IDeviceOperator DeviceOperator;
}
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
@@ -231,12 +230,4 @@ public class BaseStorageServiceImpl
return ResultFs.InvalidArgument.Log();
}
}
-
- // LibHac addition
- internal Result OpenDeviceOperator(ref SharedRef outDeviceOperator,
- AccessControl accessControl)
- {
- outDeviceOperator.SetByCopy(in _deviceOperator);
- return Result.Success;
- }
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/DefaultFsServerObjects.cs b/src/LibHac/FsSrv/DefaultFsServerObjects.cs
index 9f780f16..7adf2fc6 100644
--- a/src/LibHac/FsSrv/DefaultFsServerObjects.cs
+++ b/src/LibHac/FsSrv/DefaultFsServerObjects.cs
@@ -1,7 +1,7 @@
using LibHac.Common;
using LibHac.Common.Keys;
using LibHac.FsSrv.FsCreator;
-using LibHac.FsSrv.Sf;
+using LibHac.FsSrv.Storage;
using LibHac.FsSystem;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
@@ -10,9 +10,9 @@ namespace LibHac.FsSrv;
public class DefaultFsServerObjects
{
public FileSystemCreatorInterfaces FsCreators { get; set; }
- public IDeviceOperator DeviceOperator { get; set; }
public EmulatedGameCard GameCard { get; set; }
public EmulatedSdCard SdCard { get; set; }
+ public EmulatedStorageDeviceManagerFactory StorageDeviceManagerFactory { get; set; }
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, KeySet keySet,
FileSystemServer fsServer, RandomDataGenerator randomGenerator)
@@ -39,14 +39,14 @@ public class DefaultFsServerObjects
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(ref sharedRootFileSystem.Ref);
creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdCard, ref sharedRootFileSystemCopy.Ref);
- var deviceOperator = new EmulatedDeviceOperator(gameCard, sdCard);
+ var storageDeviceManagerFactory = new EmulatedStorageDeviceManagerFactory(fsServer, true);
return new DefaultFsServerObjects
{
FsCreators = creators,
- DeviceOperator = deviceOperator,
GameCard = gameCard,
- SdCard = sdCard
+ SdCard = sdCard,
+ StorageDeviceManagerFactory = storageDeviceManagerFactory
};
}
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FileSystemServerInitializer.cs b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
index f461cb19..ea543f8e 100644
--- a/src/LibHac/FsSrv/FileSystemServerInitializer.cs
+++ b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
@@ -31,11 +31,11 @@ public static class FileSystemServerInitializer
if (config.FsCreators == null)
throw new ArgumentException("FsCreators must not be null");
- if (config.DeviceOperator == null)
- throw new ArgumentException("DeviceOperator must not be null");
+ if (config.StorageDeviceManagerFactory == null)
+ throw new ArgumentException("StorageDeviceManagerFactory must not be null");
server.SetDebugFlagEnabled(false);
- server.Storage.InitializeStorageDeviceManagerFactory(null);
+ server.Storage.InitializeStorageDeviceManagerFactory(config.StorageDeviceManagerFactory);
FileSystemProxyConfiguration fspConfig = InitializeFileSystemProxy(server, config);
@@ -55,6 +55,9 @@ public static class FileSystemServerInitializer
saveService.FixSaveData().IgnoreResult();
saveService.RecoverMultiCommit().IgnoreResult();
+ config.StorageDeviceManagerFactory.SetReady(StorageDevicePortId.SdCard, null);
+ config.StorageDeviceManagerFactory.SetReady(StorageDevicePortId.GameCard, null);
+
// NS usually takes care of this
if (client.Fs.IsSdCardInserted())
client.Fs.SetSdCardAccessibility(true);
@@ -86,7 +89,6 @@ public static class FileSystemServerInitializer
baseStorageConfig.BisStorageCreator = config.FsCreators.BuiltInStorageCreator;
baseStorageConfig.GameCardStorageCreator = config.FsCreators.GameCardStorageCreator;
baseStorageConfig.FsServer = server;
- baseStorageConfig.DeviceOperator = config.DeviceOperator;
var baseStorageService = new BaseStorageServiceImpl(in baseStorageConfig);
var timeService = new TimeServiceImpl(server);
@@ -263,9 +265,9 @@ public class FileSystemServerConfig
public FileSystemCreatorInterfaces FsCreators { get; set; }
///
- /// An for managing the gamecard and SD card.
+ /// An for managing the gamecard and SD card.
///
- public IDeviceOperator DeviceOperator { get; set; }
+ public IStorageDeviceManagerFactory StorageDeviceManagerFactory { get; set; }
///
/// A keyset containing rights IDs and title keys.
diff --git a/src/LibHac/FsSrv/Storage/EmulatedStorageDeviceManagerFactory.cs b/src/LibHac/FsSrv/Storage/EmulatedStorageDeviceManagerFactory.cs
new file mode 100644
index 00000000..2e3a1902
--- /dev/null
+++ b/src/LibHac/FsSrv/Storage/EmulatedStorageDeviceManagerFactory.cs
@@ -0,0 +1,150 @@
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.FsSrv.Storage.Sf;
+using LibHac.GcSrv;
+using LibHac.Os;
+using LibHac.SdmmcSrv;
+using LibHac.Sf;
+
+namespace LibHac.FsSrv.Storage;
+
+public class EmulatedStorageDeviceManagerFactory : IStorageDeviceManagerFactory
+{
+ private SdkMutexType _gameCardDeviceMutex;
+ private SdkMutexType _sdCardDeviceMutex;
+ private SdkMutexType _mmcDeviceMutex;
+
+ private SharedRef _sdCardDeviceManager;
+ private SharedRef _dummyGameCardDeviceManager;
+ private SharedRef _gameCardDeviceManager;
+ private SharedRef _mmcDeviceManager;
+
+ private readonly bool _hasGameCard;
+
+ private readonly FileSystemServer _fsServer;
+
+ public EmulatedStorageDeviceManagerFactory(FileSystemServer fsServer, bool hasGameCard)
+ {
+ _fsServer = fsServer;
+ _hasGameCard = hasGameCard;
+
+ _gameCardDeviceMutex = new SdkMutexType();
+ _sdCardDeviceMutex = new SdkMutexType();
+ }
+
+ public void Dispose()
+ {
+ _sdCardDeviceManager.Destroy();
+ _dummyGameCardDeviceManager.Destroy();
+ _gameCardDeviceManager.Destroy();
+ _mmcDeviceManager.Destroy();
+ }
+
+ public Result Create(ref SharedRef outDeviceManager, StorageDevicePortId portId)
+ {
+ switch (portId)
+ {
+ case StorageDevicePortId.Mmc:
+ EnsureMmcReady();
+ outDeviceManager.SetByCopy(in _mmcDeviceManager);
+ break;
+ case StorageDevicePortId.SdCard:
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _sdCardDeviceMutex);
+
+ if (!_sdCardDeviceManager.HasValue)
+ return ResultFs.StorageDeviceNotReady.Log();
+
+ outDeviceManager.SetByCopy(in _sdCardDeviceManager);
+ break;
+ }
+ case StorageDevicePortId.GameCard:
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _gameCardDeviceMutex);
+
+ if (!_dummyGameCardDeviceManager.HasValue && !_gameCardDeviceManager.HasValue)
+ return ResultFs.StorageDeviceNotReady.Log();
+
+ if (_hasGameCard)
+ {
+ outDeviceManager.SetByCopy(in _gameCardDeviceManager);
+ }
+ else
+ {
+ outDeviceManager.SetByCopy(in _dummyGameCardDeviceManager);
+ }
+
+ break;
+ }
+ default:
+ return ResultFs.StorageDeviceInvalidOperation.Log();
+ }
+
+ return Result.Success;
+ }
+
+ public Result SetReady(StorageDevicePortId portId, NativeHandle handle)
+ {
+ switch (portId)
+ {
+ case StorageDevicePortId.Mmc:
+ EnsureMmcReady();
+ break;
+ case StorageDevicePortId.SdCard:
+ EnsureSdCardReady();
+ break;
+ case StorageDevicePortId.GameCard:
+ EnsureGameCardReady();
+ break;
+ default:
+ return ResultFs.StorageDeviceInvalidOperation.Log();
+ }
+
+ return Result.Success;
+ }
+
+ public Result UnsetReady(StorageDevicePortId portId)
+ {
+ return ResultFs.StorageDeviceInvalidOperation.Log();
+ }
+
+ private void EnsureMmcReady()
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mmcDeviceMutex);
+
+ if (!_mmcDeviceManager.HasValue)
+ {
+ _mmcDeviceManager.Reset(new MmcManager());
+ }
+ }
+
+ private void EnsureSdCardReady()
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _sdCardDeviceMutex);
+
+ if (!_sdCardDeviceManager.HasValue)
+ {
+ _sdCardDeviceManager.Reset(new SdCardManager());
+
+ // Todo: BuiltInStorageFileSystemCreator::SetSdCardPortReady
+ }
+ }
+
+ private void EnsureGameCardReady()
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _gameCardDeviceMutex);
+
+ if (!_dummyGameCardDeviceManager.HasValue && !_gameCardDeviceManager.HasValue)
+ {
+ if (_hasGameCard)
+ {
+ using SharedRef manger = GameCardManager.CreateShared(_fsServer);
+ _gameCardDeviceManager.SetByMove(ref manger.Ref);
+ }
+ else
+ {
+ _dummyGameCardDeviceManager.Reset(new DummyGameCardManager());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceManager.cs b/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceManager.cs
index 1564a270..39f69df6 100644
--- a/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceManager.cs
+++ b/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceManager.cs
@@ -8,8 +8,8 @@ namespace LibHac.FsSrv.Storage.Sf;
public interface IStorageDeviceManager : IDisposable
{
Result IsInserted(out bool isInserted);
- Result IsHandleValid(out bool isValid, uint handle);
- Result OpenDetectionEvent(ref SharedRef outEventNotifier);
+ Result IsHandleValid(out bool isValid, GameCardHandle handle);
+ Result OpenDetectionEvent(ref SharedRef outDetectionEvent);
Result OpenOperator(ref SharedRef outDeviceOperator);
Result OpenDevice(ref SharedRef outStorageDevice, ulong attribute);
Result OpenStorage(ref SharedRef outStorage, ulong attribute);
diff --git a/src/LibHac/FsSrv/Storage/StorageDeviceManagerFactory.cs b/src/LibHac/FsSrv/Storage/StorageDeviceManagerFactory.cs
index d19650c5..13684b10 100644
--- a/src/LibHac/FsSrv/Storage/StorageDeviceManagerFactory.cs
+++ b/src/LibHac/FsSrv/Storage/StorageDeviceManagerFactory.cs
@@ -33,7 +33,7 @@ internal static class StorageDeviceManagerFactory
IStorageDeviceManagerFactory factory = storage.GetStorageDeviceManagerFactory(null);
Assert.SdkNotNull(factory);
- return factory.Create(ref outDeviceManager, portId);
+ return factory.Create(ref outDeviceManager, portId).Ret();
}
public static IStorageDeviceManagerFactory GetStorageDeviceManagerFactory(this StorageService storage,
diff --git a/src/LibHac/FsSystem/CardDeviceDetectionEvent.cs b/src/LibHac/FsSystem/CardDeviceDetectionEvent.cs
new file mode 100644
index 00000000..df86e661
--- /dev/null
+++ b/src/LibHac/FsSystem/CardDeviceDetectionEvent.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using LibHac.Common;
+using LibHac.FsSrv.Sf;
+using LibHac.Os;
+using LibHac.Sf;
+
+namespace LibHac.FsSystem;
+
+internal class CardDeviceDetectionEventManager : IDisposable
+{
+ private LinkedList _events;
+ private SdkMutex _mutex;
+ protected CallbackArguments CallbackArgs;
+
+ protected class CallbackArguments
+ {
+ public CardDeviceDetectionEventManager EventManager;
+ public SdkMutex Mutex;
+ public Sdmmc.Port Port;
+ }
+
+ public CardDeviceDetectionEventManager()
+ {
+ _events = new LinkedList();
+ _mutex = new SdkMutex();
+
+ CallbackArgs = new CallbackArguments { EventManager = this, Mutex = _mutex };
+ }
+
+ public virtual void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result CreateDetectionEvent(ref SharedRef outDetectionEvent)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Unlink(CardDeviceDetectionEvent detectionEvent)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SignalAll()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected static void DetectionEventCallback(object args)
+ {
+ throw new NotImplementedException();
+ }
+}
+
+internal class CardDeviceDetectionEvent : IEventNotifier
+{
+ private CardDeviceDetectionEventManager _eventManager;
+ // Todo: SystemEvent
+
+ public CardDeviceDetectionEvent(CardDeviceDetectionEventManager eventManager)
+ {
+ _eventManager = eventManager;
+ }
+
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetEventHandle(out NativeHandle handle)
+ {
+ throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/Gc/GameCardDummy.cs b/src/LibHac/Gc/GameCardDummy.cs
new file mode 100644
index 00000000..3c21c6ef
--- /dev/null
+++ b/src/LibHac/Gc/GameCardDummy.cs
@@ -0,0 +1,200 @@
+using System;
+using LibHac.Fs;
+using LibHac.Gc.Impl;
+using LibHac.Gc.Writer;
+
+namespace LibHac.Gc;
+
+public class GameCardDummy
+{
+ public GameCardWriter Writer => new GameCardWriter();
+
+ public readonly struct GameCardWriter
+ {
+ public GameCardWriter()
+ {
+
+ }
+
+ public void ChangeMode(AsicMode mode)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result ActivateForWriter()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result EraseAndWriteParameter(MemorySize size, uint romAreaStartPageIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result Write(ReadOnlySpan source, uint pageIndex, uint pageCount)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetCardAvailableRawSize(out long outSize)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetVerifyEnableFlag(bool isEnabled)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetUserAsicFirmwareBuffer(ReadOnlySpan firmwareBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetRmaInformation(out RmaInformation outRmaInformation)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result WriteDevCardParam(in DevCardParameter devCardParam)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result ReadDevCardParam(out DevCardParameter outDevCardParam)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result ForceErase()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public void PresetInternalKeys(ReadOnlySpan gameCardKey, ReadOnlySpan gameCardCertificate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Initialize(Memory workBuffer, ulong deviceBufferAddress)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void FinalizeGc()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void PowerOffGameCard()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RegisterDeviceVirtualAddress(Memory buffer, ulong deviceBufferAddress)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void UnregisterDeviceVirtualAddress(Memory buffer, ulong deviceBufferAddress)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetInitializationResult()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result Activate()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Deactivate()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result SetCardToSecureMode()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result Read(Span destination, uint pageIndex, uint pageCount)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void PutToSleep()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Awaken()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsCardInserted()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsCardActivationValid()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetCardStatus(out GameCardStatus outStatus)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetCardDeviceId(Span destBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetCardDeviceCertificate(Span destBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result ChallengeCardExistence(Span responseBuffer, ReadOnlySpan challengeSeedBuffer,
+ ReadOnlySpan challengeValueBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetCardImageHash(Span destBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetGameCardIdSet(out GameCardIdSet outGcIdSet)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RegisterDetectionEventCallback(Action