mirror of
https://github.com/pound-emu/pound.git
synced 2025-12-20 07:36:59 +00:00
more import progress
This commit is contained in:
parent
bbc104bb56
commit
bda989dc6c
9 changed files with 494 additions and 21 deletions
|
|
@ -6,10 +6,10 @@
|
|||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/include/fmt/format.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/logging/types.h"
|
||||
#include "formatter.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
|
|
|
|||
62
core/crypto/aes_util.h
Executable file
62
core/crypto/aes_util.h
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include "../common/common_types.h"
|
||||
#include "../core/file_sys/vfs/vfs.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
struct CipherContext;
|
||||
|
||||
enum class Mode {
|
||||
CTR = 11,
|
||||
ECB = 2,
|
||||
XTS = 70,
|
||||
};
|
||||
|
||||
enum class Op {
|
||||
Encrypt,
|
||||
Decrypt,
|
||||
};
|
||||
|
||||
template <typename Key, std::size_t KeySize = sizeof(Key)>
|
||||
class AESCipher {
|
||||
static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8.");
|
||||
static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
|
||||
|
||||
public:
|
||||
AESCipher(Key key, Mode mode);
|
||||
~AESCipher();
|
||||
|
||||
void SetIV(std::span<const u8> data);
|
||||
|
||||
template <typename Source, typename Dest>
|
||||
void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const {
|
||||
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
|
||||
"Transcode source and destination types must be trivially copyable.");
|
||||
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
|
||||
}
|
||||
|
||||
void Transcode(const u8* src, std::size_t size, u8* dest, Op op) const;
|
||||
|
||||
template <typename Source, typename Dest>
|
||||
void XTSTranscode(const Source* src, std::size_t size, Dest* dest, std::size_t sector_id,
|
||||
std::size_t sector_size, Op op) {
|
||||
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
|
||||
"XTSTranscode source and destination types must be trivially copyable.");
|
||||
XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
|
||||
sector_size, op);
|
||||
}
|
||||
|
||||
void XTSTranscode(const u8* src, std::size_t size, u8* dest, std::size_t sector_id,
|
||||
std::size_t sector_size, Op op);
|
||||
|
||||
private:
|
||||
std::unique_ptr<CipherContext> ctx;
|
||||
};
|
||||
} // namespace Core::Crypto
|
||||
35
core/crypto/ctr_encryption_layer.h
Executable file
35
core/crypto/ctr_encryption_layer.h
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "aes_util.h"
|
||||
#include "encryption_layer.h"
|
||||
#include "key_manager.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
// Sits on top of a VirtualFile and provides CTR-mode AES description.
|
||||
class CTREncryptionLayer : public EncryptionLayer {
|
||||
public:
|
||||
using IVData = std::array<u8, 16>;
|
||||
|
||||
CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, std::size_t base_offset_);
|
||||
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
|
||||
void SetIV(const IVData& iv);
|
||||
|
||||
private:
|
||||
std::size_t base_offset;
|
||||
|
||||
// Must be mutable as operations modify cipher contexts.
|
||||
mutable AESCipher<Key128> cipher;
|
||||
mutable IVData iv{};
|
||||
|
||||
void UpdateIV(std::size_t offset) const;
|
||||
};
|
||||
|
||||
} // namespace Core::Crypto
|
||||
32
core/crypto/encryption_layer.h
Executable file
32
core/crypto/encryption_layer.h
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common/common_types.h"
|
||||
#include "../core/file_sys/vfs/vfs.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
// Basically non-functional class that implements all of the methods that are irrelevant to an
|
||||
// EncryptionLayer. Reduces duplicate code.
|
||||
class EncryptionLayer : public FileSys::VfsFile {
|
||||
public:
|
||||
explicit EncryptionLayer(FileSys::VirtualFile base);
|
||||
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override = 0;
|
||||
|
||||
std::string GetName() const override;
|
||||
std::size_t GetSize() const override;
|
||||
bool Resize(std::size_t new_size) override;
|
||||
std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
protected:
|
||||
FileSys::VirtualFile base;
|
||||
};
|
||||
|
||||
} // namespace Core::Crypto
|
||||
344
core/crypto/key_manager.h
Executable file
344
core/crypto/key_manager.h
Executable file
|
|
@ -0,0 +1,344 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include <variant>
|
||||
#include <fmt/format.h>
|
||||
#include "../common/common_funcs.h"
|
||||
#include "../common/common_types.h"
|
||||
#include "partition_data_manager.h"
|
||||
|
||||
namespace Common::FS {
|
||||
class IOFile;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class ContentProvider;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
using Key128 = std::array<u8, 0x10>;
|
||||
using Key256 = std::array<u8, 0x20>;
|
||||
using SHA256Hash = std::array<u8, 0x20>;
|
||||
|
||||
enum class SignatureType {
|
||||
RSA_4096_SHA1 = 0x10000,
|
||||
RSA_2048_SHA1 = 0x10001,
|
||||
ECDSA_SHA1 = 0x10002,
|
||||
RSA_4096_SHA256 = 0x10003,
|
||||
RSA_2048_SHA256 = 0x10004,
|
||||
ECDSA_SHA256 = 0x10005,
|
||||
};
|
||||
|
||||
u64 GetSignatureTypeDataSize(SignatureType type);
|
||||
u64 GetSignatureTypePaddingSize(SignatureType type);
|
||||
|
||||
enum class TitleKeyType : u8 {
|
||||
Common = 0,
|
||||
Personalized = 1,
|
||||
};
|
||||
|
||||
struct TicketData {
|
||||
std::array<u8, 0x40> issuer;
|
||||
union {
|
||||
std::array<u8, 0x100> title_key_block;
|
||||
|
||||
struct {
|
||||
Key128 title_key_common;
|
||||
std::array<u8, 0xF0> title_key_common_pad;
|
||||
};
|
||||
};
|
||||
|
||||
INSERT_PADDING_BYTES(0x1);
|
||||
TitleKeyType type;
|
||||
INSERT_PADDING_BYTES(0x3);
|
||||
u8 revision;
|
||||
INSERT_PADDING_BYTES(0xA);
|
||||
u64 ticket_id;
|
||||
u64 device_id;
|
||||
std::array<u8, 0x10> rights_id;
|
||||
u32 account_id;
|
||||
INSERT_PADDING_BYTES(0x14C);
|
||||
};
|
||||
static_assert(sizeof(TicketData) == 0x2C0, "TicketData has incorrect size.");
|
||||
|
||||
struct RSA4096Ticket {
|
||||
SignatureType sig_type;
|
||||
std::array<u8, 0x200> sig_data;
|
||||
INSERT_PADDING_BYTES(0x3C);
|
||||
TicketData data;
|
||||
};
|
||||
static_assert(sizeof(RSA4096Ticket) == 0x500, "RSA4096Ticket has incorrect size.");
|
||||
|
||||
struct RSA2048Ticket {
|
||||
SignatureType sig_type;
|
||||
std::array<u8, 0x100> sig_data;
|
||||
INSERT_PADDING_BYTES(0x3C);
|
||||
TicketData data;
|
||||
};
|
||||
static_assert(sizeof(RSA2048Ticket) == 0x400, "RSA2048Ticket has incorrect size.");
|
||||
|
||||
struct ECDSATicket {
|
||||
SignatureType sig_type;
|
||||
std::array<u8, 0x3C> sig_data;
|
||||
INSERT_PADDING_BYTES(0x40);
|
||||
TicketData data;
|
||||
};
|
||||
static_assert(sizeof(ECDSATicket) == 0x340, "ECDSATicket has incorrect size.");
|
||||
|
||||
struct Ticket {
|
||||
std::variant<std::monostate, RSA4096Ticket, RSA2048Ticket, ECDSATicket> data;
|
||||
|
||||
[[nodiscard]] bool IsValid() const;
|
||||
[[nodiscard]] SignatureType GetSignatureType() const;
|
||||
[[nodiscard]] TicketData& GetData();
|
||||
[[nodiscard]] const TicketData& GetData() const;
|
||||
[[nodiscard]] u64 GetSize() const;
|
||||
|
||||
/**
|
||||
* Synthesizes a common ticket given a title key and rights ID.
|
||||
*
|
||||
* @param title_key Title key to store in the ticket.
|
||||
* @param rights_id Rights ID the ticket is for.
|
||||
* @return The synthesized common ticket.
|
||||
*/
|
||||
static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id);
|
||||
|
||||
/**
|
||||
* Reads a ticket from a file.
|
||||
*
|
||||
* @param file File to read the ticket from.
|
||||
* @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false.
|
||||
*/
|
||||
static Ticket Read(const FileSys::VirtualFile& file);
|
||||
|
||||
/**
|
||||
* Reads a ticket from a memory buffer.
|
||||
*
|
||||
* @param raw_data Buffer to read the ticket from.
|
||||
* @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false.
|
||||
*/
|
||||
static Ticket Read(std::span<const u8> raw_data);
|
||||
};
|
||||
|
||||
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
|
||||
static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
|
||||
|
||||
template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
|
||||
struct RSAKeyPair {
|
||||
std::array<u8, byte_size> encryption_key;
|
||||
std::array<u8, byte_size> decryption_key;
|
||||
std::array<u8, byte_size> modulus;
|
||||
std::array<u8, 4> exponent;
|
||||
};
|
||||
|
||||
template <size_t bit_size, size_t byte_size>
|
||||
bool operator==(const RSAKeyPair<bit_size, byte_size>& lhs,
|
||||
const RSAKeyPair<bit_size, byte_size>& rhs) {
|
||||
return std::tie(lhs.encryption_key, lhs.decryption_key, lhs.modulus, lhs.exponent) ==
|
||||
std::tie(rhs.encryption_key, rhs.decryption_key, rhs.modulus, rhs.exponent);
|
||||
}
|
||||
|
||||
template <size_t bit_size, size_t byte_size>
|
||||
bool operator!=(const RSAKeyPair<bit_size, byte_size>& lhs,
|
||||
const RSAKeyPair<bit_size, byte_size>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
enum class KeyCategory : u8 {
|
||||
Standard,
|
||||
Title,
|
||||
Console,
|
||||
};
|
||||
|
||||
enum class S256KeyType : u64 {
|
||||
SDKey, // f1=SDKeyType
|
||||
Header, //
|
||||
SDKeySource, // f1=SDKeyType
|
||||
HeaderSource, //
|
||||
};
|
||||
|
||||
enum class S128KeyType : u64 {
|
||||
Master, // f1=crypto revision
|
||||
Package1, // f1=crypto revision
|
||||
Package2, // f1=crypto revision
|
||||
Titlekek, // f1=crypto revision
|
||||
ETicketRSAKek, //
|
||||
KeyArea, // f1=crypto revision f2=type {app, ocean, system}
|
||||
SDSeed, //
|
||||
Titlekey, // f1=rights id LSB f2=rights id MSB
|
||||
Source, // f1=source type, f2= sub id
|
||||
Keyblob, // f1=crypto revision
|
||||
KeyblobMAC, // f1=crypto revision
|
||||
TSEC, //
|
||||
SecureBoot, //
|
||||
BIS, // f1=partition (0-3), f2=type {crypt, tweak}
|
||||
HeaderKek, //
|
||||
SDKek, //
|
||||
RSAKek, //
|
||||
};
|
||||
|
||||
enum class KeyAreaKeyType : u8 {
|
||||
Application,
|
||||
Ocean,
|
||||
System,
|
||||
};
|
||||
|
||||
enum class SourceKeyType : u8 {
|
||||
SDKek, //
|
||||
AESKekGeneration, //
|
||||
AESKeyGeneration, //
|
||||
RSAOaepKekGeneration, //
|
||||
Master, //
|
||||
Keyblob, // f2=crypto revision
|
||||
KeyAreaKey, // f2=KeyAreaKeyType
|
||||
Titlekek, //
|
||||
Package2, //
|
||||
HeaderKek, //
|
||||
KeyblobMAC, //
|
||||
ETicketKek, //
|
||||
ETicketKekek, //
|
||||
};
|
||||
|
||||
enum class SDKeyType : u8 {
|
||||
Save,
|
||||
NCA,
|
||||
};
|
||||
|
||||
enum class BISKeyType : u8 {
|
||||
Crypto,
|
||||
Tweak,
|
||||
};
|
||||
|
||||
enum class RSAKekType : u8 {
|
||||
Mask0,
|
||||
Seed3,
|
||||
};
|
||||
|
||||
template <typename KeyType>
|
||||
struct KeyIndex {
|
||||
KeyType type;
|
||||
u64 field1;
|
||||
u64 field2;
|
||||
|
||||
std::string DebugInfo() const {
|
||||
u8 key_size = 16;
|
||||
if constexpr (std::is_same_v<KeyType, S256KeyType>)
|
||||
key_size = 32;
|
||||
return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size,
|
||||
static_cast<u8>(type), field1, field2);
|
||||
}
|
||||
};
|
||||
|
||||
// boost flat_map requires operator< for O(log(n)) lookups.
|
||||
template <typename KeyType>
|
||||
bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
|
||||
return std::tie(lhs.type, lhs.field1, lhs.field2) < std::tie(rhs.type, rhs.field1, rhs.field2);
|
||||
}
|
||||
|
||||
class KeyManager {
|
||||
public:
|
||||
static KeyManager& Instance() {
|
||||
static KeyManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
KeyManager(const KeyManager&) = delete;
|
||||
KeyManager& operator=(const KeyManager&) = delete;
|
||||
|
||||
KeyManager(KeyManager&&) = delete;
|
||||
KeyManager& operator=(KeyManager&&) = delete;
|
||||
|
||||
bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
|
||||
bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
|
||||
|
||||
Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
|
||||
Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
|
||||
|
||||
Key256 GetBISKey(u8 partition_id) const;
|
||||
|
||||
void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
|
||||
void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
|
||||
|
||||
static bool KeyFileExists(bool title);
|
||||
|
||||
// Call before using the sd seed to attempt to derive it if it doesn't exist. Needs system
|
||||
// save 8*43 and the private file to exist.
|
||||
void DeriveSDSeedLazy();
|
||||
|
||||
bool BaseDeriveNecessary() const;
|
||||
void DeriveBase();
|
||||
void DeriveETicket(PartitionDataManager& data, const FileSys::ContentProvider& provider);
|
||||
void PopulateTickets();
|
||||
void SynthesizeTickets();
|
||||
|
||||
void PopulateFromPartitionData(PartitionDataManager& data);
|
||||
|
||||
const std::map<u128, Ticket>& GetCommonTickets() const;
|
||||
const std::map<u128, Ticket>& GetPersonalizedTickets() const;
|
||||
|
||||
bool AddTicket(const Ticket& ticket);
|
||||
|
||||
void ReloadKeys();
|
||||
bool AreKeysLoaded() const;
|
||||
|
||||
private:
|
||||
KeyManager();
|
||||
|
||||
std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
|
||||
std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
|
||||
|
||||
// Map from rights ID to ticket
|
||||
std::map<u128, Ticket> common_tickets;
|
||||
std::map<u128, Ticket> personal_tickets;
|
||||
bool ticket_databases_loaded = false;
|
||||
|
||||
std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
|
||||
std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
|
||||
std::array<u8, 576> eticket_extended_kek{};
|
||||
RSAKeyPair<2048> eticket_rsa_keypair{};
|
||||
|
||||
bool dev_mode;
|
||||
void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys);
|
||||
|
||||
template <size_t Size>
|
||||
void WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
const std::array<u8, Size>& key);
|
||||
|
||||
void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
|
||||
|
||||
void DeriveETicketRSAKey();
|
||||
|
||||
void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
|
||||
void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
|
||||
|
||||
/// Parses the title key section of a ticket.
|
||||
std::optional<Key128> ParseTicketTitleKey(const Ticket& ticket);
|
||||
};
|
||||
|
||||
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
|
||||
Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source);
|
||||
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source);
|
||||
Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source);
|
||||
std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
|
||||
const Key128& key);
|
||||
|
||||
std::optional<Key128> DeriveSDSeed();
|
||||
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
|
||||
|
||||
std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save);
|
||||
|
||||
} // namespace Core::Crypto
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "import/common/common_types.h"
|
||||
#include "core/fs/vfs/vfs_types.h"
|
||||
#include "..common/common_types.h"
|
||||
#include "vfs/vfs_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "import/common/common_types.h"
|
||||
#include "import/common/swap.h"
|
||||
#include "core/fs/vfs/vfs.h"
|
||||
#include "../common/common_types.h"
|
||||
#include "../common/swap.h"
|
||||
#include "vfs/vfs.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
class KeyManager;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "../common/common_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
|
|
|||
|
|
@ -6,19 +6,19 @@
|
|||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_ranges.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/ctr_encryption_layer.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/vfs/vfs_offset.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "../common/logging/log.h"
|
||||
#include "../common/polyfill_ranges.h"
|
||||
#include "../core/crypto/aes_util.h"
|
||||
#include "../core/crypto/ctr_encryption_layer.h"
|
||||
#include "../core/crypto/key_manager.h"
|
||||
#include "content_archive.h"
|
||||
#include "partition_filesystem.h"
|
||||
#include "vfs/vfs_offset.h"
|
||||
#include "../core/loader/loader.h"
|
||||
|
||||
#include "core/file_sys/fssystem/fssystem_compression_configuration.h"
|
||||
#include "core/file_sys/fssystem/fssystem_crypto_configuration.h"
|
||||
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
|
||||
#include "fssystem/fssystem_compression_configuration.h"
|
||||
#include "fssystem/fssystem_crypto_configuration.h"
|
||||
#include "fssystem/fssystem_nca_file_system_driver.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue