245 lines
7.7 KiB
C++
245 lines
7.7 KiB
C++
#pragma once
|
|
|
|
#include "ui/menus/menu_base.hpp"
|
|
#include "yati/container/base.hpp"
|
|
#include "yati/source/base.hpp"
|
|
#include "ui/list.hpp"
|
|
#include <span>
|
|
#include <memory>
|
|
|
|
namespace sphaira::ui::menu::gc {
|
|
|
|
typedef enum {
|
|
FsGameCardPartitionRaw_None = -1,
|
|
FsGameCardPartitionRaw_Normal = 0,
|
|
FsGameCardPartitionRaw_Secure = 1,
|
|
} FsGameCardPartitionRaw;
|
|
|
|
////////////////////////////////////////////////
|
|
// The below structs are taken from nxdumptool./
|
|
////////////////////////////////////////////////
|
|
|
|
/// Located at offset 0x7000 in the gamecard image.
|
|
typedef struct {
|
|
u8 signature[0x100]; ///< RSA-2048-PKCS#1 v1.5 with SHA-256 signature over the rest of the data.
|
|
u32 magic; ///< "CERT".
|
|
u32 version;
|
|
u8 kek_index;
|
|
u8 reserved[0x7];
|
|
u8 t1_card_device_id[0x10];
|
|
u8 iv[0x10];
|
|
u8 hw_key[0x10]; ///< Encrypted.
|
|
u8 data[0xC0]; ///< Encrypted.
|
|
} FsGameCardCertificate;
|
|
|
|
static_assert(sizeof(FsGameCardCertificate) == 0x200);
|
|
|
|
typedef struct {
|
|
u8 maker_code; ///< FsCardId1MakerCode.
|
|
u8 memory_capacity; ///< Matches GameCardRomSize.
|
|
u8 reserved; ///< Known values: 0x00, 0x01, 0x02, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0C, 0x0D, 0x0E, 0x80.
|
|
u8 memory_type; ///< FsCardId1MemoryType.
|
|
} FsCardId1;
|
|
|
|
static_assert(sizeof(FsCardId1) == 0x4);
|
|
|
|
typedef struct {
|
|
u8 card_security_number; ///< FsCardId2CardSecurityNumber.
|
|
u8 card_type; ///< FsCardId2CardType.
|
|
u8 reserved[0x2]; ///< Usually filled with zeroes.
|
|
} FsCardId2;
|
|
|
|
static_assert(sizeof(FsCardId2) == 0x4);
|
|
|
|
typedef struct {
|
|
u8 reserved[0x4]; ///< Usually filled with zeroes.
|
|
} FsCardId3;
|
|
|
|
static_assert(sizeof(FsCardId3) == 0x4);
|
|
|
|
/// Returned by fsDeviceOperatorGetGameCardIdSet.
|
|
typedef struct {
|
|
FsCardId1 id1; ///< Specifies maker code, memory capacity and memory type.
|
|
FsCardId2 id2; ///< Specifies card security number and card type.
|
|
FsCardId3 id3; ///< Always zero (so far).
|
|
} FsGameCardIdSet;
|
|
|
|
/// Encrypted using AES-128-ECB with the common titlekek generator key (stored in the .rodata segment from the Lotus firmware).
|
|
typedef struct {
|
|
union {
|
|
u8 value[0x10];
|
|
struct {
|
|
u8 package_id[0x8]; ///< Matches package_id from GameCardHeader.
|
|
u8 reserved[0x8]; ///< Just zeroes.
|
|
};
|
|
};
|
|
} GameCardKeySource;
|
|
|
|
static_assert(sizeof(GameCardKeySource) == 0x10);
|
|
|
|
/// Plaintext area. Dumped from FS program memory.
|
|
typedef struct {
|
|
GameCardKeySource key_source;
|
|
u8 encrypted_titlekey[0x10]; ///< Encrypted using AES-128-CCM with the decrypted key_source and the nonce from this section.
|
|
u8 mac[0x10]; ///< Used to verify the validity of the decrypted titlekey.
|
|
u8 nonce[0xC]; ///< Used as the IV to decrypt encrypted_titlekey using AES-128-CCM.
|
|
u8 reserved[0x1C4];
|
|
} GameCardInitialData;
|
|
|
|
static_assert(sizeof(GameCardInitialData) == 0x200);
|
|
|
|
typedef struct {
|
|
u8 maker_code; ///< GameCardUidMakerCode.
|
|
u8 version; ///< TODO: determine whether this matches GameCardVersion or not.
|
|
u8 card_type; ///< GameCardUidCardType.
|
|
u8 unique_data[0x9];
|
|
u32 random;
|
|
u8 platform_flag;
|
|
u8 reserved[0xB];
|
|
FsCardId1 card_id_1_mirror; ///< This field mirrors bit 5 of FsCardId1MemoryType.
|
|
u8 mac[0x20];
|
|
} GameCardUid;
|
|
|
|
static_assert(sizeof(GameCardUid) == 0x40);
|
|
|
|
/// Plaintext area. Dumped from FS program memory.
|
|
/// Overall structure may change with each new LAFW version.
|
|
typedef struct {
|
|
u32 asic_security_mode; ///< Determines how the Lotus ASIC initialised the gamecard security mode. Usually 0xFFFFFFF9.
|
|
u32 asic_status; ///< Bitmask of the internal gamecard interface status. Usually 0x20000000.
|
|
FsCardId1 card_id1;
|
|
FsCardId2 card_id2;
|
|
GameCardUid card_uid;
|
|
u8 reserved[0x190];
|
|
u8 mac[0x20]; ///< Changes with each gamecard (re)insertion.
|
|
} GameCardSpecificData;
|
|
|
|
static_assert(sizeof(GameCardSpecificData) == 0x200);
|
|
|
|
/// Plaintext area. Dumped from FS program memory.
|
|
/// This struct is returned by Lotus command "ChangeToSecureMode" (0xF). This means it is only available *after* the gamecard secure area has been mounted.
|
|
/// A copy of the gamecard header without the RSA-2048 signature and a plaintext GameCardInfo precedes this struct in FS program memory.
|
|
typedef struct {
|
|
GameCardSpecificData specific_data;
|
|
FsGameCardCertificate certificate;
|
|
u8 reserved[0x200];
|
|
GameCardInitialData initial_data;
|
|
} GameCardSecurityInformation;
|
|
|
|
static_assert(sizeof(GameCardSecurityInformation) == 0x800);
|
|
|
|
///////////////////
|
|
// nxdumptool fin./
|
|
///////////////////
|
|
|
|
struct GcCollection : yati::container::CollectionEntry {
|
|
GcCollection(const char* _name, s64 _size, u8 _type, u8 _id_offset) {
|
|
name = _name;
|
|
size = _size;
|
|
type = _type;
|
|
id_offset = _id_offset;
|
|
}
|
|
|
|
// NcmContentType
|
|
u8 type{};
|
|
u8 id_offset{};
|
|
};
|
|
|
|
using GcCollections = std::vector<GcCollection>;
|
|
|
|
struct ApplicationEntry {
|
|
u64 app_id{};
|
|
u32 version{};
|
|
u8 key_gen{};
|
|
std::vector<u8> icon;
|
|
NacpLanguageEntry lang_entry{};
|
|
|
|
std::vector<GcCollections> application{};
|
|
std::vector<GcCollections> patch{};
|
|
std::vector<GcCollections> add_on{};
|
|
std::vector<GcCollections> data_patch{};
|
|
yati::container::Collections tickets{};
|
|
|
|
auto GetSize() const -> s64;
|
|
auto GetSize(const std::vector<GcCollections>& entries) const -> s64;
|
|
};
|
|
|
|
struct Menu final : MenuBase {
|
|
Menu(u32 flags);
|
|
~Menu();
|
|
|
|
auto GetShortTitle() const -> const char* override { return "GC"; };
|
|
void Update(Controller* controller, TouchInfo* touch) override;
|
|
void Draw(NVGcontext* vg, Theme* theme) override;
|
|
void OnFocusGained() override;
|
|
|
|
Result GcStorageRead(void* buf, s64 off, s64 size);
|
|
|
|
private:
|
|
Result GcPoll(bool* inserted);
|
|
Result GcOnEvent(bool force = false);
|
|
|
|
// GameCard FS api.
|
|
Result GcMount();
|
|
void GcUnmount();
|
|
|
|
// GameCard Storage api
|
|
Result GcMountStorage();
|
|
void GcUmountStorage();
|
|
Result GcMountPartition(FsGameCardPartitionRaw partition);
|
|
void GcUnmountPartition();
|
|
Result GcStorageReadInternal(void* buf, s64 off, s64 size, u64* bytes_read);
|
|
|
|
// taken from nxdumptool.
|
|
Result GcGetSecurityInfo(GameCardSecurityInformation& out);
|
|
|
|
Result LoadControlData(ApplicationEntry& e);
|
|
Result UpdateStorageSize();
|
|
void FreeImage();
|
|
void OnChangeIndex(s64 new_index);
|
|
Result DumpGames(u32 flags);
|
|
|
|
private:
|
|
FsDeviceOperator m_dev_op{};
|
|
FsGameCardHandle m_handle{};
|
|
std::unique_ptr<fs::FsNativeGameCard> m_fs{};
|
|
FsEventNotifier m_event_notifier{};
|
|
Event m_event{};
|
|
|
|
std::vector<ApplicationEntry> m_entries{};
|
|
std::unique_ptr<List> m_list{};
|
|
s64 m_entry_index{};
|
|
s64 m_option_index{};
|
|
|
|
s64 m_size_free_sd{};
|
|
s64 m_size_total_sd{};
|
|
s64 m_size_free_nand{};
|
|
s64 m_size_total_nand{};
|
|
int m_icon{};
|
|
bool m_mounted{};
|
|
|
|
FsStorage m_storage{};
|
|
// size of normal partition.
|
|
s64 m_parition_normal_size{};
|
|
// size of secure partition.
|
|
s64 m_parition_secure_size{};
|
|
// used size reported in the xci header.
|
|
s64 m_storage_trimmed_size{};
|
|
// total size of m_parition_normal_size + m_parition_secure_size.
|
|
s64 m_storage_total_size{};
|
|
// reported size via rom_size in the xci header.
|
|
s64 m_storage_full_size{};
|
|
// found in xci header.
|
|
u64 m_package_id{};
|
|
// found in xci header.
|
|
u8 m_initial_data_hash[SHA256_HASH_SIZE]{};
|
|
// currently mounted storage partiton.
|
|
FsGameCardPartitionRaw m_partition{FsGameCardPartitionRaw_None};
|
|
bool m_storage_mounted{};
|
|
|
|
// set when the gc should be re-mounted, cleared when handled.
|
|
bool m_dirty{};
|
|
};
|
|
|
|
} // namespace sphaira::ui::menu::gc
|