mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add an emulated gamecard and storage creator
This commit is contained in:
parent
9934f477d5
commit
51a13068df
14 changed files with 498 additions and 55 deletions
79
src/LibHac/Fs/GameCard.cs
Normal file
79
src/LibHac/Fs/GameCard.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using LibHac.FsService;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public static class GameCard
|
||||
{
|
||||
public static Result OpenGameCardPartition(this FileSystemClient fs, out IStorage storage,
|
||||
GameCardHandle handle, GameCardPartitionRaw partitionType)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
return fsProxy.OpenGameCardStorage(out storage, handle, partitionType);
|
||||
}
|
||||
|
||||
public static Result GetGameCardHandle(this FileSystemClient fs, out GameCardHandle handle)
|
||||
{
|
||||
handle = default;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return deviceOperator.GetGameCardHandle(out handle);
|
||||
}
|
||||
|
||||
public static bool IsGameCardInserted(this FileSystemClient fs)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||
if (rc.IsFailure()) throw new LibHacException("Abort");
|
||||
|
||||
rc = deviceOperator.IsGameCardInserted(out bool isInserted);
|
||||
if (rc.IsFailure()) throw new LibHacException("Abort");
|
||||
|
||||
return isInserted;
|
||||
}
|
||||
|
||||
public static long GetGameCardSizeBytes(GameCardSize size)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case GameCardSize.Size1Gb: return 0x3B800000;
|
||||
case GameCardSize.Size2Gb: return 0x77000000;
|
||||
case GameCardSize.Size4Gb: return 0xEE000000;
|
||||
case GameCardSize.Size8Gb: return 0x1DC000000;
|
||||
case GameCardSize.Size16Gb: return 0x3B8000000;
|
||||
case GameCardSize.Size32Gb: return 0x770000000;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(size), size, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static long CardPageToOffset(int page)
|
||||
{
|
||||
return (long)page << 9;
|
||||
}
|
||||
}
|
||||
|
||||
public enum GameCardSize
|
||||
{
|
||||
Size1Gb = 0xFA,
|
||||
Size2Gb = 0xF8,
|
||||
Size4Gb = 0xF0,
|
||||
Size8Gb = 0xE0,
|
||||
Size16Gb = 0xE1,
|
||||
Size32Gb = 0xE2
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum GameCardAttribute : byte
|
||||
{
|
||||
AutoBoot = 1 << 0,
|
||||
HistoryErase = 1 << 1,
|
||||
RepairTool = 1 << 2
|
||||
}
|
||||
}
|
|
@ -15,6 +15,15 @@
|
|||
public static Result TargetNotFound => new Result(ModuleFs, 1002);
|
||||
public static Result ExternalKeyNotFound => new Result(ModuleFs, 1004);
|
||||
|
||||
public static Result InvalidBufferForGameCard => new Result(ModuleFs, 2503);
|
||||
public static Result GameCardNotInserted => new Result(ModuleFs, 2520);
|
||||
|
||||
public static Result GameCardNotInsertedOnGetHandle => new Result(ModuleFs, 2951);
|
||||
public static Result InvalidGameCardHandleOnRead => new Result(ModuleFs, 2952);
|
||||
public static Result InvalidGameCardHandleOnGetCardInfo => new Result(ModuleFs, 2954);
|
||||
public static Result InvalidGameCardHandleOnOpenNormalPartition => new Result(ModuleFs, 2960);
|
||||
public static Result InvalidGameCardHandleOnOpenSecurePartition => new Result(ModuleFs, 2961);
|
||||
|
||||
public static Result NotImplemented => new Result(ModuleFs, 3001);
|
||||
public static Result Result3002 => new Result(ModuleFs, 3002);
|
||||
public static Result SaveDataPathAlreadyExists => new Result(ModuleFs, 3003);
|
||||
|
@ -98,6 +107,8 @@
|
|||
public static Result UnsupportedOperationInAesCtrExStorageWrite => new Result(ModuleFs, 6310);
|
||||
public static Result UnsupportedOperationInIndirectStorageWrite => new Result(ModuleFs, 6324);
|
||||
public static Result UnsupportedOperationInIndirectStorageSetSize => new Result(ModuleFs, 6325);
|
||||
public static Result UnsupportedOperationInRoGameCardStorageWrite => new Result(ModuleFs, 6350);
|
||||
public static Result UnsupportedOperationInRoGameCardStorageSetSize => new Result(ModuleFs, 6351);
|
||||
public static Result UnsupportedOperationInConcatFsQueryEntry => new Result(ModuleFs, 6359);
|
||||
public static Result UnsupportedOperationModifyRomFsFileSystem => new Result(ModuleFs, 6364);
|
||||
public static Result UnsupportedOperationRomFsFileSystemGetSpace => new Result(ModuleFs, 6366);
|
||||
|
|
125
src/LibHac/FsService/Creators/EmulatedGameCardStorageCreator.cs
Normal file
125
src/LibHac/FsService/Creators/EmulatedGameCardStorageCreator.cs
Normal file
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService.Creators
|
||||
{
|
||||
class EmulatedGameCardStorageCreator : IGameCardStorageCreator
|
||||
{
|
||||
private EmulatedGameCard GameCard { get; }
|
||||
|
||||
public EmulatedGameCardStorageCreator(EmulatedGameCard gameCard)
|
||||
{
|
||||
GameCard = gameCard;
|
||||
}
|
||||
|
||||
public Result CreateNormal(GameCardHandle handle, out IStorage storage)
|
||||
{
|
||||
storage = default;
|
||||
|
||||
if (GameCard.IsGameCardHandleInvalid(handle))
|
||||
{
|
||||
return ResultFs.InvalidGameCardHandleOnOpenNormalPartition.Log();
|
||||
}
|
||||
|
||||
var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle);
|
||||
|
||||
Result rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
storage = new SubStorage2(baseStorage, 0, cardInfo.SecureAreaOffset);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateSecure(GameCardHandle handle, out IStorage storage)
|
||||
{
|
||||
storage = default;
|
||||
|
||||
if (GameCard.IsGameCardHandleInvalid(handle))
|
||||
{
|
||||
return ResultFs.InvalidGameCardHandleOnOpenSecurePartition.Log();
|
||||
}
|
||||
|
||||
Span<byte> deviceId = stackalloc byte[0x10];
|
||||
Span<byte> imageHash = stackalloc byte[0x20];
|
||||
|
||||
Result rc = GameCard.GetGameCardDeviceId(deviceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GameCard.GetGameCardImageHash(imageHash);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle, deviceId, imageHash);
|
||||
|
||||
rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
storage = new SubStorage2(baseStorage, cardInfo.SecureAreaOffset, cardInfo.SecureAreaSize);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateWritable(GameCardHandle handle, out IStorage storage)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class ReadOnlyGameCardStorage : StorageBase
|
||||
{
|
||||
private EmulatedGameCard GameCard { get; }
|
||||
private GameCardHandle Handle { get; set; }
|
||||
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
private bool IsSecureMode { get; }
|
||||
private byte[] DeviceId { get; } = new byte[0x10];
|
||||
private byte[] ImageHash { get; } = new byte[0x20];
|
||||
|
||||
public ReadOnlyGameCardStorage(EmulatedGameCard gameCard, GameCardHandle handle)
|
||||
{
|
||||
GameCard = gameCard;
|
||||
Handle = handle;
|
||||
}
|
||||
|
||||
public ReadOnlyGameCardStorage(EmulatedGameCard gameCard, GameCardHandle handle, ReadOnlySpan<byte> deviceId, ReadOnlySpan<byte> imageHash)
|
||||
{
|
||||
GameCard = gameCard;
|
||||
Handle = handle;
|
||||
IsSecureMode = true;
|
||||
deviceId.CopyTo(DeviceId);
|
||||
imageHash.CopyTo(ImageHash);
|
||||
}
|
||||
|
||||
public override Result Read(long offset, Span<byte> destination)
|
||||
{
|
||||
// In secure mode, if Handle is old and the card's device ID and
|
||||
// header hash are still the same, Handle is updated to the new handle
|
||||
|
||||
return GameCard.Read(Handle, offset, destination);
|
||||
}
|
||||
|
||||
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
||||
{
|
||||
return ResultFs.UnsupportedOperationInRoGameCardStorageWrite.Log();
|
||||
}
|
||||
|
||||
public override Result Flush()
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public override Result SetSize(long size)
|
||||
{
|
||||
return ResultFs.UnsupportedOperationInRoGameCardStorageSetSize.Log();
|
||||
}
|
||||
|
||||
public override Result GetSize(out long size)
|
||||
{
|
||||
size = 0;
|
||||
|
||||
Result rc = GameCard.GetCardInfo(out GameCardInfo info, Handle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
size = info.Size;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,17 +21,24 @@ namespace LibHac.FsService.Creators
|
|||
public IBuiltInStorageFileSystemCreator BuiltInStorageFileSystemCreator { get; set; }
|
||||
public ISdFileSystemCreator SdFileSystemCreator { get; set; }
|
||||
|
||||
public static FileSystemCreators GetDefaultEmulatedCreators(IFileSystem rootFileSystem, Keyset keyset)
|
||||
public IDeviceOperator DeviceOperator { get; set; }
|
||||
|
||||
public static (FileSystemCreators fsCreators, EmulatedGameCard gameCard) GetDefaultEmulatedCreators(
|
||||
IFileSystem rootFileSystem, Keyset keyset)
|
||||
{
|
||||
var creators = new FileSystemCreators();
|
||||
var gameCard = new EmulatedGameCard();
|
||||
|
||||
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
|
||||
creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(keyset);
|
||||
creators.GameCardStorageCreator = new EmulatedGameCardStorageCreator(gameCard);
|
||||
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset);
|
||||
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem);
|
||||
creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(rootFileSystem);
|
||||
|
||||
return creators;
|
||||
creators.DeviceOperator = new EmulatedDeviceOperator(gameCard);
|
||||
|
||||
return (creators, gameCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
38
src/LibHac/FsService/EmulatedDeviceOperator.cs
Normal file
38
src/LibHac/FsService/EmulatedDeviceOperator.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
class EmulatedDeviceOperator : IDeviceOperator
|
||||
{
|
||||
private EmulatedGameCard GameCard { get; set; }
|
||||
|
||||
public EmulatedDeviceOperator(EmulatedGameCard gameCard)
|
||||
{
|
||||
GameCard = gameCard;
|
||||
}
|
||||
|
||||
public Result IsSdCardInserted(out bool isInserted)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result IsGameCardInserted(out bool isInserted)
|
||||
{
|
||||
isInserted = GameCard.IsGameCardInserted();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetGameCardHandle(out GameCardHandle handle)
|
||||
{
|
||||
if (!GameCard.IsGameCardInserted())
|
||||
{
|
||||
handle = default;
|
||||
return ResultFs.GameCardNotInsertedOnGetHandle.Log();
|
||||
}
|
||||
|
||||
handle = GameCard.GetGameCardHandle();
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
103
src/LibHac/FsService/EmulatedGameCard.cs
Normal file
103
src/LibHac/FsService/EmulatedGameCard.cs
Normal file
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public class EmulatedGameCard
|
||||
{
|
||||
private IStorage CardImageStorage { get; set; }
|
||||
private int Handle { get; set; }
|
||||
private XciHeader CardHeader { get; set; }
|
||||
|
||||
public GameCardHandle GetGameCardHandle()
|
||||
{
|
||||
return new GameCardHandle(Handle);
|
||||
}
|
||||
|
||||
public bool IsGameCardHandleInvalid(GameCardHandle handle)
|
||||
{
|
||||
return Handle != handle.Value;
|
||||
}
|
||||
|
||||
public bool IsGameCardInserted()
|
||||
{
|
||||
return CardImageStorage != null;
|
||||
}
|
||||
|
||||
public void InsertGameCard(IStorage cardImageStorage)
|
||||
{
|
||||
RemoveGameCard();
|
||||
|
||||
CardImageStorage = cardImageStorage;
|
||||
|
||||
CardHeader = new XciHeader(null, cardImageStorage.AsStream());
|
||||
}
|
||||
|
||||
public void RemoveGameCard()
|
||||
{
|
||||
if (IsGameCardInserted())
|
||||
{
|
||||
CardImageStorage = null;
|
||||
Handle++;
|
||||
}
|
||||
}
|
||||
|
||||
public Result Read(GameCardHandle handle, long offset, Span<byte> destination)
|
||||
{
|
||||
if (IsGameCardHandleInvalid(handle)) return ResultFs.InvalidGameCardHandleOnRead.Log();
|
||||
if (!IsGameCardInserted()) return ResultFs.GameCardNotInserted.Log();
|
||||
|
||||
return CardImageStorage.Read(offset, destination);
|
||||
}
|
||||
|
||||
public Result GetGameCardImageHash(Span<byte> outBuffer)
|
||||
{
|
||||
if (outBuffer.Length < 0x20) return ResultFs.InvalidBufferForGameCard.Log();
|
||||
if (!IsGameCardInserted()) return ResultFs.GameCardNotInserted.Log();
|
||||
|
||||
CardHeader.ImageHash.CopyTo(outBuffer.Slice(0, 0x20));
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetGameCardDeviceId(Span<byte> outBuffer)
|
||||
{
|
||||
if (outBuffer.Length < 0x10) return ResultFs.InvalidBufferForGameCard.Log();
|
||||
if (!IsGameCardInserted()) return ResultFs.GameCardNotInserted.Log();
|
||||
|
||||
// Skip the security mode check
|
||||
|
||||
// Instead of caching the CardKeyArea data, read the value directly
|
||||
return CardImageStorage.Read(0x7110, outBuffer.Slice(0, 0x10));
|
||||
}
|
||||
|
||||
internal Result GetCardInfo(out GameCardInfo cardInfo, GameCardHandle handle)
|
||||
{
|
||||
cardInfo = default;
|
||||
|
||||
if (IsGameCardHandleInvalid(handle)) return ResultFs.InvalidGameCardHandleOnGetCardInfo.Log();
|
||||
if (!IsGameCardInserted()) return ResultFs.GameCardNotInserted.Log();
|
||||
|
||||
cardInfo = GetCardInfoImpl();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private GameCardInfo GetCardInfoImpl()
|
||||
{
|
||||
var info = new GameCardInfo();
|
||||
|
||||
CardHeader.RootPartitionHeaderHash.AsSpan().CopyTo(info.RootPartitionHeaderHash);
|
||||
info.PackageId = CardHeader.PackageId;
|
||||
info.Size = GameCard.GetGameCardSizeBytes(CardHeader.GameCardSize);
|
||||
info.RootPartitionOffset = CardHeader.RootPartitionOffset;
|
||||
info.RootPartitionHeaderSize = CardHeader.RootPartitionHeaderSize;
|
||||
info.SecureAreaOffset = GameCard.CardPageToOffset(CardHeader.LimAreaPage);
|
||||
info.SecureAreaSize = info.Size - info.SecureAreaOffset;
|
||||
info.UpdateVersion = CardHeader.UppVersion;
|
||||
info.UpdateTitleId = CardHeader.UppId;
|
||||
info.Attribute = CardHeader.Flags;
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -302,12 +302,16 @@ namespace LibHac.FsService
|
|||
|
||||
public Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Missing permission check and StorageInterfaceAdapter
|
||||
|
||||
return FsProxyCore.OpenGameCardStorage(out storage, handle, partitionId);
|
||||
}
|
||||
|
||||
public Result OpenDeviceOperator(out IDeviceOperator deviceOperator)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Missing permission check
|
||||
|
||||
return FsProxyCore.OpenDeviceOperator(out deviceOperator);
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReader(out ISaveDataInfoReader infoReader)
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace LibHac.FsService
|
|||
{
|
||||
private FileSystemCreators FsCreators { get; }
|
||||
private ExternalKeySet ExternalKeys { get; }
|
||||
private IDeviceOperator DeviceOperator { get; }
|
||||
|
||||
private byte[] SdEncryptionSeed { get; } = new byte[0x10];
|
||||
|
||||
private const string NintendoDirectoryName = "Nintendo";
|
||||
|
@ -18,10 +20,11 @@ namespace LibHac.FsService
|
|||
|
||||
private GlobalAccessLogMode LogMode { get; set; }
|
||||
|
||||
public FileSystemProxyCore(FileSystemCreators fsCreators, ExternalKeySet externalKeys)
|
||||
public FileSystemProxyCore(FileSystemCreators fsCreators, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
|
||||
{
|
||||
FsCreators = fsCreators;
|
||||
ExternalKeys = externalKeys ?? new ExternalKeySet();
|
||||
DeviceOperator = deviceOperator;
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId)
|
||||
|
@ -34,6 +37,27 @@ namespace LibHac.FsService
|
|||
return FsCreators.SdFileSystemCreator.Create(out fileSystem);
|
||||
}
|
||||
|
||||
public Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId)
|
||||
{
|
||||
switch (partitionId)
|
||||
{
|
||||
case GameCardPartitionRaw.Normal:
|
||||
return FsCreators.GameCardStorageCreator.CreateNormal(handle, out storage);
|
||||
case GameCardPartitionRaw.Secure:
|
||||
return FsCreators.GameCardStorageCreator.CreateSecure(handle, out storage);
|
||||
case GameCardPartitionRaw.Writable:
|
||||
return FsCreators.GameCardStorageCreator.CreateWritable(handle, out storage);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(partitionId), partitionId, null);
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenDeviceOperator(out IDeviceOperator deviceOperator)
|
||||
{
|
||||
deviceOperator = DeviceOperator;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out IFileSystem fileSystem, ContentStorageId storageId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using LibHac.Fs;
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsService.Creators;
|
||||
|
||||
namespace LibHac.FsService
|
||||
|
@ -10,22 +11,23 @@ namespace LibHac.FsService
|
|||
/// <summary>The client instance to be used for internal operations like save indexer access.</summary>
|
||||
private FileSystemClient FsClient { get; }
|
||||
private ITimeSpanGenerator Timer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="FileSystemServer"/> with a new default <see cref="ITimeSpanGenerator"/>.
|
||||
/// </summary>
|
||||
/// <param name="fsCreators">The <see cref="FileSystemCreators"/> used for creating filesystems.</param>
|
||||
public FileSystemServer(FileSystemCreators fsCreators) : this(fsCreators, null, new StopWatchTimeSpanGenerator()) { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="FileSystemServer"/>.
|
||||
/// </summary>
|
||||
/// <param name="fsCreators">The <see cref="FileSystemCreators"/> used for creating filesystems.</param>
|
||||
/// <param name="externalKeys">A keyset containing rights IDs and title keys. If null, an empty set will be created.</param>
|
||||
/// <param name="timer">The <see cref="ITimeSpanGenerator"/> to use for access log timestamps.</param>
|
||||
public FileSystemServer(FileSystemCreators fsCreators, ExternalKeySet externalKeys, ITimeSpanGenerator timer)
|
||||
/// <param name="config">The configuration for the created <see cref="FileSystemServer"/>.</param>
|
||||
public FileSystemServer(FileSystemServerConfig config)
|
||||
{
|
||||
FsProxyCore = new FileSystemProxyCore(fsCreators, externalKeys);
|
||||
if(config.FsCreators == null)
|
||||
throw new ArgumentException("FsCreators must not be null");
|
||||
|
||||
if(config.DeviceOperator == null)
|
||||
throw new ArgumentException("DeviceOperator must not be null");
|
||||
|
||||
ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
|
||||
ITimeSpanGenerator timer = config.TimeSpanGenerator ?? new StopWatchTimeSpanGenerator();
|
||||
|
||||
FsProxyCore = new FileSystemProxyCore(config.FsCreators, externalKeySet, config.DeviceOperator);
|
||||
FsClient = new FileSystemClient(this, timer);
|
||||
Timer = timer;
|
||||
}
|
||||
|
@ -53,4 +55,32 @@ namespace LibHac.FsService
|
|||
return new FileSystemProxy(FsProxyCore, FsClient);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the configuration for creating a new <see cref="FileSystemServer"/>.
|
||||
/// </summary>
|
||||
public class FileSystemServerConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="FileSystemCreators"/> used for creating filesystems.
|
||||
/// </summary>
|
||||
public FileSystemCreators FsCreators { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IDeviceOperator"/> for managing the gamecard and SD card.
|
||||
/// </summary>
|
||||
public IDeviceOperator DeviceOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A keyset containing rights IDs and title keys.
|
||||
/// If null, an empty set will be created.
|
||||
/// </summary>
|
||||
public ExternalKeySet ExternalKeySet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used for generating access log timestamps.
|
||||
/// If null, a new <see cref="StopWatchTimeSpanGenerator"/> will be created.
|
||||
/// </summary>
|
||||
public ITimeSpanGenerator TimeSpanGenerator { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
namespace LibHac.FsService
|
||||
using System;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public struct GameCardHandle
|
||||
public struct GameCardHandle : IEquatable<GameCardHandle>
|
||||
{
|
||||
public int Value;
|
||||
internal readonly int Value;
|
||||
|
||||
public GameCardHandle(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => obj is GameCardHandle handle && Equals(handle);
|
||||
public bool Equals(GameCardHandle other) => Value == other.Value;
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
public static bool operator ==(GameCardHandle left, GameCardHandle right) => left.Equals(right);
|
||||
public static bool operator !=(GameCardHandle left, GameCardHandle right) => !(left == right);
|
||||
}
|
||||
}
|
||||
|
|
18
src/LibHac/FsService/GameCardInfo.cs
Normal file
18
src/LibHac/FsService/GameCardInfo.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
internal class GameCardInfo
|
||||
{
|
||||
public byte[] RootPartitionHeaderHash { get; } = new byte[0x20];
|
||||
public ulong PackageId { get; set; }
|
||||
public long Size { get; set; }
|
||||
public long RootPartitionOffset { get; set; }
|
||||
public long RootPartitionHeaderSize { get; set; }
|
||||
public long SecureAreaOffset { get; set; }
|
||||
public long SecureAreaSize { get; set; }
|
||||
public int UpdateVersion { get; set; }
|
||||
public ulong UpdateTitleId { get; set; }
|
||||
public GameCardAttribute Attribute { get; set; }
|
||||
}
|
||||
}
|
|
@ -20,14 +20,19 @@ namespace LibHac
|
|||
Fs = new FileSystemClient(timer);
|
||||
}
|
||||
|
||||
public void InitializeFileSystemServer(FileSystemCreators fsCreators)
|
||||
public void InitializeFileSystemServer(FileSystemCreators fsCreators, IDeviceOperator deviceOperator)
|
||||
{
|
||||
if (FsSrv != null) return;
|
||||
|
||||
lock (_initLocker)
|
||||
{
|
||||
if (FsSrv != null) return;
|
||||
FsSrv = new FileSystemServer(fsCreators);
|
||||
|
||||
var config = new FileSystemServerConfig();
|
||||
config.FsCreators = fsCreators;
|
||||
config.DeviceOperator = deviceOperator;
|
||||
|
||||
FsSrv = new FileSystemServer(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
|
@ -36,9 +37,9 @@ namespace LibHac
|
|||
public int BackupAreaStartPage { get; set; }
|
||||
public byte KekIndex { get; set; }
|
||||
public byte TitleKeyDecIndex { get; set; }
|
||||
public RomSize RomSize { get; set; }
|
||||
public GameCardSize GameCardSize { get; set; }
|
||||
public byte CardHeaderVersion { get; set; }
|
||||
public XciFlags Flags { get; set; }
|
||||
public GameCardAttribute Flags { get; set; }
|
||||
public ulong PackageId { get; set; }
|
||||
public long ValidDataEndPage { get; set; }
|
||||
public byte[] AesCbcIv { get; set; }
|
||||
|
@ -62,8 +63,9 @@ namespace LibHac
|
|||
public byte[] UppHash { get; set; }
|
||||
public ulong UppId { get; set; }
|
||||
|
||||
public Validity SignatureValidity { get; set; }
|
||||
public byte[] ImageHash { get; }
|
||||
|
||||
public Validity SignatureValidity { get; set; }
|
||||
public Validity PartitionFsHeaderValidity { get; set; }
|
||||
|
||||
public XciHeader(Keyset keyset, Stream stream)
|
||||
|
@ -88,9 +90,9 @@ namespace LibHac
|
|||
byte keyIndex = reader.ReadByte();
|
||||
KekIndex = (byte)(keyIndex >> 4);
|
||||
TitleKeyDecIndex = (byte)(keyIndex & 7);
|
||||
RomSize = (RomSize)reader.ReadByte();
|
||||
GameCardSize = (GameCardSize)reader.ReadByte();
|
||||
CardHeaderVersion = reader.ReadByte();
|
||||
Flags = (XciFlags)reader.ReadByte();
|
||||
Flags = (GameCardAttribute)reader.ReadByte();
|
||||
PackageId = reader.ReadUInt64();
|
||||
ValidDataEndPage = reader.ReadInt64();
|
||||
AesCbcIv = reader.ReadBytes(Crypto.Aes128Size);
|
||||
|
@ -104,7 +106,7 @@ namespace LibHac
|
|||
SelKey = reader.ReadInt32();
|
||||
LimAreaPage = reader.ReadInt32();
|
||||
|
||||
if (!keyset.XciHeaderKey.IsEmpty())
|
||||
if (keyset != null && !keyset.XciHeaderKey.IsEmpty())
|
||||
{
|
||||
byte[] encHeader = reader.ReadBytes(EncryptedHeaderSize);
|
||||
var decHeader = new byte[EncryptedHeaderSize];
|
||||
|
@ -126,30 +128,14 @@ namespace LibHac
|
|||
}
|
||||
}
|
||||
|
||||
ImageHash = Crypto.ComputeSha256(sigData, 0, sigData.Length);
|
||||
|
||||
reader.BaseStream.Position = RootPartitionOffset;
|
||||
PartitionFsHeaderValidity = Crypto.CheckMemoryHashTable(reader.ReadBytes((int)RootPartitionHeaderSize), RootPartitionHeaderHash, 0, (int)RootPartitionHeaderSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum RomSize
|
||||
{
|
||||
Size1Gb = 0xFA,
|
||||
Size2Gb = 0xF8,
|
||||
Size4Gb = 0xF0,
|
||||
Size8Gb = 0xE0,
|
||||
Size16Gb = 0xE1,
|
||||
Size32Gb = 0xE2
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum XciFlags
|
||||
{
|
||||
AutoBoot = 1 << 0,
|
||||
HistoryErase = 1 << 1,
|
||||
RepairTool = 1 << 2
|
||||
}
|
||||
|
||||
public enum CardClockRate
|
||||
{
|
||||
ClockRate25 = 10551312,
|
||||
|
|
|
@ -149,7 +149,7 @@ namespace hactoolnet
|
|||
PrintItem(sb, colLen, "Magic:", xci.Header.Magic);
|
||||
PrintItem(sb, colLen, $"Header Signature{xci.Header.SignatureValidity.GetValidityString()}:", xci.Header.Signature);
|
||||
PrintItem(sb, colLen, $"Header Hash{xci.Header.PartitionFsHeaderValidity.GetValidityString()}:", xci.Header.RootPartitionHeaderHash);
|
||||
PrintItem(sb, colLen, "Cartridge Type:", GetCartridgeType(xci.Header.RomSize));
|
||||
PrintItem(sb, colLen, "Cartridge Type:", GetCartridgeType(xci.Header.GameCardSize));
|
||||
PrintItem(sb, colLen, "Cartridge Size:", $"0x{Util.MediaToReal(xci.Header.ValidDataEndPage + 1):x12}");
|
||||
PrintItem(sb, colLen, "Header IV:", xci.Header.AesCbcIv);
|
||||
|
||||
|
@ -192,16 +192,16 @@ namespace hactoolnet
|
|||
}
|
||||
}
|
||||
|
||||
private static string GetCartridgeType(RomSize size)
|
||||
private static string GetCartridgeType(GameCardSize size)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case RomSize.Size1Gb: return "1GB";
|
||||
case RomSize.Size2Gb: return "2GB";
|
||||
case RomSize.Size4Gb: return "4GB";
|
||||
case RomSize.Size8Gb: return "8GB";
|
||||
case RomSize.Size16Gb: return "16GB";
|
||||
case RomSize.Size32Gb: return "32GB";
|
||||
case GameCardSize.Size1Gb: return "1GB";
|
||||
case GameCardSize.Size2Gb: return "2GB";
|
||||
case GameCardSize.Size4Gb: return "4GB";
|
||||
case GameCardSize.Size8Gb: return "8GB";
|
||||
case GameCardSize.Size16Gb: return "16GB";
|
||||
case GameCardSize.Size32Gb: return "32GB";
|
||||
default: return string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue