diff --git a/src/LibHac/FsSrv/Impl/ProgramIndexMapInfoManager.cs b/src/LibHac/FsSrv/Impl/ProgramIndexMapInfoManager.cs
new file mode 100644
index 00000000..f18c9424
--- /dev/null
+++ b/src/LibHac/FsSrv/Impl/ProgramIndexMapInfoManager.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using LibHac.Fs;
+using LibHac.Ncm;
+using LibHac.Os;
+using LibHac.Util;
+
+namespace LibHac.FsSrv.Impl;
+
+///
+/// Keeps track of the program IDs and program indexes of each program in a multi-program application.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
+public class ProgramIndexMapInfoManager : IDisposable
+{
+ private LinkedList _mapEntries;
+ private SdkMutexType _mutex;
+
+ public ProgramIndexMapInfoManager()
+ {
+ _mapEntries = new LinkedList();
+ _mutex = new SdkMutexType();
+ }
+
+ public void Dispose()
+ {
+ Clear();
+ }
+
+ ///
+ /// Unregisters any previously registered program index map info and registers the provided map info.
+ ///
+ /// The program map info entries to register.
+ /// : The operation was successful.
+ public Result Reset(ReadOnlySpan programIndexMapInfo)
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ ClearImpl();
+
+ for (int i = 0; i < programIndexMapInfo.Length; i++)
+ {
+ var entry = new ProgramIndexMapInfo
+ {
+ ProgramId = programIndexMapInfo[i].ProgramId,
+ MainProgramId = programIndexMapInfo[i].MainProgramId,
+ ProgramIndex = programIndexMapInfo[i].ProgramIndex
+ };
+
+ _mapEntries.AddLast(entry);
+ }
+
+ // We skip running ClearImpl() if the allocation failed because we don't need to worry about that in C#
+
+ return Result.Success;
+
+ }
+
+ ///
+ /// Unregisters all currently registered program index map info.
+ ///
+ public void Clear()
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ ClearImpl();
+ }
+
+ ///
+ /// Gets the associated with the specified program ID.
+ ///
+ /// The program ID of the map info to get.
+ /// If the program ID was found, the associated
+ /// with that ID; otherwise, .
+ public Optional Get(ProgramId programId)
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ return GetImpl((in ProgramIndexMapInfo x) => x.ProgramId == programId);
+ }
+
+ ///
+ /// Gets the of the program with index in the
+ /// multi-program app is part of.
+ ///
+ /// A program ID in the multi-program app to query.
+ /// The index of the program to get.
+ /// If the program exists, the ID of the program with the specified index,
+ /// otherwise
+ public ProgramId GetProgramId(ProgramId programId, byte programIndex)
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ Optional programIndexMapInfo =
+ GetImpl((in ProgramIndexMapInfo x) => x.ProgramId == programId);
+
+ if (!programIndexMapInfo.HasValue)
+ return ProgramId.InvalidId;
+
+ Optional targetProgramIndexMapInfo = GetImpl((in ProgramIndexMapInfo x) =>
+ x.MainProgramId == programIndexMapInfo.Value.MainProgramId && x.ProgramIndex == programIndex);
+
+ if (!targetProgramIndexMapInfo.HasValue)
+ return ProgramId.InvalidId;
+
+ return targetProgramIndexMapInfo.Value.ProgramId;
+ }
+
+ ///
+ /// Gets the number of currently registered programs,
+ ///
+ /// The number of registered programs.
+ public int GetProgramCount()
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ return _mapEntries.Count;
+ }
+
+ private delegate bool EntrySelector(in ProgramIndexMapInfo candidate);
+
+ private Optional GetImpl(EntrySelector selector)
+ {
+ var returnValue = new Optional();
+
+ foreach (ProgramIndexMapInfo entry in _mapEntries)
+ {
+ if (selector(in entry))
+ {
+ returnValue.Set(default);
+
+ returnValue.Value.ProgramId = entry.ProgramId;
+ returnValue.Value.MainProgramId = entry.MainProgramId;
+ returnValue.Value.ProgramIndex = entry.ProgramIndex;
+
+ break;
+ }
+ }
+
+ return returnValue;
+ }
+
+ private void ClearImpl()
+ {
+ _mapEntries.Clear();
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/ProgramIndexMapInfoManager.cs b/src/LibHac/FsSrv/ProgramIndexMapInfoManager.cs
deleted file mode 100644
index 2015b761..00000000
--- a/src/LibHac/FsSrv/ProgramIndexMapInfoManager.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using System;
-using System.Collections.Generic;
-using LibHac.Fs;
-using LibHac.Ncm;
-using LibHac.Util;
-
-namespace LibHac.FsSrv;
-
-///
-/// Keeps track of the program IDs and program indexes of each program in a multi-program application.
-///
-/// Based on FS 10.0.0 (nnSdk 10.4.0)
-public class ProgramIndexMapInfoManager
-{
- private LinkedList MapEntries { get; } = new LinkedList();
-
- ///
- /// Unregisters any previously registered program index map info and registers the provided map info.
- ///
- /// The program map info entries to register.
- /// : The operation was successful.
- public Result Reset(ReadOnlySpan programIndexMapInfo)
- {
- lock (MapEntries)
- {
- ClearImpl();
-
- for (int i = 0; i < programIndexMapInfo.Length; i++)
- {
- MapEntries.AddLast(programIndexMapInfo[i]);
- }
-
- return Result.Success;
- }
- }
-
- ///
- /// Unregisters all currently registered program index map info.
- ///
- public void Clear()
- {
- lock (MapEntries)
- {
- ClearImpl();
- }
- }
-
- ///
- /// Gets the associated with the specified program ID.
- ///
- /// The program ID of the map info to get.
- /// If the program ID was found, the associated
- /// with that ID; otherwise, .
- public Optional Get(ProgramId programId)
- {
- lock (MapEntries)
- {
- return GetImpl((in ProgramIndexMapInfo x) => x.ProgramId == programId);
- }
- }
-
- ///
- /// Gets the of the program with index in the
- /// multi-program app is part of.
- ///
- /// A program ID in the multi-program app to query.
- /// The index of the program to get.
- /// If the program exists, the ID of the program with the specified index,
- /// otherwise
- public ProgramId GetProgramId(ProgramId programId, byte programIndex)
- {
- lock (MapEntries)
- {
- Optional mainProgramMapInfo =
- GetImpl((in ProgramIndexMapInfo x) => x.ProgramId == programId);
-
- if (!mainProgramMapInfo.HasValue)
- return ProgramId.InvalidId;
-
- Optional requestedMapInfo = GetImpl((in ProgramIndexMapInfo x) =>
- x.MainProgramId == mainProgramMapInfo.Value.MainProgramId && x.ProgramIndex == programIndex);
-
- if (!requestedMapInfo.HasValue)
- return ProgramId.InvalidId;
-
- return requestedMapInfo.Value.ProgramId;
- }
- }
-
- ///
- /// Gets the number of currently registered programs,
- ///
- /// The number of registered programs.
- public int GetProgramCount()
- {
- lock (MapEntries)
- {
- return MapEntries.Count;
- }
- }
-
- private delegate bool EntrySelector(in ProgramIndexMapInfo candidate);
-
- private Optional GetImpl(EntrySelector selector)
- {
- foreach (ProgramIndexMapInfo entry in MapEntries)
- {
- if (selector(in entry))
- {
- return new Optional(entry);
- }
- }
-
- return new Optional();
- }
-
- private void ClearImpl()
- {
- MapEntries.Clear();
- }
-}
diff --git a/src/LibHac/Util/Optional.cs b/src/LibHac/Util/Optional.cs
index f9d8ac7d..b73bbbef 100644
--- a/src/LibHac/Util/Optional.cs
+++ b/src/LibHac/Util/Optional.cs
@@ -1,4 +1,5 @@
-using System.Runtime.InteropServices;
+using System;
+using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Diag;
@@ -36,6 +37,12 @@ public struct Optional
public static implicit operator Optional(in T value) => new Optional(in value);
+ public void Set(T value)
+ {
+ _value = value;
+ _hasValue = true;
+ }
+
public void Set(in T value)
{
_value = value;
@@ -47,4 +54,4 @@ public struct Optional
_hasValue = false;
_value = default;
}
-}
+}
\ No newline at end of file
diff --git a/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs b/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs
index 55790e32..afa913b6 100644
--- a/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs
+++ b/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs
@@ -2,7 +2,6 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Shim;
-using LibHac.FsSrv;
using LibHac.FsSrv.Impl;
using LibHac.Ncm;
using LibHac.Util;
@@ -194,4 +193,4 @@ public class ProgramIndexMapInfoTests
Assert.False(manager.Get(new ProgramId(2)).HasValue);
}
-}
+}
\ No newline at end of file