mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add GameCardStorageCreator and GameCardFileSystemCreator
This commit is contained in:
parent
a55b1d7c58
commit
80588438c0
8 changed files with 435 additions and 20 deletions
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Diag;
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
|
@ -10,11 +11,25 @@ namespace LibHac.Fs;
|
||||||
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
|
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
|
||||||
public class MemoryStorage : IStorage
|
public class MemoryStorage : IStorage
|
||||||
{
|
{
|
||||||
private byte[] _storageBuffer;
|
private byte[] _buffer;
|
||||||
|
private int _size;
|
||||||
|
|
||||||
public MemoryStorage(byte[] buffer)
|
public MemoryStorage(byte[] buffer)
|
||||||
{
|
{
|
||||||
_storageBuffer = buffer;
|
_buffer = buffer;
|
||||||
|
_size = buffer.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryStorage(byte[] buffer, int size)
|
||||||
|
{
|
||||||
|
Assert.SdkRequiresNotNull(buffer);
|
||||||
|
Assert.SdkRequiresInRange(size, 0, buffer.Length);
|
||||||
|
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
|
Abort.DoAbortUnless(buffer is null || 0 <= size && size < buffer.Length);
|
||||||
|
|
||||||
|
_buffer = buffer;
|
||||||
|
_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Result Read(long offset, Span<byte> destination)
|
public override Result Read(long offset, Span<byte> destination)
|
||||||
|
@ -22,10 +37,10 @@ public class MemoryStorage : IStorage
|
||||||
if (destination.Length == 0)
|
if (destination.Length == 0)
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
Result res = CheckAccessRange(offset, destination.Length, _storageBuffer.Length);
|
Result res = CheckAccessRange(offset, destination.Length, _size);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
_storageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
|
_buffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -35,10 +50,10 @@ public class MemoryStorage : IStorage
|
||||||
if (source.Length == 0)
|
if (source.Length == 0)
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
Result res = CheckAccessRange(offset, source.Length, _storageBuffer.Length);
|
Result res = CheckAccessRange(offset, source.Length, _size);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
source.CopyTo(_storageBuffer.AsSpan((int)offset));
|
source.CopyTo(_buffer.AsSpan((int)offset));
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +70,7 @@ public class MemoryStorage : IStorage
|
||||||
|
|
||||||
public override Result GetSize(out long size)
|
public override Result GetSize(out long size)
|
||||||
{
|
{
|
||||||
size = _storageBuffer.Length;
|
size = _size;
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ public class EmulatedGameCardFsCreator : IGameCardFileSystemCreator
|
||||||
_gameCard = gameCard;
|
_gameCard = gameCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
|
||||||
public Result Create(ref SharedRef<IFileSystem> outFileSystem, GameCardHandle handle,
|
public Result Create(ref SharedRef<IFileSystem> outFileSystem, GameCardHandle handle,
|
||||||
GameCardPartition partitionType)
|
GameCardPartition partitionType)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,8 @@ public class EmulatedGameCardStorageCreator : IGameCardStorageCreator
|
||||||
GameCard = gameCard;
|
GameCard = gameCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
|
||||||
public Result CreateReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage)
|
public Result CreateReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage)
|
||||||
{
|
{
|
||||||
if (GameCard.IsGameCardHandleInvalid(handle))
|
if (GameCard.IsGameCardHandleInvalid(handle))
|
||||||
|
|
343
src/LibHac/FsSrv/FsCreator/GameCardFileSystemCreator.cs
Normal file
343
src/LibHac/FsSrv/FsCreator/GameCardFileSystemCreator.cs
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSrv.Storage;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Gc;
|
||||||
|
using LibHac.Os;
|
||||||
|
using LibHac.Util;
|
||||||
|
using PartitionEntry = LibHac.FsSystem.Impl.Sha256PartitionFileSystemFormat.PartitionEntry;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the root partition of a game card and handles opening the various partitions it contains.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Based on nnSdk 15.3.0 (FS 15.0.0)</remarks>
|
||||||
|
public class GameCardRootPartition : IDisposable
|
||||||
|
{
|
||||||
|
private const int LogoPartitionSizeMax = 0x12000;
|
||||||
|
private static ReadOnlySpan<byte> UpdatePartitionName => "update"u8;
|
||||||
|
private static ReadOnlySpan<byte> NormalPartitionName => "normal"u8;
|
||||||
|
private static ReadOnlySpan<byte> SecurePartitionName => "secure"u8;
|
||||||
|
private static ReadOnlySpan<byte> LogoPartitionName => "logo"u8;
|
||||||
|
private static ReadOnlySpan<byte> LogoPartitionPath => "/logo"u8;
|
||||||
|
|
||||||
|
private UniqueRef<Sha256PartitionFileSystemMeta> _partitionFsMeta;
|
||||||
|
private SharedRef<IStorage> _alignedRootStorage;
|
||||||
|
private GameCardHandle _gcHandle;
|
||||||
|
private long _metaDataSize;
|
||||||
|
private IGameCardStorageCreator _gameCardStorageCreator;
|
||||||
|
private byte[] _logoPartitionData;
|
||||||
|
private SharedRef<IStorage> _logoPartitionStorage;
|
||||||
|
private SdkMutexType _mutex;
|
||||||
|
|
||||||
|
// LibHac addition so we can access fssrv::storage functions
|
||||||
|
private readonly FileSystemServer _fsServer;
|
||||||
|
|
||||||
|
public GameCardRootPartition(GameCardHandle handle, ref SharedRef<IStorage> rootStorage,
|
||||||
|
IGameCardStorageCreator storageCreator, ref UniqueRef<Sha256PartitionFileSystemMeta> partitionFsMeta,
|
||||||
|
FileSystemServer fsServer)
|
||||||
|
{
|
||||||
|
_partitionFsMeta = new UniqueRef<Sha256PartitionFileSystemMeta>(ref partitionFsMeta);
|
||||||
|
_alignedRootStorage = SharedRef<IStorage>.CreateMove(ref rootStorage);
|
||||||
|
_gcHandle = handle;
|
||||||
|
_gameCardStorageCreator = storageCreator;
|
||||||
|
_logoPartitionStorage = new SharedRef<IStorage>();
|
||||||
|
_mutex = new SdkMutexType();
|
||||||
|
_metaDataSize = _partitionFsMeta.Get.GetMetaDataSize();
|
||||||
|
|
||||||
|
_fsServer = fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_logoPartitionStorage.Destroy();
|
||||||
|
_alignedRootStorage.Destroy();
|
||||||
|
_partitionFsMeta.Destroy();
|
||||||
|
|
||||||
|
if (_logoPartitionData is not null)
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(_logoPartitionData);
|
||||||
|
_logoPartitionData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static U8Span GetPartitionPath(GameCardPartition partitionType)
|
||||||
|
{
|
||||||
|
switch (partitionType)
|
||||||
|
{
|
||||||
|
case GameCardPartition.Update: return UpdatePartitionName;
|
||||||
|
case GameCardPartition.Normal: return NormalPartitionName;
|
||||||
|
case GameCardPartition.Secure: return SecurePartitionName;
|
||||||
|
case GameCardPartition.Logo: return LogoPartitionName;
|
||||||
|
default:
|
||||||
|
Abort.UnexpectedDefault();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
Assert.SdkNotNull(in _alignedRootStorage);
|
||||||
|
|
||||||
|
return _fsServer.Storage.IsGameCardActivationValid(_gcHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenPartition(ref SharedRef<IStorage> outStorage, out ReadOnlyRef<PartitionEntry> outEntry,
|
||||||
|
GameCardHandle handle, GameCardPartition partitionType)
|
||||||
|
{
|
||||||
|
outEntry = default;
|
||||||
|
|
||||||
|
switch (partitionType)
|
||||||
|
{
|
||||||
|
case GameCardPartition.Update:
|
||||||
|
case GameCardPartition.Normal:
|
||||||
|
case GameCardPartition.Secure:
|
||||||
|
case GameCardPartition.Logo:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ResultFs.InvalidArgument.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
int entryIndex = _partitionFsMeta.Get.GetEntryIndex(GetPartitionPath(partitionType));
|
||||||
|
if (entryIndex < 0)
|
||||||
|
return ResultFs.PartitionNotFound.Log();
|
||||||
|
|
||||||
|
ref readonly PartitionEntry entry = ref _partitionFsMeta.Get.GetEntry(entryIndex);
|
||||||
|
|
||||||
|
switch (partitionType)
|
||||||
|
{
|
||||||
|
case GameCardPartition.Update:
|
||||||
|
case GameCardPartition.Normal:
|
||||||
|
{
|
||||||
|
// The root partition contains the entire non-secure section of the game card, so we just need to make
|
||||||
|
// a SubStorage of the appropriate range.
|
||||||
|
outStorage.Reset(new SubStorage(in _alignedRootStorage, _metaDataSize + entry.Offset, entry.Size));
|
||||||
|
|
||||||
|
if (!outStorage.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorA.Log();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
case GameCardPartition.Secure:
|
||||||
|
{
|
||||||
|
Result res = EnsureLogoDataCached();
|
||||||
|
if (res.IsFailure() && !ResultFs.PartitionNotFound.Includes(res))
|
||||||
|
return res.Miss();
|
||||||
|
|
||||||
|
using var secureStorage = new SharedRef<IStorage>();
|
||||||
|
res = _gameCardStorageCreator.CreateSecureReadOnly(handle, ref secureStorage.Ref);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
const int dataAlignment = 512;
|
||||||
|
|
||||||
|
using var alignedStorage = new SharedRef<IStorage>(
|
||||||
|
new AlignmentMatchingStorageInBulkRead<AlignmentMatchingStorageSize1>(in secureStorage,
|
||||||
|
dataAlignment));
|
||||||
|
|
||||||
|
if (!alignedStorage.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorB.Log();
|
||||||
|
|
||||||
|
outStorage.SetByMove(ref alignedStorage.Ref);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
case GameCardPartition.Logo:
|
||||||
|
{
|
||||||
|
Result res = EnsureLogoDataCached();
|
||||||
|
if (res.IsFailure() && !ResultFs.PartitionNotFound.Includes(res))
|
||||||
|
return res.Miss();
|
||||||
|
|
||||||
|
outStorage.SetByCopy(in _logoPartitionStorage);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultFs.PartitionNotFound.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result EnsureLogoDataCached()
|
||||||
|
{
|
||||||
|
int entryIndex = _partitionFsMeta.Get.GetEntryIndex(GetPartitionPath(GameCardPartition.Logo));
|
||||||
|
if (entryIndex < 0)
|
||||||
|
return ResultFs.PartitionNotFound.Log();
|
||||||
|
|
||||||
|
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
if (_logoPartitionStorage.HasValue)
|
||||||
|
return Result.Success;
|
||||||
|
|
||||||
|
ref readonly PartitionEntry logoEntry = ref _partitionFsMeta.Get.GetEntry(entryIndex);
|
||||||
|
if (logoEntry.Size > LogoPartitionSizeMax)
|
||||||
|
return ResultFs.GameCardLogoDataTooLarge.Log();
|
||||||
|
|
||||||
|
using var rootPartitionFs = new Sha256PartitionFileSystem();
|
||||||
|
Result res = rootPartitionFs.Initialize(_partitionFsMeta.Get, in _alignedRootStorage);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
using var file = new UniqueRef<IFile>();
|
||||||
|
|
||||||
|
using var pathLogo = new Path();
|
||||||
|
res = PathFunctions.SetUpFixedPath(ref pathLogo.Ref(), LogoPartitionPath);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
res = rootPartitionFs.OpenFile(ref file.Ref, in pathLogo, OpenMode.Read);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
_logoPartitionData = ArrayPool<byte>.Shared.Rent(LogoPartitionSizeMax);
|
||||||
|
|
||||||
|
res = file.Get.Read(out long readSize, offset: 0, _logoPartitionData.AsSpan(0, LogoPartitionSizeMax),
|
||||||
|
ReadOption.None);
|
||||||
|
if (ResultFs.DataCorrupted.Includes(res))
|
||||||
|
return ResultFs.GameCardLogoDataCorrupted.LogConverted(res);
|
||||||
|
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
if (readSize != logoEntry.Size)
|
||||||
|
return ResultFs.GameCardLogoDataSizeInvalid.Log();
|
||||||
|
|
||||||
|
_logoPartitionStorage.Reset(new MemoryStorage(_logoPartitionData, (int)logoEntry.Size));
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates <see cref="IFileSystem"/>s of the various partitions contained by the currently mounted game card.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Based on nnSdk 15.3.0 (FS 15.0.0)</remarks>
|
||||||
|
public class GameCardFileSystemCreator : IGameCardFileSystemCreator
|
||||||
|
{
|
||||||
|
private MemoryResource _allocator;
|
||||||
|
private GameCardStorageCreator _gameCardStorageCreator;
|
||||||
|
private UniqueRef<GameCardRootPartition> _rootPartition;
|
||||||
|
private SdkMutexType _mutex;
|
||||||
|
|
||||||
|
// LibHac addition so we can access fssrv::storage functions
|
||||||
|
private readonly FileSystemServer _fsServer;
|
||||||
|
|
||||||
|
public GameCardFileSystemCreator(MemoryResource allocator, GameCardStorageCreator gameCardStorageCreator,
|
||||||
|
FileSystemServer fsServer)
|
||||||
|
{
|
||||||
|
_allocator = allocator;
|
||||||
|
_gameCardStorageCreator = gameCardStorageCreator;
|
||||||
|
_rootPartition = new UniqueRef<GameCardRootPartition>();
|
||||||
|
_mutex = new SdkMutexType();
|
||||||
|
|
||||||
|
_fsServer = fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_rootPartition.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Create(ref SharedRef<IFileSystem> outFileSystem, GameCardHandle handle, GameCardPartition partitionType)
|
||||||
|
{
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
using (ScopedLock.Lock(ref _mutex))
|
||||||
|
{
|
||||||
|
// Initialize the root partition if not already initialized.
|
||||||
|
if (!_rootPartition.HasValue || !_rootPartition.Get.IsValid())
|
||||||
|
{
|
||||||
|
// Open an IStorage of the game card's normal area.
|
||||||
|
using var rootStorage = new SharedRef<IStorage>();
|
||||||
|
res = _gameCardStorageCreator.CreateReadOnly(handle, ref rootStorage.Ref);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
// Make sure reads to the game card are aligned to the game card's sector size.
|
||||||
|
const int dataAlignment = 512;
|
||||||
|
using var alignedRootStorage = new SharedRef<IStorage>(
|
||||||
|
new AlignmentMatchingStorageInBulkRead<AlignmentMatchingStorageSize1>(in rootStorage, dataAlignment));
|
||||||
|
|
||||||
|
if (!alignedRootStorage.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorC.Log();
|
||||||
|
|
||||||
|
res = _fsServer.Storage.GetGameCardStatus(out GameCardStatus status, handle);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
// Get an IStorage of the start of the root partition to the start of the secure area.
|
||||||
|
long updateAndNormalPartitionSize = status.SecureAreaOffset - status.PartitionFsHeaderOffset;
|
||||||
|
using var rootFsStorage = new SharedRef<IStorage>(new SubStorage(in alignedRootStorage,
|
||||||
|
status.PartitionFsHeaderOffset, updateAndNormalPartitionSize));
|
||||||
|
|
||||||
|
if (!rootFsStorage.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorD.Log();
|
||||||
|
|
||||||
|
// Initialize a reader for the root partition.
|
||||||
|
using var rootPartitionFsMeta = new UniqueRef<Sha256PartitionFileSystemMeta>(new Sha256PartitionFileSystemMeta());
|
||||||
|
if (!rootPartitionFsMeta.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorG.Log();
|
||||||
|
|
||||||
|
res = GetSaltFromCompatibilityType(out Optional<byte> salt, status.CompatibilityType);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
res = rootPartitionFsMeta.Get.Initialize(rootFsStorage.Get, _allocator, status.PartitionFsHeaderHash, salt);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
_rootPartition.Reset(new GameCardRootPartition(handle, ref rootFsStorage.Ref, _gameCardStorageCreator,
|
||||||
|
ref rootPartitionFsMeta.Ref, _fsServer));
|
||||||
|
|
||||||
|
if (!_rootPartition.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorE.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the raw storage of the requested partition.
|
||||||
|
using var partitionStorage = new SharedRef<IStorage>();
|
||||||
|
res = _rootPartition.Get.OpenPartition(ref partitionStorage.Ref, out ReadOnlyRef<PartitionEntry> refEntry,
|
||||||
|
handle, partitionType);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
// Initialize a Sha256PartitionFileSystem for reading the partition's file system.
|
||||||
|
using var partitionFsMeta = new UniqueRef<Sha256PartitionFileSystemMeta>(new Sha256PartitionFileSystemMeta());
|
||||||
|
if (!partitionFsMeta.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorH.Log();
|
||||||
|
|
||||||
|
if (partitionType == GameCardPartition.Logo)
|
||||||
|
{
|
||||||
|
res = partitionFsMeta.Get.Initialize(partitionStorage.Get, _allocator);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = partitionFsMeta.Get.Initialize(partitionStorage.Get, _allocator, refEntry.Value.Hash);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
|
res = Sha256PartitionFileSystemMeta.QueryMetaDataSize(out _, partitionStorage.Get);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
using var fs = new SharedRef<Sha256PartitionFileSystem>(new Sha256PartitionFileSystem());
|
||||||
|
if (!fs.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInGameCardFileSystemCreatorF.Log();
|
||||||
|
|
||||||
|
res = fs.Get.Initialize(ref partitionFsMeta.Ref, in partitionStorage);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
outFileSystem.SetByMove(ref fs.Ref);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result GetSaltFromCompatibilityType(out Optional<byte> outSalt, byte compatibilityType)
|
||||||
|
{
|
||||||
|
switch ((GameCardCompatibilityType)compatibilityType)
|
||||||
|
{
|
||||||
|
case GameCardCompatibilityType.Normal:
|
||||||
|
outSalt = default;
|
||||||
|
break;
|
||||||
|
case GameCardCompatibilityType.Terra:
|
||||||
|
outSalt = new Optional<byte>(compatibilityType);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
outSalt = default;
|
||||||
|
return ResultFs.GameCardFsInvalidCompatibilityType.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
38
src/LibHac/FsSrv/FsCreator/GameCardStorageCreator.cs
Normal file
38
src/LibHac/FsSrv/FsCreator/GameCardStorageCreator.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSrv.Storage;
|
||||||
|
using LibHac.GcSrv;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates <see cref="IStorage"/>s to the currently mounted game card.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Based on nnSdk 15.3.0 (FS 15.0.0)</remarks>
|
||||||
|
public class GameCardStorageCreator : IGameCardStorageCreator
|
||||||
|
{
|
||||||
|
// LibHac addition so we can access fssrv::storage functions
|
||||||
|
private readonly FileSystemServer _fsServer;
|
||||||
|
|
||||||
|
public GameCardStorageCreator(FileSystemServer fsServer)
|
||||||
|
{
|
||||||
|
_fsServer = fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public Result CreateReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage)
|
||||||
|
{
|
||||||
|
return _fsServer.Storage.OpenGameCardStorage(ref outStorage, OpenGameCardAttribute.ReadOnly, handle).Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CreateSecureReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage)
|
||||||
|
{
|
||||||
|
return _fsServer.Storage.OpenGameCardStorage(ref outStorage, OpenGameCardAttribute.SecureReadOnly, handle).Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CreateWriteOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage)
|
||||||
|
{
|
||||||
|
return _fsServer.Storage.OpenGameCardStorage(ref outStorage, OpenGameCardAttribute.WriteOnly, handle).Ret();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
using LibHac.Common;
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.FsCreator;
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
public interface IGameCardFileSystemCreator
|
public interface IGameCardFileSystemCreator : IDisposable
|
||||||
{
|
{
|
||||||
Result Create(ref SharedRef<IFileSystem> outFileSystem, GameCardHandle handle, GameCardPartition partitionType);
|
Result Create(ref SharedRef<IFileSystem> outFileSystem, GameCardHandle handle, GameCardPartition partitionType);
|
||||||
}
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
using LibHac.Common;
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.FsCreator;
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
public interface IGameCardStorageCreator
|
public interface IGameCardStorageCreator : IDisposable
|
||||||
{
|
{
|
||||||
Result CreateReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage);
|
Result CreateReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage);
|
||||||
Result CreateSecureReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage);
|
Result CreateSecureReadOnly(GameCardHandle handle, ref SharedRef<IStorage> outStorage);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
@ -8,11 +7,25 @@ using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
namespace LibHac.FsSystem;
|
||||||
|
|
||||||
public interface IAlignmentMatchingStorageSize { }
|
public interface IAlignmentMatchingStorageSize
|
||||||
|
{
|
||||||
|
static abstract uint Alignment { get; }
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 1)] public struct AlignmentMatchingStorageSize1 : IAlignmentMatchingStorageSize { }
|
public struct AlignmentMatchingStorageSize1 : IAlignmentMatchingStorageSize
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 16)] public struct AlignmentMatchingStorageSize16 : IAlignmentMatchingStorageSize { }
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 512)] public struct AlignmentMatchingStorageSize512 : IAlignmentMatchingStorageSize { }
|
public static uint Alignment => 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct AlignmentMatchingStorageSize16 : IAlignmentMatchingStorageSize
|
||||||
|
{
|
||||||
|
public static uint Alignment => 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct AlignmentMatchingStorageSize512 : IAlignmentMatchingStorageSize
|
||||||
|
{
|
||||||
|
public static uint Alignment => 512;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles accessing a base <see cref="IStorage"/> that must always be accessed via an aligned offset and size.
|
/// Handles accessing a base <see cref="IStorage"/> that must always be accessed via an aligned offset and size.
|
||||||
|
@ -29,8 +42,8 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
|
||||||
where TDataAlignment : struct, IAlignmentMatchingStorageSize
|
where TDataAlignment : struct, IAlignmentMatchingStorageSize
|
||||||
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
||||||
{
|
{
|
||||||
public static uint DataAlign => (uint)Unsafe.SizeOf<TDataAlignment>();
|
public static uint DataAlign => TDataAlignment.Alignment;
|
||||||
public static uint BufferAlign => (uint)Unsafe.SizeOf<TBufferAlignment>();
|
public static uint BufferAlign => TBufferAlignment.Alignment;
|
||||||
|
|
||||||
public static uint DataAlignMax => 0x200;
|
public static uint DataAlignMax => 0x200;
|
||||||
|
|
||||||
|
@ -170,7 +183,7 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
|
||||||
public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
||||||
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
||||||
{
|
{
|
||||||
public static uint BufferAlign => (uint)Unsafe.SizeOf<TBufferAlignment>();
|
public static uint BufferAlign => TBufferAlignment.Alignment;
|
||||||
|
|
||||||
private IStorage _baseStorage;
|
private IStorage _baseStorage;
|
||||||
private long _baseStorageSize;
|
private long _baseStorageSize;
|
||||||
|
@ -314,7 +327,7 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
||||||
public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
|
public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
|
||||||
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
||||||
{
|
{
|
||||||
public static uint BufferAlign => (uint)Unsafe.SizeOf<TBufferAlignment>();
|
public static uint BufferAlign => TBufferAlignment.Alignment;
|
||||||
|
|
||||||
private IStorage _baseStorage;
|
private IStorage _baseStorage;
|
||||||
private SharedRef<IStorage> _sharedBaseStorage;
|
private SharedRef<IStorage> _sharedBaseStorage;
|
||||||
|
|
Loading…
Reference in a new issue