From 51a13068df0234cd8b0c3fc79d8d8a78c423c324 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 7 Oct 2019 18:39:06 -0500 Subject: [PATCH] Add an emulated gamecard and storage creator --- src/LibHac/Fs/GameCard.cs | 79 +++++++++++ src/LibHac/Fs/ResultFs.cs | 11 ++ .../EmulatedGameCardStorageCreator.cs | 125 ++++++++++++++++++ .../FsService/Creators/FileSystemCreators.cs | 11 +- .../FsService/EmulatedDeviceOperator.cs | 38 ++++++ src/LibHac/FsService/EmulatedGameCard.cs | 103 +++++++++++++++ src/LibHac/FsService/FileSystemProxy.cs | 8 +- src/LibHac/FsService/FileSystemProxyCore.cs | 26 +++- src/LibHac/FsService/FileSystemServer.cs | 56 ++++++-- src/LibHac/FsService/GameCardHandle.cs | 19 ++- src/LibHac/FsService/GameCardInfo.cs | 18 +++ src/LibHac/Horizon.cs | 9 +- src/LibHac/XciHeader.cs | 34 ++--- src/hactoolnet/ProcessXci.cs | 16 +-- 14 files changed, 498 insertions(+), 55 deletions(-) create mode 100644 src/LibHac/Fs/GameCard.cs create mode 100644 src/LibHac/FsService/Creators/EmulatedGameCardStorageCreator.cs create mode 100644 src/LibHac/FsService/EmulatedDeviceOperator.cs create mode 100644 src/LibHac/FsService/EmulatedGameCard.cs create mode 100644 src/LibHac/FsService/GameCardInfo.cs diff --git a/src/LibHac/Fs/GameCard.cs b/src/LibHac/Fs/GameCard.cs new file mode 100644 index 00000000..70680e4a --- /dev/null +++ b/src/LibHac/Fs/GameCard.cs @@ -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 + } +} diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index b9d4edfe..1922b40e 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -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); diff --git a/src/LibHac/FsService/Creators/EmulatedGameCardStorageCreator.cs b/src/LibHac/FsService/Creators/EmulatedGameCardStorageCreator.cs new file mode 100644 index 00000000..a0b73f52 --- /dev/null +++ b/src/LibHac/FsService/Creators/EmulatedGameCardStorageCreator.cs @@ -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 deviceId = stackalloc byte[0x10]; + Span 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 deviceId, ReadOnlySpan imageHash) + { + GameCard = gameCard; + Handle = handle; + IsSecureMode = true; + deviceId.CopyTo(DeviceId); + imageHash.CopyTo(ImageHash); + } + + public override Result Read(long offset, Span 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 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; + } + } + } +} diff --git a/src/LibHac/FsService/Creators/FileSystemCreators.cs b/src/LibHac/FsService/Creators/FileSystemCreators.cs index c0f83871..08b35243 100644 --- a/src/LibHac/FsService/Creators/FileSystemCreators.cs +++ b/src/LibHac/FsService/Creators/FileSystemCreators.cs @@ -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); } } } diff --git a/src/LibHac/FsService/EmulatedDeviceOperator.cs b/src/LibHac/FsService/EmulatedDeviceOperator.cs new file mode 100644 index 00000000..5e529756 --- /dev/null +++ b/src/LibHac/FsService/EmulatedDeviceOperator.cs @@ -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; + } + } +} diff --git a/src/LibHac/FsService/EmulatedGameCard.cs b/src/LibHac/FsService/EmulatedGameCard.cs new file mode 100644 index 00000000..e3e8ec25 --- /dev/null +++ b/src/LibHac/FsService/EmulatedGameCard.cs @@ -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 destination) + { + if (IsGameCardHandleInvalid(handle)) return ResultFs.InvalidGameCardHandleOnRead.Log(); + if (!IsGameCardInserted()) return ResultFs.GameCardNotInserted.Log(); + + return CardImageStorage.Read(offset, destination); + } + + public Result GetGameCardImageHash(Span 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 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; + } + } +} diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index adc67c3c..b16566ec 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -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) diff --git a/src/LibHac/FsService/FileSystemProxyCore.cs b/src/LibHac/FsService/FileSystemProxyCore.cs index 8bda0eaf..df691c0b 100644 --- a/src/LibHac/FsService/FileSystemProxyCore.cs +++ b/src/LibHac/FsService/FileSystemProxyCore.cs @@ -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; diff --git a/src/LibHac/FsService/FileSystemServer.cs b/src/LibHac/FsService/FileSystemServer.cs index 7e6338c0..c1ae6de2 100644 --- a/src/LibHac/FsService/FileSystemServer.cs +++ b/src/LibHac/FsService/FileSystemServer.cs @@ -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 /// The client instance to be used for internal operations like save indexer access. private FileSystemClient FsClient { get; } private ITimeSpanGenerator Timer { get; } - - /// - /// Creates a new with a new default . - /// - /// The used for creating filesystems. - public FileSystemServer(FileSystemCreators fsCreators) : this(fsCreators, null, new StopWatchTimeSpanGenerator()) { } - + /// /// Creates a new . /// - /// The used for creating filesystems. - /// A keyset containing rights IDs and title keys. If null, an empty set will be created. - /// The to use for access log timestamps. - public FileSystemServer(FileSystemCreators fsCreators, ExternalKeySet externalKeys, ITimeSpanGenerator timer) + /// The configuration for the created . + 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); } } + + /// + /// Contains the configuration for creating a new . + /// + public class FileSystemServerConfig + { + /// + /// The used for creating filesystems. + /// + public FileSystemCreators FsCreators { get; set; } + + /// + /// An for managing the gamecard and SD card. + /// + public IDeviceOperator DeviceOperator { get; set; } + + /// + /// A keyset containing rights IDs and title keys. + /// If null, an empty set will be created. + /// + public ExternalKeySet ExternalKeySet { get; set; } + + /// + /// Used for generating access log timestamps. + /// If null, a new will be created. + /// + public ITimeSpanGenerator TimeSpanGenerator { get; set; } + } } diff --git a/src/LibHac/FsService/GameCardHandle.cs b/src/LibHac/FsService/GameCardHandle.cs index 1753a869..21214ad8 100644 --- a/src/LibHac/FsService/GameCardHandle.cs +++ b/src/LibHac/FsService/GameCardHandle.cs @@ -1,7 +1,20 @@ -namespace LibHac.FsService +using System; + +namespace LibHac.FsService { - public struct GameCardHandle + public struct GameCardHandle : IEquatable { - 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); } } diff --git a/src/LibHac/FsService/GameCardInfo.cs b/src/LibHac/FsService/GameCardInfo.cs new file mode 100644 index 00000000..2047473d --- /dev/null +++ b/src/LibHac/FsService/GameCardInfo.cs @@ -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; } + } +} diff --git a/src/LibHac/Horizon.cs b/src/LibHac/Horizon.cs index d93fc1df..302acee5 100644 --- a/src/LibHac/Horizon.cs +++ b/src/LibHac/Horizon.cs @@ -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); } } } diff --git a/src/LibHac/XciHeader.cs b/src/LibHac/XciHeader.cs index f945c8a3..361dcad8 100644 --- a/src/LibHac/XciHeader.cs +++ b/src/LibHac/XciHeader.cs @@ -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, diff --git a/src/hactoolnet/ProcessXci.cs b/src/hactoolnet/ProcessXci.cs index 03841cca..43c36f48 100644 --- a/src/hactoolnet/ProcessXci.cs +++ b/src/hactoolnet/ProcessXci.cs @@ -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; } }