mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 04:02:11 +00:00
Baby steps.
This commit is contained in:
parent
65e40e7600
commit
eccd3f0c1c
53 changed files with 5785 additions and 5145 deletions
4
Makefile
4
Makefile
|
@ -32,8 +32,8 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||
#---------------------------------------------------------------------------------
|
||||
|
||||
VERSION_MAJOR := 1
|
||||
VERSION_MINOR := 1
|
||||
VERSION_MICRO := 9
|
||||
VERSION_MINOR := 2
|
||||
VERSION_MICRO := 0
|
||||
|
||||
APP_TITLE := nxdumptool
|
||||
APP_AUTHOR := DarkMatterCore
|
||||
|
|
177
old/main.c
Normal file
177
old/main.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
bool exitMainLoop = false;
|
||||
|
||||
/* Initialize application resources */
|
||||
if (!initApplicationResources(argc, argv))
|
||||
{
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Main application loop */
|
||||
while(appletMainLoop())
|
||||
{
|
||||
UIResult result = uiProcess();
|
||||
switch(result)
|
||||
{
|
||||
case resultShowMainMenu:
|
||||
uiSetState(stateMainMenu);
|
||||
break;
|
||||
case resultShowGameCardMenu:
|
||||
uiSetState(stateGameCardMenu);
|
||||
break;
|
||||
case resultShowXciDumpMenu:
|
||||
uiSetState(stateXciDumpMenu);
|
||||
break;
|
||||
case resultDumpXci:
|
||||
uiSetState(stateDumpXci);
|
||||
break;
|
||||
case resultShowNspDumpMenu:
|
||||
uiSetState(stateNspDumpMenu);
|
||||
break;
|
||||
case resultShowNspAppDumpMenu:
|
||||
uiSetState(stateNspAppDumpMenu);
|
||||
break;
|
||||
case resultShowNspPatchDumpMenu:
|
||||
uiSetState(stateNspPatchDumpMenu);
|
||||
break;
|
||||
case resultShowNspAddOnDumpMenu:
|
||||
uiSetState(stateNspAddOnDumpMenu);
|
||||
break;
|
||||
case resultDumpNsp:
|
||||
uiSetState(stateDumpNsp);
|
||||
break;
|
||||
case resultShowHfs0Menu:
|
||||
uiSetState(stateHfs0Menu);
|
||||
break;
|
||||
case resultShowRawHfs0PartitionDumpMenu:
|
||||
uiSetState(stateRawHfs0PartitionDumpMenu);
|
||||
break;
|
||||
case resultDumpRawHfs0Partition:
|
||||
uiSetState(stateDumpRawHfs0Partition);
|
||||
break;
|
||||
case resultShowHfs0PartitionDataDumpMenu:
|
||||
uiSetState(stateHfs0PartitionDataDumpMenu);
|
||||
break;
|
||||
case resultDumpHfs0PartitionData:
|
||||
uiSetState(stateDumpHfs0PartitionData);
|
||||
break;
|
||||
case resultShowHfs0BrowserMenu:
|
||||
uiSetState(stateHfs0BrowserMenu);
|
||||
break;
|
||||
case resultHfs0BrowserGetList:
|
||||
uiSetState(stateHfs0BrowserGetList);
|
||||
break;
|
||||
case resultShowHfs0Browser:
|
||||
uiSetState(stateHfs0Browser);
|
||||
break;
|
||||
case resultHfs0BrowserCopyFile:
|
||||
uiSetState(stateHfs0BrowserCopyFile);
|
||||
break;
|
||||
case resultShowExeFsMenu:
|
||||
uiSetState(stateExeFsMenu);
|
||||
break;
|
||||
case resultShowExeFsSectionDataDumpMenu:
|
||||
uiSetState(stateExeFsSectionDataDumpMenu);
|
||||
break;
|
||||
case resultDumpExeFsSectionData:
|
||||
uiSetState(stateDumpExeFsSectionData);
|
||||
break;
|
||||
case resultShowExeFsSectionBrowserMenu:
|
||||
uiSetState(stateExeFsSectionBrowserMenu);
|
||||
break;
|
||||
case resultExeFsSectionBrowserGetList:
|
||||
uiSetState(stateExeFsSectionBrowserGetList);
|
||||
break;
|
||||
case resultShowExeFsSectionBrowser:
|
||||
uiSetState(stateExeFsSectionBrowser);
|
||||
break;
|
||||
case resultExeFsSectionBrowserCopyFile:
|
||||
uiSetState(stateExeFsSectionBrowserCopyFile);
|
||||
break;
|
||||
case resultShowRomFsMenu:
|
||||
uiSetState(stateRomFsMenu);
|
||||
break;
|
||||
case resultShowRomFsSectionDataDumpMenu:
|
||||
uiSetState(stateRomFsSectionDataDumpMenu);
|
||||
break;
|
||||
case resultDumpRomFsSectionData:
|
||||
uiSetState(stateDumpRomFsSectionData);
|
||||
break;
|
||||
case resultShowRomFsSectionBrowserMenu:
|
||||
uiSetState(stateRomFsSectionBrowserMenu);
|
||||
break;
|
||||
case resultRomFsSectionBrowserGetEntries:
|
||||
uiSetState(stateRomFsSectionBrowserGetEntries);
|
||||
break;
|
||||
case resultShowRomFsSectionBrowser:
|
||||
uiSetState(stateRomFsSectionBrowser);
|
||||
break;
|
||||
case resultRomFsSectionBrowserChangeDir:
|
||||
uiSetState(stateRomFsSectionBrowserChangeDir);
|
||||
break;
|
||||
case resultRomFsSectionBrowserCopyFile:
|
||||
uiSetState(stateRomFsSectionBrowserCopyFile);
|
||||
break;
|
||||
case resultRomFsSectionBrowserCopyDir:
|
||||
uiSetState(stateRomFsSectionBrowserCopyDir);
|
||||
break;
|
||||
case resultDumpGameCardCertificate:
|
||||
uiSetState(stateDumpGameCardCertificate);
|
||||
break;
|
||||
case resultShowSdCardEmmcMenu:
|
||||
uiSetState(stateSdCardEmmcMenu);
|
||||
break;
|
||||
case resultShowSdCardEmmcTitleMenu:
|
||||
uiSetState(stateSdCardEmmcTitleMenu);
|
||||
break;
|
||||
case resultShowSdCardEmmcOrphanPatchAddOnMenu:
|
||||
uiSetState(stateSdCardEmmcOrphanPatchAddOnMenu);
|
||||
break;
|
||||
case resultShowSdCardEmmcBatchModeMenu:
|
||||
uiSetState(stateSdCardEmmcBatchModeMenu);
|
||||
break;
|
||||
case resultSdCardEmmcBatchDump:
|
||||
uiSetState(stateSdCardEmmcBatchDump);
|
||||
break;
|
||||
case resultShowTicketMenu:
|
||||
uiSetState(stateTicketMenu);
|
||||
break;
|
||||
case resultDumpTicket:
|
||||
uiSetState(stateDumpTicket);
|
||||
break;
|
||||
case resultShowUpdateMenu:
|
||||
uiSetState(stateUpdateMenu);
|
||||
break;
|
||||
case resultUpdateNSWDBXml:
|
||||
uiSetState(stateUpdateNSWDBXml);
|
||||
break;
|
||||
case resultUpdateApplication:
|
||||
uiSetState(stateUpdateApplication);
|
||||
break;
|
||||
case resultExit:
|
||||
exitMainLoop = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (exitMainLoop) break;
|
||||
}
|
||||
|
||||
out:
|
||||
/* Deinitialize application resources */
|
||||
deinitApplicationResources();
|
||||
|
||||
return ret;
|
||||
}
|
761
old/nca.h
Normal file
761
old/nca.h
Normal file
|
@ -0,0 +1,761 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __NCA_H__
|
||||
#define __NCA_H__
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#define NCA3_MAGIC (u32)0x4E434133 // "NCA3"
|
||||
#define NCA2_MAGIC (u32)0x4E434132 // "NCA2"
|
||||
|
||||
#define NCA_HEADER_LENGTH 0x400
|
||||
#define NCA_SECTION_HEADER_LENGTH 0x200
|
||||
#define NCA_SECTION_HEADER_CNT 4
|
||||
#define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_SECTION_HEADER_LENGTH * NCA_SECTION_HEADER_CNT))
|
||||
|
||||
#define NCA_AES_XTS_SECTOR_SIZE 0x200
|
||||
|
||||
#define NCA_KEY_AREA_KEY_CNT 4
|
||||
#define NCA_KEY_AREA_KEY_SIZE 0x10
|
||||
#define NCA_KEY_AREA_SIZE (NCA_KEY_AREA_KEY_CNT * NCA_KEY_AREA_KEY_SIZE)
|
||||
|
||||
#define NCA_FS_HEADER_PARTITION_PFS0 0x01
|
||||
#define NCA_FS_HEADER_FSTYPE_PFS0 0x02
|
||||
|
||||
#define NCA_FS_HEADER_PARTITION_ROMFS 0x00
|
||||
#define NCA_FS_HEADER_FSTYPE_ROMFS 0x03
|
||||
|
||||
#define NCA_FS_HEADER_CRYPT_NONE 0x01
|
||||
#define NCA_FS_HEADER_CRYPT_XTS 0x02
|
||||
#define NCA_FS_HEADER_CRYPT_CTR 0x03
|
||||
#define NCA_FS_HEADER_CRYPT_BKTR 0x04
|
||||
|
||||
#define PFS0_MAGIC (u32)0x50465330 // "PFS0"
|
||||
|
||||
#define IVFC_MAGIC (u32)0x49564643 // "IVFC"
|
||||
#define IVFC_MAX_LEVEL 6
|
||||
|
||||
#define BKTR_MAGIC (u32)0x424B5452 // "BKTR"
|
||||
|
||||
#define ROMFS_HEADER_SIZE 0x50
|
||||
#define ROMFS_ENTRY_EMPTY (u32)0xFFFFFFFF
|
||||
|
||||
#define ROMFS_NONAME_DIRENTRY_SIZE 0x18
|
||||
#define ROMFS_NONAME_FILEENTRY_SIZE 0x20
|
||||
|
||||
#define ROMFS_ENTRY_DIR 1
|
||||
#define ROMFS_ENTRY_FILE 2
|
||||
|
||||
#define META_MAGIC (u32)0x4D455441 // "META"
|
||||
|
||||
#define NPDM_SIGNATURE_SIZE 0x100
|
||||
#define NPDM_SIGNATURE_AREA_SIZE 0x200
|
||||
|
||||
#define NSP_NCA_FILENAME_LENGTH 0x25 // NCA ID + ".nca" + NULL terminator
|
||||
#define NSP_CNMT_FILENAME_LENGTH 0x2A // NCA ID + ".cnmt.nca" / ".cnmt.xml" + NULL terminator
|
||||
#define NSP_PROGRAM_XML_FILENAME_LENGTH 0x31 // NCA ID + ".programinfo.xml" + NULL terminator
|
||||
#define NSP_NACP_XML_FILENAME_LENGTH 0x2A // NCA ID + ".nacp.xml" + NULL terminator
|
||||
#define NSP_LEGAL_XML_FILENAME_LENGTH 0x2F // NCA ID + ".legalinfo.xml" + NULL terminator
|
||||
#define NSP_TIK_FILENAME_LENGTH 0x25 // Rights ID + ".tik" + NULL terminator
|
||||
#define NSP_CERT_FILENAME_LENGTH 0x26 // Rights ID + ".cert" + NULL terminator
|
||||
|
||||
#define ETICKET_ENTRY_SIZE 0x400
|
||||
#define ETICKET_TITLEKEY_OFFSET 0x180
|
||||
#define ETICKET_RIGHTSID_OFFSET 0x2A0
|
||||
#define ETICKET_UNKNOWN_FIELD_SIZE 0x140
|
||||
#define ETICKET_DATA_OFFSET 0x140
|
||||
|
||||
#define ETICKET_CA_CERT_SIZE 0x400
|
||||
#define ETICKET_XS_CERT_SIZE 0x300
|
||||
|
||||
#define ETICKET_TIK_FILE_SIZE (ETICKET_ENTRY_SIZE - 0x140)
|
||||
#define ETICKET_CERT_FILE_SIZE (ETICKET_CA_CERT_SIZE + ETICKET_XS_CERT_SIZE)
|
||||
|
||||
#define ETICKET_TITLEKEY_COMMON 0
|
||||
#define ETICKET_TITLEKEY_PERSONALIZED 1
|
||||
|
||||
typedef enum {
|
||||
DUMP_APP_NSP = 0,
|
||||
DUMP_PATCH_NSP,
|
||||
DUMP_ADDON_NSP
|
||||
} nspDumpType;
|
||||
|
||||
typedef struct {
|
||||
u32 magic;
|
||||
u32 file_cnt;
|
||||
u32 str_table_size;
|
||||
u32 reserved;
|
||||
} PACKED pfs0_header;
|
||||
|
||||
typedef struct {
|
||||
u64 file_offset;
|
||||
u64 file_size;
|
||||
u32 filename_offset;
|
||||
u32 reserved;
|
||||
} PACKED pfs0_file_entry;
|
||||
|
||||
typedef struct {
|
||||
u32 media_start_offset;
|
||||
u32 media_end_offset;
|
||||
u8 _0x8[0x8]; /* Padding. */
|
||||
} PACKED nca_section_entry_t;
|
||||
|
||||
typedef struct {
|
||||
u8 master_hash[0x20]; /* SHA-256 hash of the hash table. */
|
||||
u32 block_size; /* In bytes. */
|
||||
u32 always_2;
|
||||
u64 hash_table_offset; /* Normally zero. */
|
||||
u64 hash_table_size;
|
||||
u64 pfs0_offset;
|
||||
u64 pfs0_size;
|
||||
u8 _0x48[0xF0];
|
||||
} PACKED pfs0_superblock_t;
|
||||
|
||||
typedef struct {
|
||||
u64 logical_offset;
|
||||
u64 hash_data_size;
|
||||
u32 block_size;
|
||||
u32 reserved;
|
||||
} PACKED ivfc_level_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
u32 magic;
|
||||
u32 id;
|
||||
u32 master_hash_size;
|
||||
u32 num_levels;
|
||||
ivfc_level_hdr_t level_headers[IVFC_MAX_LEVEL];
|
||||
u8 _0xA0[0x20];
|
||||
u8 master_hash[0x20];
|
||||
} PACKED ivfc_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
ivfc_hdr_t ivfc_header;
|
||||
u8 _0xE0[0x58];
|
||||
} PACKED romfs_superblock_t;
|
||||
|
||||
typedef struct {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 magic; /* "BKTR" */
|
||||
u32 _0x14; /* Version? */
|
||||
u32 num_entries;
|
||||
u32 _0x1C; /* Reserved? */
|
||||
} PACKED bktr_header_t;
|
||||
|
||||
typedef struct {
|
||||
ivfc_hdr_t ivfc_header;
|
||||
u8 _0xE0[0x18];
|
||||
bktr_header_t relocation_header;
|
||||
bktr_header_t subsection_header;
|
||||
} PACKED bktr_superblock_t;
|
||||
|
||||
/* NCA FS header. */
|
||||
typedef struct {
|
||||
u8 _0x0;
|
||||
u8 _0x1;
|
||||
u8 partition_type;
|
||||
u8 fs_type;
|
||||
u8 crypt_type;
|
||||
u8 _0x5[0x3];
|
||||
union { /* FS-specific superblock. Size = 0x138. */
|
||||
pfs0_superblock_t pfs0_superblock;
|
||||
romfs_superblock_t romfs_superblock;
|
||||
bktr_superblock_t bktr_superblock;
|
||||
};
|
||||
union {
|
||||
u8 section_ctr[0x8];
|
||||
struct {
|
||||
u32 section_ctr_low;
|
||||
u32 section_ctr_high;
|
||||
};
|
||||
};
|
||||
u8 _0x148[0xB8]; /* Padding. */
|
||||
} PACKED nca_fs_header_t;
|
||||
|
||||
/* Nintendo content archive header. */
|
||||
typedef struct {
|
||||
u8 fixed_key_sig[0x100]; /* RSA-PSS signature over header with fixed key. */
|
||||
u8 npdm_key_sig[0x100]; /* RSA-PSS signature over header with key in NPDM. */
|
||||
u32 magic;
|
||||
u8 distribution; /* System vs gamecard. */
|
||||
u8 content_type;
|
||||
u8 crypto_type; /* Which keyblob (field 1) */
|
||||
u8 kaek_ind; /* Which kaek index? */
|
||||
u64 nca_size; /* Entire archive size. */
|
||||
u64 title_id;
|
||||
u8 _0x218[0x4]; /* Padding. */
|
||||
union {
|
||||
u32 sdk_version; /* What SDK was this built with? */
|
||||
struct {
|
||||
u8 sdk_revision;
|
||||
u8 sdk_micro;
|
||||
u8 sdk_minor;
|
||||
u8 sdk_major;
|
||||
};
|
||||
};
|
||||
u8 crypto_type2; /* Which keyblob (field 2) */
|
||||
u8 fixed_key_generation;
|
||||
u8 _0x222[0xE]; /* Padding. */
|
||||
u8 rights_id[0x10]; /* Rights ID (for titlekey crypto). */
|
||||
nca_section_entry_t section_entries[4]; /* Section entry metadata. */
|
||||
u8 section_hashes[4][0x20]; /* SHA-256 hashes for each section header. */
|
||||
u8 nca_keys[4][0x10]; /* Key area (encrypted) */
|
||||
u8 _0x340[0xC0]; /* Padding. */
|
||||
nca_fs_header_t fs_headers[4]; /* FS section headers. */
|
||||
} PACKED nca_header_t;
|
||||
|
||||
typedef struct {
|
||||
u32 magic;
|
||||
u32 _0x4;
|
||||
u32 _0x8;
|
||||
u8 mmu_flags;
|
||||
u8 _0xD;
|
||||
u8 main_thread_prio;
|
||||
u8 default_cpuid;
|
||||
u64 _0x10;
|
||||
u32 process_category;
|
||||
u32 main_stack_size;
|
||||
char title_name[0x50];
|
||||
u32 aci0_offset;
|
||||
u32 aci0_size;
|
||||
u32 acid_offset;
|
||||
u32 acid_size;
|
||||
} PACKED npdm_t;
|
||||
|
||||
typedef struct {
|
||||
u64 title_id;
|
||||
u32 version;
|
||||
u8 type;
|
||||
u8 unk1;
|
||||
u16 extended_header_size;
|
||||
u16 content_cnt;
|
||||
u16 content_meta_cnt;
|
||||
u8 attr;
|
||||
u8 unk2[0x03];
|
||||
u32 required_dl_sysver;
|
||||
u8 unk3[0x04];
|
||||
} PACKED cnmt_header;
|
||||
|
||||
typedef struct {
|
||||
u64 patch_tid; /* Patch TID / Application TID */
|
||||
u32 min_sysver; /* Minimum system/application version */
|
||||
u32 min_appver; /* Minimum application version (only for base applications) */
|
||||
} PACKED cnmt_extended_header;
|
||||
|
||||
typedef struct {
|
||||
u8 hash[0x20];
|
||||
u8 nca_id[0x10];
|
||||
u8 size[6];
|
||||
u8 type;
|
||||
u8 id_offset;
|
||||
} PACKED cnmt_content_record;
|
||||
|
||||
typedef struct {
|
||||
u8 type;
|
||||
u64 title_id;
|
||||
u32 version;
|
||||
u32 required_dl_sysver;
|
||||
u32 nca_cnt;
|
||||
u8 digest[SHA256_HASH_SIZE];
|
||||
char digest_str[(SHA256_HASH_SIZE * 2) + 1];
|
||||
u8 min_keyblob;
|
||||
u32 min_sysver;
|
||||
u64 patch_tid;
|
||||
u32 min_appver;
|
||||
} cnmt_xml_program_info;
|
||||
|
||||
typedef struct {
|
||||
u8 type;
|
||||
u8 nca_id[SHA256_HASH_SIZE / 2];
|
||||
char nca_id_str[SHA256_HASH_SIZE + 1];
|
||||
u64 size;
|
||||
u8 hash[SHA256_HASH_SIZE];
|
||||
char hash_str[(SHA256_HASH_SIZE * 2) + 1];
|
||||
u8 keyblob;
|
||||
u8 id_offset;
|
||||
u64 cnt_record_offset; // Relative to the start of the content records section in the CNMT
|
||||
u8 decrypted_nca_keys[NCA_KEY_AREA_SIZE];
|
||||
u8 encrypted_header_mod[NCA_FULL_HEADER_LENGTH];
|
||||
} cnmt_xml_content_info;
|
||||
|
||||
typedef struct {
|
||||
u32 nca_index;
|
||||
u8 *hash_table;
|
||||
u64 hash_table_offset; // Relative to NCA start
|
||||
u64 hash_table_size;
|
||||
u8 block_mod_cnt;
|
||||
u8 *block_data[2];
|
||||
u64 block_offset[2]; // Relative to NCA start
|
||||
u64 block_size[2];
|
||||
} nca_program_mod_data;
|
||||
|
||||
typedef struct {
|
||||
char filename[100];
|
||||
u64 icon_size;
|
||||
u8 icon_data[0x20000];
|
||||
} nacp_icons_ctx;
|
||||
|
||||
typedef struct {
|
||||
u32 nca_index;
|
||||
u64 xml_size;
|
||||
char *xml_data;
|
||||
u8 nacp_icon_cnt; // Only used with Control NCAs
|
||||
nacp_icons_ctx *nacp_icons; // Only used with Control NCAs
|
||||
} xml_record_info;
|
||||
|
||||
typedef struct {
|
||||
u64 section_offset; // Relative to NCA start
|
||||
u64 section_size;
|
||||
u64 hash_table_offset; // Relative to NCA start
|
||||
u64 hash_block_size;
|
||||
u32 hash_block_cnt;
|
||||
u64 pfs0_offset; // Relative to NCA start
|
||||
u64 pfs0_size;
|
||||
u64 title_cnmt_offset; // Relative to NCA start
|
||||
u64 title_cnmt_size;
|
||||
} nca_cnmt_mod_data;
|
||||
|
||||
typedef struct {
|
||||
u32 sig_type;
|
||||
u8 signature[0x100];
|
||||
u8 padding[0x3C];
|
||||
char sig_issuer[0x40];
|
||||
u8 titlekey_block[0x100];
|
||||
u8 unk1;
|
||||
u8 titlekey_type;
|
||||
u8 unk2[0x03];
|
||||
u8 master_key_rev;
|
||||
u8 unk3[0x0A];
|
||||
u64 ticket_id;
|
||||
u64 device_id;
|
||||
u8 rights_id[0x10];
|
||||
u32 account_id;
|
||||
u8 unk4[0x0C];
|
||||
} PACKED rsa2048_sha256_ticket;
|
||||
|
||||
typedef struct {
|
||||
bool has_rights_id;
|
||||
u8 rights_id[0x10];
|
||||
char rights_id_str[33];
|
||||
char tik_filename[37];
|
||||
char cert_filename[38];
|
||||
u8 enc_titlekey[0x10];
|
||||
u8 dec_titlekey[0x10];
|
||||
u8 cert_data[ETICKET_CERT_FILE_SIZE];
|
||||
rsa2048_sha256_ticket tik_data;
|
||||
bool retrieved_tik;
|
||||
bool missing_tik;
|
||||
} title_rights_ctx;
|
||||
|
||||
typedef struct {
|
||||
NcmStorageId storageId;
|
||||
NcmContentStorage ncmStorage;
|
||||
NcmContentId ncaId;
|
||||
u8 idOffset;
|
||||
Aes128CtrContext aes_ctx;
|
||||
u64 exefs_offset; // Relative to NCA start
|
||||
u64 exefs_size;
|
||||
pfs0_header exefs_header;
|
||||
u64 exefs_entries_offset; // Relative to NCA start
|
||||
pfs0_file_entry *exefs_entries;
|
||||
u64 exefs_str_table_offset; // Relative to NCA start
|
||||
char *exefs_str_table;
|
||||
u64 exefs_data_offset; // Relative to NCA start
|
||||
} exefs_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
NcmStorageId storageId;
|
||||
NcmContentStorage ncmStorage;
|
||||
NcmContentId ncaId;
|
||||
u8 idOffset;
|
||||
Aes128CtrContext aes_ctx;
|
||||
u64 section_offset; // Relative to NCA start
|
||||
u64 section_size;
|
||||
u64 romfs_offset; // Relative to NCA start
|
||||
u64 romfs_size;
|
||||
u64 romfs_dirtable_offset; // Relative to NCA start
|
||||
u64 romfs_dirtable_size;
|
||||
romfs_dir *romfs_dir_entries;
|
||||
u64 romfs_filetable_offset; // Relative to NCA start
|
||||
u64 romfs_filetable_size;
|
||||
romfs_file *romfs_file_entries;
|
||||
u64 romfs_filedata_offset; // Relative to NCA start
|
||||
} romfs_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
u64 virt_offset;
|
||||
u64 phys_offset;
|
||||
u32 is_patch;
|
||||
} PACKED bktr_relocation_entry_t;
|
||||
|
||||
typedef struct {
|
||||
u32 _0x0;
|
||||
u32 num_entries;
|
||||
u64 virtual_offset_end;
|
||||
bktr_relocation_entry_t entries[0x3FF0 / sizeof(bktr_relocation_entry_t)];
|
||||
u8 padding[0x3FF0 % sizeof(bktr_relocation_entry_t)];
|
||||
} PACKED bktr_relocation_bucket_t;
|
||||
|
||||
typedef struct {
|
||||
u32 _0x0;
|
||||
u32 num_buckets;
|
||||
u64 total_size;
|
||||
u64 bucket_virtual_offsets[0x3FF0 / sizeof(u64)];
|
||||
bktr_relocation_bucket_t buckets[];
|
||||
} PACKED bktr_relocation_block_t;
|
||||
|
||||
typedef struct {
|
||||
u64 offset;
|
||||
u32 _0x8;
|
||||
u32 ctr_val;
|
||||
} PACKED bktr_subsection_entry_t;
|
||||
|
||||
typedef struct {
|
||||
u32 _0x0;
|
||||
u32 num_entries;
|
||||
u64 physical_offset_end;
|
||||
bktr_subsection_entry_t entries[0x3FF];
|
||||
} PACKED bktr_subsection_bucket_t;
|
||||
|
||||
typedef struct {
|
||||
u32 _0x0;
|
||||
u32 num_buckets;
|
||||
u64 total_size;
|
||||
u64 bucket_physical_offsets[0x3FF0 / sizeof(u64)];
|
||||
bktr_subsection_bucket_t buckets[];
|
||||
} PACKED bktr_subsection_block_t;
|
||||
|
||||
typedef struct {
|
||||
NcmStorageId storageId;
|
||||
NcmContentStorage ncmStorage;
|
||||
NcmContentId ncaId;
|
||||
u8 idOffset;
|
||||
Aes128CtrContext aes_ctx;
|
||||
u64 section_offset; // Relative to NCA start
|
||||
u64 section_size;
|
||||
bktr_superblock_t superblock;
|
||||
bktr_relocation_block_t *relocation_block;
|
||||
bktr_subsection_block_t *subsection_block;
|
||||
u64 virtual_seek; // Relative to section start
|
||||
u64 bktr_seek; // Relative to section start (patch BKTR section)
|
||||
u64 base_seek; // Relative to section start (base application RomFS section)
|
||||
u64 romfs_offset; // Relative to section start
|
||||
u64 romfs_size;
|
||||
u64 romfs_dirtable_offset; // Relative to section start
|
||||
u64 romfs_dirtable_size;
|
||||
romfs_dir *romfs_dir_entries;
|
||||
u64 romfs_filetable_offset; // Relative to section start
|
||||
u64 romfs_filetable_size;
|
||||
romfs_file *romfs_file_entries;
|
||||
u64 romfs_filedata_offset; // Relative to section start
|
||||
} bktr_ctx_t;
|
||||
|
||||
// Used in HFS0 / ExeFS / RomFS browsers
|
||||
typedef struct {
|
||||
u64 size;
|
||||
char sizeStr[32];
|
||||
} browser_entry_size_info;
|
||||
|
||||
typedef struct {
|
||||
u8 type; // 1 = Dir, 2 = File
|
||||
u64 offset; // Relative to directory/file table, depending on type
|
||||
browser_entry_size_info sizeInfo; // Only used if type == 2
|
||||
} romfs_browser_entry;
|
||||
|
||||
typedef struct {
|
||||
char name[0x200];
|
||||
char publisher[0x100];
|
||||
} Title;
|
||||
|
||||
typedef enum {
|
||||
Language_AmericanEnglish = 0,
|
||||
Language_BritishEnglish = 1,
|
||||
Language_Japanese = 2,
|
||||
Language_French = 3,
|
||||
Language_German = 4,
|
||||
Language_LatinAmericanSpanish = 5,
|
||||
Language_Spanish = 6,
|
||||
Language_Italian = 7,
|
||||
Language_Dutch = 8,
|
||||
Language_CanadianFrench = 9,
|
||||
Language_Portuguese = 10,
|
||||
Language_Russian = 11,
|
||||
Language_Korean = 12,
|
||||
Language_TraditionalChinese = 13,
|
||||
Language_SimplifiedChinese = 14
|
||||
} Language;
|
||||
|
||||
typedef enum {
|
||||
StartupUserAccount_None = 0,
|
||||
StartupUserAccount_Required = 1,
|
||||
StartupUserAccount_RequiredWithNetworkServiceAccountAvailable = 2
|
||||
} StartupUserAccount;
|
||||
|
||||
/* Introduced as of SDK 6.4.0 */
|
||||
typedef enum {
|
||||
UserAccountSwitchLock_Disable = 0,
|
||||
UserAccountSwitchLock_Enable = 1
|
||||
} UserAccountSwitchLock;
|
||||
|
||||
/* Introduced as of SDK 3.4.0 */
|
||||
typedef enum {
|
||||
AddOnContentRegistrationType_AllOnLaunch = 0,
|
||||
AddOnContentRegistrationType_OnDemand = 1
|
||||
} AddOnContentRegistrationType;
|
||||
|
||||
typedef struct {
|
||||
u32 AttributeFlag_Demo : 1;
|
||||
u32 AttributeFlag_RetailInteractiveDisplay : 1; /* Introduced as of SDK 3.4.0 */
|
||||
} AttributeFlag;
|
||||
|
||||
typedef struct {
|
||||
u32 SupportedLanguageFlag_AmericanEnglish : 1;
|
||||
u32 SupportedLanguageFlag_BritishEnglish : 1;
|
||||
u32 SupportedLanguageFlag_Japanese : 1;
|
||||
u32 SupportedLanguageFlag_French : 1;
|
||||
u32 SupportedLanguageFlag_German : 1;
|
||||
u32 SupportedLanguageFlag_LatinAmericanSpanish : 1;
|
||||
u32 SupportedLanguageFlag_Spanish : 1;
|
||||
u32 SupportedLanguageFlag_Italian : 1;
|
||||
u32 SupportedLanguageFlag_Dutch : 1;
|
||||
u32 SupportedLanguageFlag_CanadianFrench : 1;
|
||||
u32 SupportedLanguageFlag_Portuguese : 1;
|
||||
u32 SupportedLanguageFlag_Russian : 1;
|
||||
u32 SupportedLanguageFlag_Korean : 1;
|
||||
u32 SupportedLanguageFlag_TraditionalChinese : 1;
|
||||
u32 SupportedLanguageFlag_SimplifiedChinese : 1;
|
||||
} SupportedLanguageFlag;
|
||||
|
||||
typedef struct {
|
||||
u32 ParentalControlFlag_FreeCommunication : 1;
|
||||
} ParentalControlFlag;
|
||||
|
||||
typedef enum {
|
||||
Screenshot_Allow = 0,
|
||||
Screenshot_Deny = 1
|
||||
} Screenshot;
|
||||
|
||||
typedef enum {
|
||||
VideoCapture_Disable = 0,
|
||||
VideoCapture_Manual = 1,
|
||||
VideoCapture_Enable = 2
|
||||
} VideoCapture;
|
||||
|
||||
typedef enum {
|
||||
DataLossConfirmation_None = 0,
|
||||
DataLossConfirmation_Required = 1
|
||||
} DataLossConfirmation;
|
||||
|
||||
typedef enum {
|
||||
PlayLogPolicy_All = 0,
|
||||
PlayLogPolicy_LogOnly = 1,
|
||||
PlayLogPolicy_None = 2
|
||||
} PlayLogPolicy;
|
||||
|
||||
typedef enum {
|
||||
RatingAgeOrganization_CERO = 0,
|
||||
RatingAgeOrganization_GRACGCRB = 1,
|
||||
RatingAgeOrganization_GSRMR = 2,
|
||||
RatingAgeOrganization_ESRB = 3,
|
||||
RatingAgeOrganization_ClassInd = 4,
|
||||
RatingAgeOrganization_USK = 5,
|
||||
RatingAgeOrganization_PEGI = 6,
|
||||
RatingAgeOrganization_PEGIPortugal = 7,
|
||||
RatingAgeOrganization_PEGIBBFC = 8,
|
||||
RatingAgeOrganization_Russian = 9,
|
||||
RatingAgeOrganization_ACB = 10,
|
||||
RatingAgeOrganization_OFLC = 11,
|
||||
RatingAgeOrganization_IARCGeneric = 12 /* Introduced as of SDK 9.3.1 */
|
||||
} RatingAgeOrganization;
|
||||
|
||||
typedef struct {
|
||||
u8 cero;
|
||||
u8 gracgcrb;
|
||||
u8 gsrmr;
|
||||
u8 esrb;
|
||||
u8 class_ind;
|
||||
u8 usk;
|
||||
u8 pegi;
|
||||
u8 pegi_portugal;
|
||||
u8 pegibbfc;
|
||||
u8 russian;
|
||||
u8 acb;
|
||||
u8 oflc;
|
||||
u8 iarc_generic;
|
||||
u8 reserved_1[0x13];
|
||||
} RatingAge;
|
||||
|
||||
typedef enum {
|
||||
LogoType_LicensedByNintendo = 0,
|
||||
LogoType_DistributedByNintendo = 1, /* Removed in SDK 3.5.2 */
|
||||
LogoType_Nintendo = 2
|
||||
} LogoType;
|
||||
|
||||
typedef enum {
|
||||
LogoHandling_Auto = 0,
|
||||
LogoHandling_Manual = 1
|
||||
} LogoHandling;
|
||||
|
||||
/* Introduced as of SDK 4.5.0 */
|
||||
typedef enum {
|
||||
RuntimeAddOnContentInstall_Deny = 0,
|
||||
RuntimeAddOnContentInstall_AllowAppend = 1
|
||||
} RuntimeAddOnContentInstall;
|
||||
|
||||
/* Introduced as of SDK 9.3.1 */
|
||||
typedef enum {
|
||||
RuntimeParameterDelivery_Always = 0,
|
||||
RuntimeParameterDelivery_AlwaysIfUserStateMatched = 1,
|
||||
RuntimeParameterDelivery_OnRestart = 2
|
||||
} RuntimeParameterDelivery;
|
||||
|
||||
/* Introduced as of SDK 3.5.2 */
|
||||
typedef enum {
|
||||
CrashReport_Deny = 0,
|
||||
CrashReport_Allow = 1
|
||||
} CrashReport;
|
||||
|
||||
typedef enum {
|
||||
Hdcp_None = 0,
|
||||
Hdcp_Required = 1
|
||||
} Hdcp;
|
||||
|
||||
/* Introduced as of SDK 7.6.0 */
|
||||
typedef struct {
|
||||
u8 StartupUserAccountOptionFlag_IsOptional : 1;
|
||||
} StartupUserAccountOptionFlag;
|
||||
|
||||
/* Introduced as of SDK 5.3.0 */
|
||||
typedef enum {
|
||||
PlayLogQueryCapability_None = 0,
|
||||
PlayLogQueryCapability_WhiteList = 1,
|
||||
PlayLogQueryCapability_All = 2
|
||||
} PlayLogQueryCapability;
|
||||
|
||||
/* Introduced as of SDK 5.3.0 */
|
||||
typedef struct {
|
||||
u8 RepairFlag_SuppressGameCardAccess : 1;
|
||||
} RepairFlag;
|
||||
|
||||
/* Introduced as of SDK 6.4.0 */
|
||||
typedef struct {
|
||||
u8 RequiredNetworkServiceLicenseOnLaunchFlag_Common : 1;
|
||||
} RequiredNetworkServiceLicenseOnLaunchFlag;
|
||||
|
||||
typedef struct {
|
||||
u64 group_id;
|
||||
u8 key[0x10];
|
||||
} NeighborDetectionGroupConfiguration;
|
||||
|
||||
typedef struct {
|
||||
NeighborDetectionGroupConfiguration send_group_configuration;
|
||||
NeighborDetectionGroupConfiguration receivable_group_configurations[0x10];
|
||||
} NeighborDetectionClientConfiguration;
|
||||
|
||||
/* Introduced as of SDK 7.6.0 */
|
||||
typedef enum {
|
||||
JitConfigurationFlag_None = 0,
|
||||
JitConfigurationFlag_Enabled = 1
|
||||
} JitConfigurationFlag;
|
||||
|
||||
typedef struct {
|
||||
u64 jit_configuration_flag;
|
||||
u64 memory_size;
|
||||
} JitConfiguration;
|
||||
|
||||
typedef struct {
|
||||
Title titles[0x10];
|
||||
char isbn[0x25];
|
||||
u8 startup_user_account;
|
||||
u8 user_account_switch_lock; /* Old: touch_screen_usage (None, Supported, Required) */
|
||||
u8 add_on_content_registration_type;
|
||||
AttributeFlag attribute_flag;
|
||||
SupportedLanguageFlag supported_language_flag;
|
||||
ParentalControlFlag parental_control_flag;
|
||||
u8 screenshot;
|
||||
u8 video_capture;
|
||||
u8 data_loss_confirmation;
|
||||
u8 play_log_policy;
|
||||
u64 presence_group_id;
|
||||
RatingAge rating_ages;
|
||||
char display_version[0x10];
|
||||
u64 add_on_content_base_id;
|
||||
u64 save_data_owner_id;
|
||||
u64 user_account_save_data_size;
|
||||
u64 user_account_save_data_journal_size;
|
||||
u64 device_save_data_size;
|
||||
u64 device_save_data_journal_size;
|
||||
u64 bcat_delivery_cache_storage_size;
|
||||
char application_error_code_category[0x8];
|
||||
u64 local_communication_ids[0x8];
|
||||
u8 logo_type;
|
||||
u8 logo_handling;
|
||||
u8 runtime_add_on_content_install;
|
||||
u8 runtime_parameter_delivery;
|
||||
u8 reserved_1[0x2];
|
||||
u8 crash_report;
|
||||
u8 hdcp;
|
||||
u64 seed_for_pseudo_device_id;
|
||||
char bcat_passphrase[0x41];
|
||||
StartupUserAccountOptionFlag startup_user_account_option;
|
||||
u8 reserved_2[0x6];
|
||||
u64 user_account_save_data_size_max;
|
||||
u64 user_account_save_data_journal_size_max;
|
||||
u64 device_save_data_size_max;
|
||||
u64 device_save_data_journal_size_max;
|
||||
u64 temporary_storage_size;
|
||||
u64 cache_storage_size;
|
||||
u64 cache_storage_journal_size;
|
||||
u64 cache_storage_data_and_journal_size_max;
|
||||
u16 cache_storage_index_max;
|
||||
u8 reserved_3[0x6];
|
||||
u64 play_log_queryable_application_ids[0x10];
|
||||
u8 play_log_query_capability;
|
||||
RepairFlag repair_flag;
|
||||
u8 program_index;
|
||||
RequiredNetworkServiceLicenseOnLaunchFlag required_network_service_license_on_launch_flag;
|
||||
u8 reserved_4[0x4];
|
||||
NeighborDetectionClientConfiguration neighbor_detection_client_configuration;
|
||||
JitConfiguration jit_configuration;
|
||||
u8 reserved_5[0xC40];
|
||||
} nacp_t;
|
||||
|
||||
char *getContentType(u8 type);
|
||||
|
||||
void generateCnmtXml(cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, char *out);
|
||||
|
||||
void convertNcaSizeToU64(const u8 size[0x6], u64 *out);
|
||||
|
||||
void convertU64ToNcaSize(const u64 size, u8 out[0x6]);
|
||||
|
||||
bool readNcaDataByContentId(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, u64 offset, void *outBuf, size_t bufSize);
|
||||
|
||||
bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *ctx, u64 offset, void *outBuf, size_t bufSize, bool encrypt);
|
||||
|
||||
bool readBktrSectionBlock(u64 offset, void *outBuf, size_t bufSize);
|
||||
|
||||
bool encryptNcaHeader(nca_header_t *input, u8 *outBuf, u64 outBufSize);
|
||||
|
||||
bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title_rights_ctx *rights_info, u8 *decrypted_nca_keys, bool retrieveTitleKeyData);
|
||||
|
||||
bool retrieveTitleKeyFromGameCardTicket(title_rights_ctx *rights_info, u8 *decrypted_nca_keys);
|
||||
|
||||
bool processProgramNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, cnmt_xml_content_info *xml_content_info, nca_program_mod_data **output, u32 *cur_mod_cnt, u32 idx);
|
||||
|
||||
bool retrieveCnmtNcaData(NcmStorageId curStorageId, u8 *ncaBuf, cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, u32 cnmtNcaIndex, nca_cnmt_mod_data *output, title_rights_ctx *rights_info);
|
||||
|
||||
bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, nca_cnmt_mod_data *cnmt_mod);
|
||||
|
||||
bool parseExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
|
||||
|
||||
bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
|
||||
|
||||
bool parseBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
|
||||
|
||||
bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, bool useCustomAcidRsaPubKey, char **outBuf, u64 *outBufSize);
|
||||
|
||||
bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **out_nacp_xml, u64 *out_nacp_xml_size, nacp_icons_ctx **out_nacp_icons_ctx, u8 *out_nacp_icons_ctx_cnt);
|
||||
|
||||
bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **outBuf, u64 *outBufSize);
|
||||
|
||||
#endif
|
|
@ -19,63 +19,6 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define LOGFILE(fmt, ...) utilsWriteLogMessage(__func__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member)
|
||||
|
||||
#define SLEEP(x) svcSleepThread((x) * (u64)1000000000)
|
||||
|
||||
static Mutex g_logfileMutex = 0;
|
||||
|
||||
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...)
|
||||
{
|
||||
mutexLock(&g_logfileMutex);
|
||||
|
||||
va_list args;
|
||||
FILE *logfile = NULL;
|
||||
|
||||
logfile = fopen(APP_BASE_PATH "log.txt", "a+");
|
||||
if (!logfile) return;
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm *ts = localtime(&now);
|
||||
|
||||
fprintf(logfile, "%d-%d-%d %d:%d:%d -> %s: ", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, func_name);
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(logfile, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
fprintf(logfile, "\r\n");
|
||||
|
||||
fclose(logfile);
|
||||
|
||||
mutexUnlock(&g_logfileMutex);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
UtilsCustomFirmwareType_Atmosphere = 0,
|
||||
UtilsCustomFirmwareType_SXOS = 1,
|
||||
UtilsCustomFirmwareType_ReiNX = 2
|
||||
} UtilsCustomFirmwareType;
|
||||
|
||||
|
||||
typedef struct {
|
||||
u16 major : 6;
|
||||
u16 minor : 6;
|
||||
u16 micro : 4;
|
||||
u16 bugfix;
|
||||
} TitleVersion;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define CONFIG_PATH APP_BASE_PATH "config.bin"
|
||||
#define NRO_NAME APP_TITLE ".nro"
|
||||
#define NRO_PATH APP_BASE_PATH NRO_NAME
|
|
@ -9,7 +9,9 @@
|
|||
#define CERT_SAVEFILE_PATH "sys:/save/80000000000000e0"
|
||||
#define CERT_SAVEFILE_STORAGE_BASE_PATH "/certificate/"
|
||||
|
||||
#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig_PubKeyRsa4096 : (pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig_PubKeyRsa2048 : CertType_Sig##sig_PubKeyEcsda240))
|
||||
#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : (pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcsda240))
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static u8 certGetCertificateType(const void *data, u64 data_size);
|
||||
static u32 certGetCertificateCountInSignatureIssuer(const char *issuer);
|
|
@ -3,6 +3,7 @@
|
|||
#ifndef __CERT_H__
|
||||
#define __CERT_H__
|
||||
|
||||
#include <switch/types.h>
|
||||
#include "signature.h"
|
||||
|
||||
#define CERT_MAX_SIZE 0x500 /* Equivalent to sizeof(CertSigRsa4096PubKeyRsa4096) */
|
|
@ -1,4 +1,3 @@
|
|||
#include <switch/arm/atomics.h>
|
||||
#include <switch/services/sm.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -34,7 +33,7 @@ Result esCountPersonalizedTicket(s32 *out_count)
|
|||
return rc;
|
||||
}
|
||||
|
||||
Result esListCommonTicket(s32 *out_entries_written, FsRightsId *out_ids, s32 count);
|
||||
Result esListCommonTicket(s32 *out_entries_written, FsRightsId *out_ids, s32 count)
|
||||
{
|
||||
struct {
|
||||
s32 num_rights_ids_written;
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <switch.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "../utils.h"
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
55
source/fspusb.c
Normal file
55
source/fspusb.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
#include "fspusb.h"
|
||||
#include "service_guard.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static Service g_fspusbSrv;
|
||||
|
||||
NX_GENERATE_SERVICE_GUARD(fspusb);
|
||||
|
||||
Result _fspusbInitialize(void) {
|
||||
return smGetService(&g_fspusbSrv, "fsp-usb");
|
||||
}
|
||||
|
||||
void _fspusbCleanup(void) {
|
||||
serviceClose(&g_fspusbSrv);
|
||||
}
|
||||
|
||||
Service* fspusbGetServiceSession(void) {
|
||||
return &g_fspusbSrv;
|
||||
}
|
||||
|
||||
Result fspusbListMountedDrives(s32 *drives_buf, size_t drive_count, s32 *out_total) {
|
||||
return serviceDispatchOut(&g_fspusbSrv, 0, *out_total,
|
||||
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
|
||||
.buffers = { { drives_buf, drive_count * sizeof(s32) } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fspusbGetDriveFileSystemType(s32 interface_id, FspUsbFileSystemType *out_type) {
|
||||
return serviceDispatchInOut(&g_fspusbSrv, 1, interface_id, *out_type);
|
||||
}
|
||||
|
||||
Result fspusbGetDriveLabel(s32 interface_id, char *out_label, size_t out_label_size) {
|
||||
return serviceDispatchIn(&g_fspusbSrv, 2, interface_id,
|
||||
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
|
||||
.buffers = { { out_label, out_label_size } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fspusbSetDriveLabel(s32 interface_id, const char *label) {
|
||||
char inputlbl[11 + 1] = {0}; // Actual limit is 11 characters
|
||||
strncpy(inputlbl, label, 11);
|
||||
return serviceDispatchIn(&g_fspusbSrv, 3, interface_id,
|
||||
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
|
||||
.buffers = { { inputlbl, 11 + 1 } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fspusbOpenDriveFileSystem(s32 interface_id, FsFileSystem *out_fs) {
|
||||
return serviceDispatchIn(&g_fspusbSrv, 4, interface_id,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out_fs->s,
|
||||
);
|
||||
}
|
37
source/fspusb.h
Normal file
37
source/fspusb.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* @file fspusb.h
|
||||
* @brief USB filesystem extension (fsp-usb) service IPC wrapper.
|
||||
* @author XorTroll
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef __FSPUSB_H__
|
||||
#define __FSPUSB_H__
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
/// This is basically FATFS' file system types.
|
||||
typedef enum {
|
||||
FspUsbFileSystemType_FAT12 = 1,
|
||||
FspUsbFileSystemType_FAT16 = 2,
|
||||
FspUsbFileSystemType_FAT32 = 3,
|
||||
FspUsbFileSystemType_exFAT = 4,
|
||||
} FspUsbFileSystemType;
|
||||
|
||||
/// Initialize fsp-usb.
|
||||
Result fspusbInitialize(void);
|
||||
|
||||
/// Exit fsp-usb.
|
||||
void fspusbExit(void);
|
||||
|
||||
/// Gets the Service object for the actual fsp-usb service session.
|
||||
Service* fspusbGetServiceSession(void);
|
||||
|
||||
Result fspusbListMountedDrives(s32 *drives_buf, size_t drive_count, s32 *out_total);
|
||||
Result fspusbGetDriveFileSystemType(s32 interface_id, FspUsbFileSystemType *out_type);
|
||||
Result fspusbGetDriveLabel(s32 interface_id, char *out_label, size_t out_label_size);
|
||||
Result fspusbSetDriveLabel(s32 interface_id, const char *label);
|
||||
Result fspusbOpenDriveFileSystem(s32 interface_id, FsFileSystem *out_fs);
|
||||
|
||||
#endif /* __FSPUSB_H__ */
|
|
@ -16,12 +16,24 @@
|
|||
#define GAMECARD_ECC_BLOCK_SIZE 0x200
|
||||
#define GAMECARD_ECC_DATA_SIZE 0x24
|
||||
|
||||
#define GAMECARD_STORAGE_AREA_NAME(x) ((x) == GameCardStorageArea_Normal ? "normal" : ((x) == GameCardStorageArea_Secure ? "secure" : "none"))
|
||||
|
||||
/* Type definitions. */
|
||||
|
||||
typedef enum {
|
||||
GameCardStorageArea_None = 0,
|
||||
GameCardStorageArea_Normal = 1,
|
||||
GameCardStorageArea_Secure = 2
|
||||
} GameCardStorageArea;
|
||||
|
||||
typedef struct {
|
||||
u64 offset; ///< Relative to the start of the gamecard header.
|
||||
u64 size; ///< Whole partition size.
|
||||
u8 *header; ///< GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table.
|
||||
} GameCardHashFileSystemPartitionInfo;
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static FsDeviceOperator g_deviceOperator = {0};
|
||||
static FsEventNotifier g_gameCardEventNotifier = {0};
|
||||
static Event g_gameCardKernelEvent = {0};
|
||||
|
@ -33,20 +45,23 @@ static mtx_t g_gameCardSharedDataMutex;
|
|||
static bool g_gameCardDetectionThreadCreated = false, g_gameCardInserted = false, g_gameCardInfoLoaded = false;
|
||||
|
||||
static FsGameCardHandle g_gameCardHandle = {0};
|
||||
static FsStorage g_gameCardStorageNormal = {0}, g_gameCardStorageSecure = {0};
|
||||
static FsStorage g_gameCardStorage = {0};
|
||||
static u8 g_gameCardStorageCurrentArea = GameCardStorageArea_None;
|
||||
static u8 *g_gameCardReadBuf = NULL;
|
||||
|
||||
static GameCardHeader g_gameCardHeader = {0};
|
||||
static u64 g_gameCardStorageNormalAreaSize = 0, g_gameCardStorageSecureAreaSize = 0;
|
||||
|
||||
static u8 *g_gameCardHfsRootHeader = NULL; /* GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table */
|
||||
static u8 *g_gameCardHfsRootHeader = NULL; /// GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table.
|
||||
static GameCardHashFileSystemPartitionInfo *g_gameCardHfsPartitions = NULL;
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static bool gamecardCreateDetectionThread(void);
|
||||
static void gamecardDestroyDetectionThread(void);
|
||||
static int gamecardDetectionThreadFunc(void *arg);
|
||||
|
||||
static inline bool gamecardCheckIfInserted(void);
|
||||
static inline bool gamecardIsInserted(void);
|
||||
|
||||
static void gamecardLoadInfo(void);
|
||||
static void gamecardFreeInfo(void);
|
||||
|
@ -54,16 +69,16 @@ static void gamecardFreeInfo(void);
|
|||
static bool gamecardGetHandle(void);
|
||||
static inline void gamecardCloseHandle(void);
|
||||
|
||||
static bool gamecardOpenStorageAreas(void);
|
||||
static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock);
|
||||
static void gamecardCloseStorageAreas(void);
|
||||
static bool gamecardOpenStorageArea(u8 area);
|
||||
static bool gamecardReadStorageArea(void *out, u64 out_size, u64 offset, bool lock);
|
||||
static void gamecardCloseStorageArea(void);
|
||||
|
||||
static bool gamecardGetSizesFromStorageAreas(void);
|
||||
|
||||
/* Service guard used to generate thread-safe initialize + exit functions */
|
||||
NX_GENERATE_SERVICE_GUARD(gamecard);
|
||||
|
||||
bool gamecardCheckReadyStatus(void)
|
||||
bool gamecardIsReady(void)
|
||||
{
|
||||
mtx_lock(&g_gameCardSharedDataMutex);
|
||||
bool status = (g_gameCardInserted && g_gameCardInfoLoaded);
|
||||
|
@ -71,9 +86,9 @@ bool gamecardCheckReadyStatus(void)
|
|||
return status;
|
||||
}
|
||||
|
||||
bool gamecardStorageRead(void *out, u64 out_size, u64 offset)
|
||||
bool gamecardRead(void *out, u64 out_size, u64 offset)
|
||||
{
|
||||
return _gamecardStorageRead(out, out_size, offset, true);
|
||||
return gamecardReadStorageArea(out, out_size, offset, true);
|
||||
}
|
||||
|
||||
bool gamecardGetHeader(GameCardHeader *out)
|
||||
|
@ -321,10 +336,10 @@ static int gamecardDetectionThreadFunc(void *arg)
|
|||
mtx_lock(&g_gameCardSharedDataMutex);
|
||||
|
||||
/* Retrieve initial gamecard insertion status */
|
||||
g_gameCardInserted = prev_status = gamecardCheckIfInserted();
|
||||
g_gameCardInserted = prev_status = gamecardIsInserted();
|
||||
|
||||
/* Load gamecard info right away if a gamecard is inserted and if a handle can be retrieved */
|
||||
if (g_gameCardInserted && gamecardGetHandle()) gamecardLoadInfo();
|
||||
/* Load gamecard info right away if a gamecard is inserted */
|
||||
if (g_gameCardInserted) gamecardLoadInfo();
|
||||
|
||||
mtx_unlock(&g_gameCardSharedDataMutex);
|
||||
|
||||
|
@ -341,19 +356,18 @@ static int gamecardDetectionThreadFunc(void *arg)
|
|||
/* Only proceed if we're dealing with a status change */
|
||||
mtx_lock(&g_gameCardSharedDataMutex);
|
||||
|
||||
g_gameCardInserted = gamecardCheckIfInserted();
|
||||
g_gameCardInserted = gamecardIsInserted();
|
||||
|
||||
if (!prev_status && g_gameCardInserted)
|
||||
{
|
||||
/* Don't access the gamecard immediately to avoid conflicts with HOS / sysmodules */
|
||||
SLEEP(GAMECARD_ACCESS_WAIT_TIME);
|
||||
|
||||
/* Load gamecard info if a gamecard is inserted and if a handle can be retrieved */
|
||||
if (gamecardGetHandle()) gamecardLoadInfo();
|
||||
/* Load gamecard info */
|
||||
gamecardLoadInfo();
|
||||
} else {
|
||||
/* Free gamecard info and close gamecard handle */
|
||||
/* Free gamecard info */
|
||||
gamecardFreeInfo();
|
||||
gamecardCloseHandle();
|
||||
}
|
||||
|
||||
prev_status = g_gameCardInserted;
|
||||
|
@ -364,14 +378,13 @@ static int gamecardDetectionThreadFunc(void *arg)
|
|||
/* Free gamecard info and close gamecard handle */
|
||||
mtx_lock(&g_gameCardSharedDataMutex);
|
||||
gamecardFreeInfo();
|
||||
gamecardCloseHandle();
|
||||
g_gameCardInserted = false;
|
||||
mtx_unlock(&g_gameCardSharedDataMutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool gamecardCheckIfInserted(void)
|
||||
static inline bool gamecardIsInserted(void)
|
||||
{
|
||||
bool inserted = false;
|
||||
Result rc = fsDeviceOperatorIsGameCardInserted(&g_deviceOperator, &inserted);
|
||||
|
@ -386,15 +399,16 @@ static void gamecardLoadInfo(void)
|
|||
GameCardHashFileSystemHeader *fs_header = NULL;
|
||||
GameCardHashFileSystemEntry *fs_entry = NULL;
|
||||
|
||||
/* Open gamecard storage areas */
|
||||
if (!gamecardOpenStorageAreas())
|
||||
/* Retrieve gamecard storage area sizes */
|
||||
/* gamecardReadStorageArea() actually checks if the storage area sizes are greater than zero, so we must first perform this step */
|
||||
if (!gamecardGetSizesFromStorageAreas())
|
||||
{
|
||||
LOGFILE("Failed to open gamecard storage areas!");
|
||||
LOGFILE("Failed to retrieve gamecard storage area sizes!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read gamecard header */
|
||||
if (!_gamecardStorageRead(&g_gameCardHeader, sizeof(GameCardHeader), 0, false))
|
||||
if (!gamecardReadStorageArea(&g_gameCardHeader, sizeof(GameCardHeader), 0, false))
|
||||
{
|
||||
LOGFILE("Failed to read gamecard header!");
|
||||
goto out;
|
||||
|
@ -409,9 +423,9 @@ static void gamecardLoadInfo(void)
|
|||
|
||||
if (utilsGetCustomFirmwareType() == UtilsCustomFirmwareType_SXOS)
|
||||
{
|
||||
/* Total size for the secure storage area is maxed out under SX OS */
|
||||
/* The total size for the secure storage area is maxed out under SX OS */
|
||||
/* Let's try to calculate it manually */
|
||||
u64 capacity = gamecardGetCapacity(&g_gameCardHeader);
|
||||
u64 capacity = gamecardGetCapacityFromHeader(&g_gameCardHeader);
|
||||
if (!capacity)
|
||||
{
|
||||
LOGFILE("Invalid gamecard capacity value! (0x%02X)", g_gameCardHeader.rom_size);
|
||||
|
@ -430,7 +444,7 @@ static void gamecardLoadInfo(void)
|
|||
}
|
||||
|
||||
/* Read root hash FS header */
|
||||
if (!_gamecardStorageRead(g_gameCardHfsRootHeader, g_gameCardHeader.partition_fs_header_size, g_gameCardHeader.partition_fs_header_address, false))
|
||||
if (!gamecardReadStorageArea(g_gameCardHfsRootHeader, g_gameCardHeader.partition_fs_header_size, g_gameCardHeader.partition_fs_header_address, false))
|
||||
{
|
||||
LOGFILE("Failed to read root hash FS header from offset 0x%lX!", g_gameCardHeader.partition_fs_header_address);
|
||||
goto out;
|
||||
|
@ -475,7 +489,7 @@ static void gamecardLoadInfo(void)
|
|||
|
||||
/* Partially read the current hash FS partition header */
|
||||
GameCardHashFileSystemHeader partition_header = {0};
|
||||
if (!_gamecardStorageRead(&partition_header, sizeof(GameCardHashFileSystemHeader), g_gameCardHfsPartitions[i].offset, false))
|
||||
if (!gamecardReadStorageArea(&partition_header, sizeof(GameCardHashFileSystemHeader), g_gameCardHfsPartitions[i].offset, false))
|
||||
{
|
||||
LOGFILE("Failed to partially read hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset);
|
||||
goto out;
|
||||
|
@ -505,7 +519,7 @@ static void gamecardLoadInfo(void)
|
|||
}
|
||||
|
||||
/* Finally, read the full hash FS partition header */
|
||||
if (!_gamecardStorageRead(g_gameCardHfsPartitions[i].header, partition_header_size, g_gameCardHfsPartitions[i].offset, false))
|
||||
if (!gamecardReadStorageArea(g_gameCardHfsPartitions[i].header, partition_header_size, g_gameCardHfsPartitions[i].offset, false))
|
||||
{
|
||||
LOGFILE("Failed to read full hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset);
|
||||
goto out;
|
||||
|
@ -521,6 +535,8 @@ out:
|
|||
static void gamecardFreeInfo(void)
|
||||
{
|
||||
memset(&g_gameCardHeader, 0, sizeof(GameCardHeader));
|
||||
g_gameCardStorageNormalAreaSize = 0;
|
||||
g_gameCardStorageSecureAreaSize = 0;
|
||||
|
||||
if (g_gameCardHfsRootHeader)
|
||||
{
|
||||
|
@ -544,7 +560,7 @@ static void gamecardFreeInfo(void)
|
|||
g_gameCardHfsPartitions = NULL;
|
||||
}
|
||||
|
||||
gamecardCloseStorageAreas();
|
||||
gamecardCloseStorageArea();
|
||||
|
||||
g_gameCardInfoLoaded = false;
|
||||
}
|
||||
|
@ -557,8 +573,6 @@ static bool gamecardGetHandle(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (g_gameCardInfoLoaded && g_gameCardHandle.value) return true;
|
||||
|
||||
Result rc1 = 0, rc2 = 0;
|
||||
FsStorage tmp_storage = {0};
|
||||
|
||||
|
@ -598,58 +612,50 @@ static inline void gamecardCloseHandle(void)
|
|||
g_gameCardHandle.value = 0;
|
||||
}
|
||||
|
||||
static bool gamecardOpenStorageAreas(void)
|
||||
static bool gamecardOpenStorageArea(u8 area)
|
||||
{
|
||||
if (!g_gameCardInserted || !g_gameCardHandle.value)
|
||||
if (!g_gameCardInserted || (area != GameCardStorageArea_Normal && area != GameCardStorageArea_Secure))
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_gamecardInfoLoaded && serviceIsActive(&(g_gameCardStorageNormal.s)) && serviceIsActive(&(g_gameCardStorageSecure.s))) return true;
|
||||
if (g_gameCardHandle.value && serviceIsActive(&(g_gameCardStorage.s)) && g_gameCardStorageCurrentArea == area) return true;
|
||||
|
||||
gamecardCloseStorageAreas();
|
||||
gamecardCloseStorageArea();
|
||||
|
||||
Result rc = 0;
|
||||
bool success = false;
|
||||
u32 partition = (area - 1); /* Zero-based index */
|
||||
|
||||
rc = fsOpenGameCardStorage(&g_gameCardStorageNormal, &g_gameCardHandle, 0);
|
||||
/* Retrieve a new gamecard handle */
|
||||
if (!gamecardGetHandle())
|
||||
{
|
||||
LOGFILE("Failed to retrieve gamecard handle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Open storage area */
|
||||
rc = fsOpenGameCardStorage(&g_gameCardStorage, &g_gameCardHandle, partition);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("fsOpenGameCardStorage failed! (0x%08X) (normal)", rc);
|
||||
goto out;
|
||||
LOGFILE("fsOpenGameCardStorage failed to open %s storage area! (0x%08X)", GAMECARD_STORAGE_AREA_NAME(area), rc);
|
||||
gamecardCloseHandle();
|
||||
return false;
|
||||
}
|
||||
|
||||
rc = fsOpenGameCardStorage(&g_gameCardStorageSecure, &g_gameCardHandle, 1);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("fsOpenGameCardStorage failed! (0x%08X) (secure)", rc);
|
||||
goto out;
|
||||
}
|
||||
g_gameCardStorageCurrentArea = area;
|
||||
|
||||
if (!gamecardGetSizesFromStorageAreas())
|
||||
{
|
||||
LOGFILE("Failed to retrieve sizes from storage areas!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
out:
|
||||
if (!success) gamecardCloseStorageAreas();
|
||||
|
||||
return success;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock)
|
||||
static bool gamecardReadStorageArea(void *out, u64 out_size, u64 offset, bool lock)
|
||||
{
|
||||
if (lock) mtx_lock(&g_gameCardSharedDataMutex);
|
||||
|
||||
bool success = false;
|
||||
|
||||
if (!g_gameCardInserted || !serviceIsActive(&(g_gameCardStorageNormal.s)) || !g_gameCardStorageNormalAreaSize || !serviceIsActive(&(g_gameCardStorageSecure.s)) || \
|
||||
!g_gameCardStorageSecureAreaSize || !out || !out_size || offset >= (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize) || \
|
||||
(offset + out_size) > (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize))
|
||||
if (!g_gameCardInserted || !g_gameCardStorageNormalAreaSize || !g_gameCardStorageSecureAreaSize || !out || !out_size || \
|
||||
offset >= (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize) || (offset + out_size) > (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize))
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
goto out;
|
||||
|
@ -657,33 +663,41 @@ static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock)
|
|||
|
||||
Result rc = 0;
|
||||
u8 *out_u8 = (u8*)out;
|
||||
u8 area = (offset < g_gameCardStorageNormalAreaSize ? GameCardStorageArea_Normal : GameCardStorageArea_Secure);
|
||||
|
||||
/* Handle reads between the end of the normal storage area and the start of the secure storage area */
|
||||
if (offset < g_gameCardStorageNormalAreaSize && (offset + out_size) > g_gameCardStorageNormalAreaSize)
|
||||
/* Handle reads that span both the normal and secure gamecard storage areas */
|
||||
if (area == GameCardStorageArea_Normal && (offset + out_size) > g_gameCardStorageNormalAreaSize)
|
||||
{
|
||||
/* Calculate normal storage area size difference */
|
||||
u64 diff_size = (g_gameCardStorageNormalAreaSize - offset);
|
||||
|
||||
if (!_gamecardStorageRead(out_u8, diff_size, offset, false)) goto out;
|
||||
if (!gamecardReadStorageArea(out_u8, diff_size, offset, false)) goto out;
|
||||
|
||||
/* Adjust variables to start reading right from the start of the secure storage area */
|
||||
out_u8 += diff_size;
|
||||
offset = g_gameCardStorageNormalAreaSize;
|
||||
/* Adjust variables to read right from the start of the secure storage area */
|
||||
out_size -= diff_size;
|
||||
offset = g_gameCardStorageNormalAreaSize;
|
||||
out_u8 += diff_size;
|
||||
area = GameCardStorageArea_Secure;
|
||||
}
|
||||
|
||||
/* Open a storage area if needed */
|
||||
/* If the right storage area has already been opened, this will return true */
|
||||
if (!gamecardOpenStorageArea(area))
|
||||
{
|
||||
LOGFILE("Failed to open %s storage area!", GAMECARD_STORAGE_AREA_NAME(area));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Calculate appropiate storage area offset and retrieve the right storage area pointer */
|
||||
const char *area = (offset < g_gameCardStorageNormalAreaSize ? "normal" : "secure");
|
||||
u64 base_offset = (offset < g_gameCardStorageNormalAreaSize ? offset : (offset - g_gameCardStorageNormalAreaSize));
|
||||
FsStorage *storage = (offset < g_gameCardStorageNormalAreaSize ? &g_gameCardStorageNormal : &g_gameCardStorageSecure);
|
||||
u64 base_offset = (area == GameCardStorageArea_Normal ? offset : (offset - g_gameCardStorageNormalAreaSize));
|
||||
|
||||
if (!(base_offset % GAMECARD_MEDIA_UNIT_SIZE) && !(out_size % GAMECARD_MEDIA_UNIT_SIZE))
|
||||
{
|
||||
/* Optimization for reads that are already aligned to GAMECARD_MEDIA_UNIT_SIZE bytes */
|
||||
rc = fsStorageRead(storage, base_offset, out_u8, out_size);
|
||||
rc = fsStorageRead(&g_gameCardStorage, base_offset, out_u8, out_size);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (aligned)", out_size, base_offset, area, rc);
|
||||
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (aligned)", out_size, base_offset, GAMECARD_STORAGE_AREA_NAME(area), rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -691,22 +705,22 @@ static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock)
|
|||
} else {
|
||||
/* Fix offset and/or size to avoid unaligned reads */
|
||||
u64 block_start_offset = (base_offset - (base_offset % GAMECARD_MEDIA_UNIT_SIZE));
|
||||
u64 block_end_offset = round_up(base_offset + out_size, GAMECARD_MEDIA_UNIT_SIZE);
|
||||
u64 block_end_offset = ROUND_UP(base_offset + out_size, GAMECARD_MEDIA_UNIT_SIZE);
|
||||
u64 block_size = (block_end_offset - block_start_offset);
|
||||
|
||||
u64 chunk_size = (block_size > GAMECARD_READ_BUFFER_SIZE ? GAMECARD_READ_BUFFER_SIZE : block_size);
|
||||
u64 out_chunk_size = (block_size > GAMECARD_READ_BUFFER_SIZE ? (GAMECARD_READ_BUFFER_SIZE - (base_offset - block_start_offset)) : out_size);
|
||||
|
||||
rc = fsStorageRead(storage, block_start_offset, g_gameCardReadBuf, chunk_size);
|
||||
if (!R_FAILED(rc))
|
||||
rc = fsStorageRead(&g_gameCardStorage, block_start_offset, g_gameCardReadBuf, chunk_size);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (unaligned)", chunk_size, block_start_offset, area, rc);
|
||||
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (unaligned)", chunk_size, block_start_offset, GAMECARD_STORAGE_AREA_NAME(area), rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(out_u8, g_gameCardReadBuf + (base_offset - block_start_offset), out_chunk_size);
|
||||
|
||||
success = (block_size > GAMECARD_READ_BUFFER_SIZE ? _gamecardStorageRead(out_u8 + out_chunk_size, out_size - out_chunk_size, base_offset + out_chunk_size, false) : true);
|
||||
success = (block_size > GAMECARD_READ_BUFFER_SIZE ? gamecardReadStorageArea(out_u8 + out_chunk_size, out_size - out_chunk_size, base_offset + out_chunk_size, false) : true);
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -715,48 +729,56 @@ out:
|
|||
return success;
|
||||
}
|
||||
|
||||
static void gamecardCloseStorageAreas(void)
|
||||
static void gamecardCloseStorageArea(void)
|
||||
{
|
||||
if (serviceIsActive(&(g_gameCardStorageNormal.s)))
|
||||
if (serviceIsActive(&(g_gameCardStorage.s)))
|
||||
{
|
||||
fsStorageClose(&g_gameCardStorageNormal);
|
||||
memset(&g_gameCardStorageNormal, 0, sizeof(FsStorage));
|
||||
fsStorageClose(&g_gameCardStorage);
|
||||
memset(&g_gameCardStorage, 0, sizeof(FsStorage));
|
||||
}
|
||||
|
||||
g_gameCardStorageNormalAreaSize = 0;
|
||||
gamecardCloseHandle();
|
||||
|
||||
if (serviceIsActive(&(g_gameCardStorageSecure.s)))
|
||||
{
|
||||
fsStorageClose(&g_gameCardStorageSecure);
|
||||
memset(&g_gameCardStorageSecure, 0, sizeof(FsStorage));
|
||||
}
|
||||
|
||||
g_gameCardStorageSecureAreaSize = 0;
|
||||
g_gameCardStorageCurrentArea = GameCardStorageArea_None;
|
||||
}
|
||||
|
||||
static bool gamecardGetSizesFromStorageAreas(void)
|
||||
{
|
||||
if (!g_gameCardInserted || !serviceIsActive(&(g_gameCardStorageNormal.s)) || !serviceIsActive(&(g_gameCardStorageSecure.s)))
|
||||
if (!g_gameCardInserted)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
LOGFILE("Gamecard not inserted!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Result rc = 0;
|
||||
|
||||
rc = fsStorageGetSize(&g_gameCardStorageNormal, (s64*)&g_gameCardStorageNormalAreaSize);
|
||||
if (R_FAILED(rc))
|
||||
for(u8 i = 0; i < 2; i++)
|
||||
{
|
||||
LOGFILE("fsStorageGetSize failed! (0x%08X) (normal)", rc);
|
||||
return false;
|
||||
}
|
||||
Result rc = 0;
|
||||
u64 area_size = 0;
|
||||
u8 area = (i == 0 ? GameCardStorageArea_Normal : GameCardStorageArea_Secure);
|
||||
|
||||
rc = fsStorageGetSize(&g_gameCardStorageSecure, (s64*)&g_gameCardStorageSecureAreaSize);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("fsStorageGetSize failed! (0x%08X) (secure)", rc);
|
||||
g_gameCardStorageNormalAreaSize = 0;
|
||||
return false;
|
||||
if (!gamecardOpenStorageArea(area))
|
||||
{
|
||||
LOGFILE("Failed to open %s storage area!", GAMECARD_STORAGE_AREA_NAME(area));
|
||||
return false;
|
||||
}
|
||||
|
||||
rc = fsStorageGetSize(&g_gameCardStorage, (s64*)&area_size);
|
||||
|
||||
gamecardCloseStorageArea();
|
||||
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("fsStorageGetSize failed to retrieve %s storage area size! (0x%08X)", GAMECARD_STORAGE_AREA_NAME(area), rc);
|
||||
g_gameCardStorageNormalAreaSize = g_gameCardStorageSecureAreaSize = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (area == GameCardStorageArea_Normal)
|
||||
{
|
||||
g_gameCardStorageNormalAreaSize = area_size;
|
||||
} else {
|
||||
g_gameCardStorageSecureAreaSize = area_size;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
|
@ -124,12 +124,12 @@ Result gamecardInitialize(void);
|
|||
void gamecardExit(void);
|
||||
|
||||
/// Used to check if a gamecard has been inserted and if info could be loaded from it.
|
||||
bool gamecardCheckReadyStatus(void);
|
||||
bool gamecardIsReady(void);
|
||||
|
||||
/// Used to read data from the inserted gamecard.
|
||||
/// All required handles are managed internally.
|
||||
/// All required handles, changes between normal <-> secure storage areas and proper offset calculations are managed internally.
|
||||
/// offset + out_size should never exceed the value returned by gamecardGetTotalRomSize().
|
||||
bool gamecardStorageRead(void *out, u64 out_size, u64 offset);
|
||||
bool gamecardRead(void *out, u64 out_size, u64 offset);
|
||||
|
||||
/// Miscellaneous functions.
|
||||
bool gamecardGetHeader(GameCardHeader *out);
|
||||
|
@ -138,13 +138,11 @@ bool gamecardGetTrimmedRomSize(u64 *out);
|
|||
bool gamecardGetCertificate(FsGameCardCertificate *out);
|
||||
bool gamecardGetBundledFirmwareUpdateVersion(u32 *out);
|
||||
|
||||
static inline u64 gamecardGetCapacity(GameCardHeader *header)
|
||||
static inline u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size)
|
||||
{
|
||||
if (!header) return 0;
|
||||
|
||||
u64 capacity = 0;
|
||||
|
||||
switch(header->rom_size)
|
||||
switch(rom_size)
|
||||
{
|
||||
case GameCardRomSize_1GB:
|
||||
capacity = (u64)0x40000000;
|
||||
|
@ -168,7 +166,13 @@ static inline u64 gamecardGetCapacity(GameCardHeader *header)
|
|||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
return capacity;
|
||||
}
|
||||
|
||||
static inline u64 gamecardGetCapacityFromHeader(GameCardHeader *header)
|
||||
{
|
||||
if (!header) return 0;
|
||||
return gamecardGetCapacityFromRomSizeValue(header->rom_size);
|
||||
}
|
||||
|
||||
#endif /* __GAMECARD_H__ */
|
|
@ -277,7 +277,7 @@ static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 progr
|
|||
return false;
|
||||
}
|
||||
|
||||
rc = svcGetProcessList(&num_processes, pids, 300);
|
||||
rc = svcGetProcessList((s32*)&num_processes, pids, 300);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("svcGetProcessList failed! (0x%08X)", rc);
|
|
@ -3,6 +3,8 @@
|
|||
#ifndef __KEYS_H__
|
||||
#define __KEYS_H__
|
||||
|
||||
#include <switch/types.h>
|
||||
|
||||
#define KEYS_FILE_PATH "sdmc:/switch/prod.keys" /* Location used by Lockpick_RCM */
|
||||
|
||||
bool keysLoadNcaKeyset(void);
|
205
source/main.c
205
source/main.c
|
@ -3,175 +3,72 @@
|
|||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
//#include "lvgl_helper.h"
|
||||
#include "utils.h"
|
||||
#include "gamecard.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
bool exitMainLoop = false;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
/* Initialize application resources */
|
||||
if (!initApplicationResources(argc, argv))
|
||||
int ret = 0;
|
||||
|
||||
LOGFILE("nxdumptool starting.");
|
||||
|
||||
if (!utilsInitializeResources())
|
||||
{
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Main application loop */
|
||||
/*lv_test();
|
||||
|
||||
while(appletMainLoop())
|
||||
{
|
||||
UIResult result = uiProcess();
|
||||
switch(result)
|
||||
{
|
||||
case resultShowMainMenu:
|
||||
uiSetState(stateMainMenu);
|
||||
break;
|
||||
case resultShowGameCardMenu:
|
||||
uiSetState(stateGameCardMenu);
|
||||
break;
|
||||
case resultShowXciDumpMenu:
|
||||
uiSetState(stateXciDumpMenu);
|
||||
break;
|
||||
case resultDumpXci:
|
||||
uiSetState(stateDumpXci);
|
||||
break;
|
||||
case resultShowNspDumpMenu:
|
||||
uiSetState(stateNspDumpMenu);
|
||||
break;
|
||||
case resultShowNspAppDumpMenu:
|
||||
uiSetState(stateNspAppDumpMenu);
|
||||
break;
|
||||
case resultShowNspPatchDumpMenu:
|
||||
uiSetState(stateNspPatchDumpMenu);
|
||||
break;
|
||||
case resultShowNspAddOnDumpMenu:
|
||||
uiSetState(stateNspAddOnDumpMenu);
|
||||
break;
|
||||
case resultDumpNsp:
|
||||
uiSetState(stateDumpNsp);
|
||||
break;
|
||||
case resultShowHfs0Menu:
|
||||
uiSetState(stateHfs0Menu);
|
||||
break;
|
||||
case resultShowRawHfs0PartitionDumpMenu:
|
||||
uiSetState(stateRawHfs0PartitionDumpMenu);
|
||||
break;
|
||||
case resultDumpRawHfs0Partition:
|
||||
uiSetState(stateDumpRawHfs0Partition);
|
||||
break;
|
||||
case resultShowHfs0PartitionDataDumpMenu:
|
||||
uiSetState(stateHfs0PartitionDataDumpMenu);
|
||||
break;
|
||||
case resultDumpHfs0PartitionData:
|
||||
uiSetState(stateDumpHfs0PartitionData);
|
||||
break;
|
||||
case resultShowHfs0BrowserMenu:
|
||||
uiSetState(stateHfs0BrowserMenu);
|
||||
break;
|
||||
case resultHfs0BrowserGetList:
|
||||
uiSetState(stateHfs0BrowserGetList);
|
||||
break;
|
||||
case resultShowHfs0Browser:
|
||||
uiSetState(stateHfs0Browser);
|
||||
break;
|
||||
case resultHfs0BrowserCopyFile:
|
||||
uiSetState(stateHfs0BrowserCopyFile);
|
||||
break;
|
||||
case resultShowExeFsMenu:
|
||||
uiSetState(stateExeFsMenu);
|
||||
break;
|
||||
case resultShowExeFsSectionDataDumpMenu:
|
||||
uiSetState(stateExeFsSectionDataDumpMenu);
|
||||
break;
|
||||
case resultDumpExeFsSectionData:
|
||||
uiSetState(stateDumpExeFsSectionData);
|
||||
break;
|
||||
case resultShowExeFsSectionBrowserMenu:
|
||||
uiSetState(stateExeFsSectionBrowserMenu);
|
||||
break;
|
||||
case resultExeFsSectionBrowserGetList:
|
||||
uiSetState(stateExeFsSectionBrowserGetList);
|
||||
break;
|
||||
case resultShowExeFsSectionBrowser:
|
||||
uiSetState(stateExeFsSectionBrowser);
|
||||
break;
|
||||
case resultExeFsSectionBrowserCopyFile:
|
||||
uiSetState(stateExeFsSectionBrowserCopyFile);
|
||||
break;
|
||||
case resultShowRomFsMenu:
|
||||
uiSetState(stateRomFsMenu);
|
||||
break;
|
||||
case resultShowRomFsSectionDataDumpMenu:
|
||||
uiSetState(stateRomFsSectionDataDumpMenu);
|
||||
break;
|
||||
case resultDumpRomFsSectionData:
|
||||
uiSetState(stateDumpRomFsSectionData);
|
||||
break;
|
||||
case resultShowRomFsSectionBrowserMenu:
|
||||
uiSetState(stateRomFsSectionBrowserMenu);
|
||||
break;
|
||||
case resultRomFsSectionBrowserGetEntries:
|
||||
uiSetState(stateRomFsSectionBrowserGetEntries);
|
||||
break;
|
||||
case resultShowRomFsSectionBrowser:
|
||||
uiSetState(stateRomFsSectionBrowser);
|
||||
break;
|
||||
case resultRomFsSectionBrowserChangeDir:
|
||||
uiSetState(stateRomFsSectionBrowserChangeDir);
|
||||
break;
|
||||
case resultRomFsSectionBrowserCopyFile:
|
||||
uiSetState(stateRomFsSectionBrowserCopyFile);
|
||||
break;
|
||||
case resultRomFsSectionBrowserCopyDir:
|
||||
uiSetState(stateRomFsSectionBrowserCopyDir);
|
||||
break;
|
||||
case resultDumpGameCardCertificate:
|
||||
uiSetState(stateDumpGameCardCertificate);
|
||||
break;
|
||||
case resultShowSdCardEmmcMenu:
|
||||
uiSetState(stateSdCardEmmcMenu);
|
||||
break;
|
||||
case resultShowSdCardEmmcTitleMenu:
|
||||
uiSetState(stateSdCardEmmcTitleMenu);
|
||||
break;
|
||||
case resultShowSdCardEmmcOrphanPatchAddOnMenu:
|
||||
uiSetState(stateSdCardEmmcOrphanPatchAddOnMenu);
|
||||
break;
|
||||
case resultShowSdCardEmmcBatchModeMenu:
|
||||
uiSetState(stateSdCardEmmcBatchModeMenu);
|
||||
break;
|
||||
case resultSdCardEmmcBatchDump:
|
||||
uiSetState(stateSdCardEmmcBatchDump);
|
||||
break;
|
||||
case resultShowTicketMenu:
|
||||
uiSetState(stateTicketMenu);
|
||||
break;
|
||||
case resultDumpTicket:
|
||||
uiSetState(stateDumpTicket);
|
||||
break;
|
||||
case resultShowUpdateMenu:
|
||||
uiSetState(stateUpdateMenu);
|
||||
break;
|
||||
case resultUpdateNSWDBXml:
|
||||
uiSetState(stateUpdateNSWDBXml);
|
||||
break;
|
||||
case resultUpdateApplication:
|
||||
uiSetState(stateUpdateApplication);
|
||||
break;
|
||||
case resultExit:
|
||||
exitMainLoop = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lv_task_handler();
|
||||
if (lvglHelperGetExitFlag()) break;
|
||||
}*/
|
||||
|
||||
if (exitMainLoop) break;
|
||||
|
||||
|
||||
|
||||
|
||||
consoleInit(NULL);
|
||||
|
||||
printf("waiting...\n");
|
||||
consoleUpdate(NULL);
|
||||
|
||||
while(appletMainLoop())
|
||||
{
|
||||
if (gamecardIsReady()) break;
|
||||
}
|
||||
|
||||
u64 size = 0;
|
||||
if (!gamecardGetTotalRomSize(&size))
|
||||
{
|
||||
printf("totalromsize failed");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
printf("totalromsize: 0x%lX\n", size);
|
||||
consoleUpdate(NULL);
|
||||
|
||||
if (!gamecardGetTrimmedRomSize(&size))
|
||||
{
|
||||
printf("trimmedromsize failed");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
printf("trimmedromsize: 0x%lX\n", size);
|
||||
|
||||
out2:
|
||||
consoleUpdate(NULL);
|
||||
SLEEP(3);
|
||||
consoleExit(NULL);
|
||||
|
||||
out:
|
||||
/* Deinitialize application resources */
|
||||
deinitApplicationResources();
|
||||
utilsCloseResources();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
3740
source/nca.c
3740
source/nca.c
File diff suppressed because it is too large
Load diff
938
source/nca.h
938
source/nca.h
File diff suppressed because it is too large
Load diff
357
source/new/nca.c
357
source/new/nca.c
|
@ -1,357 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nca.h"
|
||||
#include "keys.h"
|
||||
#include "rsa.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = {
|
||||
0x9A, 0xBB, 0xD2, 0x11, 0x86, 0x00, 0x21, 0x9D, 0x7A, 0xDC, 0x5B, 0x43, 0x95, 0xF8, 0x4E, 0xFD,
|
||||
0xFF, 0x6B, 0x25, 0xEF, 0x9F, 0x96, 0x85, 0x28, 0x18, 0x9E, 0x76, 0xB0, 0x92, 0xF0, 0x6A, 0xCB
|
||||
};
|
||||
|
||||
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx);
|
||||
static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset);
|
||||
static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset);
|
||||
|
||||
size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt)
|
||||
{
|
||||
if (!ctx || !dst || !src || !size || !sector_size || (size % sector_size) != 0)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t i, crypt_res = 0;
|
||||
u64 cur_sector = sector;
|
||||
|
||||
u8 *dst_u8 = (u8*)dst;
|
||||
const u8 *src_u8 = (const u8*)src;
|
||||
|
||||
for(i = 0; i < size; i += sector_size, cur_sector++)
|
||||
{
|
||||
/* We have to force a sector reset on each new sector to actually enable Nintendo AES-XTS cipher tweak */
|
||||
aes128XtsContextResetSector(ctx, cur_sector, true);
|
||||
|
||||
if (encrypt)
|
||||
{
|
||||
crypt_res = aes128XtsEncrypt(ctx, dst_u8 + i, src_u8 + i, sector_size);
|
||||
} else {
|
||||
crypt_res = aes128XtsDecrypt(ctx, dst_u8 + i, src_u8 + i, sector_size);
|
||||
}
|
||||
|
||||
if (crypt_res != sector_size) break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool ncaDecryptKeyArea(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
{
|
||||
LOGFILE("Invalid NCA context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Result rc = 0;
|
||||
const u8 *kek_src = NULL;
|
||||
u8 key_count, tmp_kek[0x10] = {0};
|
||||
|
||||
/* Check if we're dealing with a NCA0 with a plain text key area */
|
||||
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
|
||||
{
|
||||
memcpy(ctx->decrypted_keys, ctx->header.encrypted_keys, 0x40);
|
||||
return true;
|
||||
}
|
||||
|
||||
kek_src = keysGetKeyAreaEncryptionKeySource(ctx->header.kaek_index);
|
||||
if (!kek_src)
|
||||
{
|
||||
LOGFILE("Unable to retrieve KAEK source for index 0x%02X!", ctx->header.kaek_index);
|
||||
return false;
|
||||
}
|
||||
|
||||
rc = splCryptoGenerateAesKek(kek_src, ctx->key_generation, 0, tmp_kek);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("splCryptoGenerateAesKek failed! (0x%08X)", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
|
||||
|
||||
for(u8 i = 0; i < key_count; i++)
|
||||
{
|
||||
rc = splCryptoGenerateAesKey(tmp_kek, ctx->header.encrypted_keys[i], ctx->decrypted_keys[i]);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("splCryptoGenerateAesKey failed! (0x%08X)", rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ncaEncryptKeyArea(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
{
|
||||
LOGFILE("Invalid NCA context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u8 key_count;
|
||||
const u8 *kaek = NULL;
|
||||
Aes128Context key_area_ctx = {0};
|
||||
|
||||
/* Check if we're dealing with a NCA0 with a plain text key area */
|
||||
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
|
||||
{
|
||||
memcpy(ctx->header.encrypted_keys, ctx->decrypted_keys, 0x40);
|
||||
return true;
|
||||
}
|
||||
|
||||
kaek = keysGetKeyAreaEncryptionKey(ctx->key_generation, ctx->header.kaek_index);
|
||||
if (!kaek)
|
||||
{
|
||||
LOGFILE("Unable to retrieve KAEK for key generation 0x%02X and KAEK index 0x%02X!", ctx->key_generation, ctx->header.kaek_index);
|
||||
return false;
|
||||
}
|
||||
|
||||
key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
|
||||
|
||||
aes128ContextCreate(&key_area_ctx, kaek, true);
|
||||
for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, ctx->header.encrypted_keys[i], ctx->decrypted_keys[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ncaDecryptHeader(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
{
|
||||
LOGFILE("Invalid NCA context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 i, magic = 0;
|
||||
size_t crypt_res = 0;
|
||||
const u8 *header_key = NULL;
|
||||
Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0};
|
||||
|
||||
header_key = keysGetNcaHeaderKey();
|
||||
|
||||
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, false);
|
||||
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false);
|
||||
if (crypt_res != NCA_HEADER_LENGTH)
|
||||
{
|
||||
LOGFILE("Invalid output length for decrypted NCA header! (0x%X != 0x%lX)", NCA_HEADER_LENGTH, crypt_res);
|
||||
return false;
|
||||
}
|
||||
|
||||
magic = __builtin_bswap32(ctx->header.magic);
|
||||
|
||||
switch(magic)
|
||||
{
|
||||
case NCA_NCA3_MAGIC:
|
||||
ctx->format_version = NcaVersion_Nca3;
|
||||
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_FULL_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false);
|
||||
if (crypt_res != NCA_FULL_HEADER_LENGTH)
|
||||
{
|
||||
LOGFILE("Error decrypting full NCA3 header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case NCA_NCA2_MAGIC:
|
||||
ctx->format_version = NcaVersion_Nca2;
|
||||
|
||||
for(i = 0; i < NCA_SECTION_HEADER_CNT; i++)
|
||||
{
|
||||
if (!ctx->header.fs_entries[i].enable_entry) continue;
|
||||
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false);
|
||||
if (crypt_res != NCA_FS_HEADER_LENGTH)
|
||||
{
|
||||
LOGFILE("Error decrypting NCA2 FS section header #%u!", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case NCA_NCA0_MAGIC:
|
||||
ctx->format_version = NcaVersion_Nca0;
|
||||
|
||||
/* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers */
|
||||
if (!ncaDecryptKeyArea(ctx))
|
||||
{
|
||||
LOGFILE("Error decrypting key area from NCA0 header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_keys[0], ctx->decrypted_keys[1], false);
|
||||
|
||||
for(i = 0; i < NCA_SECTION_HEADER_CNT; i++)
|
||||
{
|
||||
if (!ctx->header.fs_entries[i].enable_entry) continue;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOGFILE("Invalid NCA magic word! Wrong header key? (0x%08X)", magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fill additional context info */
|
||||
ctx->key_generation = ncaGetKeyGenerationValue(ctx);
|
||||
ctx->rights_id_available = ncaCheckRightsIdAvailability(ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ncaEncryptHeader(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
{
|
||||
LOGFILE("Invalid NCA context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 i;
|
||||
size_t crypt_res = 0;
|
||||
const u8 *header_key = NULL;
|
||||
Aes128XtsContext hdr_aes_ctx = {0};
|
||||
|
||||
header_key = keysGetNcaHeaderKey();
|
||||
|
||||
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, true);
|
||||
|
||||
switch(ctx->format_version)
|
||||
{
|
||||
case NcaVersion_Nca3:
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_FULL_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != NCA_FULL_HEADER_LENGTH)
|
||||
{
|
||||
LOGFILE("Error encrypting full NCA3 header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case NcaVersion_Nca2:
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != NCA_HEADER_LENGTH)
|
||||
{
|
||||
LOGFILE("Error encrypting partial NCA2 header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(i = 0; i < NCA_SECTION_HEADER_CNT; i++)
|
||||
{
|
||||
if (!ctx->header.fs_entries[i].enable_entry) continue;
|
||||
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != NCA_FS_HEADER_LENGTH)
|
||||
{
|
||||
LOGFILE("Error encrypting NCA2 FS section header #%u!", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case NcaVersion_Nca0:
|
||||
/* There's nothing else to do */
|
||||
break;
|
||||
default:
|
||||
LOGFILE("Invalid NCA format version! (0x%02X)", ctx->format_version);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx || ctx->format_version != NcaVersion_Nca0) return false;
|
||||
|
||||
u8 nca0_key_area_hash[SHA256_HASH_SIZE] = {0};
|
||||
sha256CalculateHash(nca0_key_area_hash, ctx->header.encrypted_keys, 0x40);
|
||||
|
||||
if (!memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset)
|
||||
{
|
||||
if (!ctr) return;
|
||||
|
||||
offset >>= 4;
|
||||
|
||||
for(u8 i = 0; i < 8; i++)
|
||||
{
|
||||
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
|
||||
offset >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset)
|
||||
{
|
||||
if (!ctr) return;
|
||||
|
||||
offset >>= 4;
|
||||
|
||||
for(u8 i = 0; i < 8; i++)
|
||||
{
|
||||
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
|
||||
offset >>= 8;
|
||||
}
|
||||
|
||||
for(u8 i = 0; i < 4; i++)
|
||||
{
|
||||
ctr[0x8 - i - 1] = (u8)(ctr_val & 0xFF);
|
||||
ctr_val >>= 8;
|
||||
}
|
||||
}
|
335
source/new/nca.h
335
source/new/nca.h
|
@ -1,335 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __NCA_H__
|
||||
#define __NCA_H__
|
||||
|
||||
#define NCA_HEADER_LENGTH 0x400
|
||||
#define NCA_FS_HEADER_LENGTH 0x200
|
||||
#define NCA_FS_HEADER_COUNT 4
|
||||
#define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_SECTION_HEADER_LENGTH * NCA_SECTION_HEADER_CNT))
|
||||
|
||||
#define NCA_NCA0_MAGIC 0x4E434130 /* "NCA0" */
|
||||
#define NCA_NCA2_MAGIC 0x4E434132 /* "NCA2" */
|
||||
#define NCA_NCA3_MAGIC 0x4E434133 /* "NCA3" */
|
||||
|
||||
#define NCA_IVFC_MAGIC 0x49564643 /* "IVFC" */
|
||||
|
||||
#define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR" */
|
||||
|
||||
#define NCA_FS_ENTRY_BLOCK_SIZE 0x200
|
||||
|
||||
#define NCA_AES_XTS_SECTOR_SIZE 0x200
|
||||
|
||||
#define NCA_IVFC_BLOCK_SIZE(x) (1 << (x))
|
||||
|
||||
typedef enum {
|
||||
NcaVersion_Nca0 = 0,
|
||||
NcaVersion_Nca2 = 1,
|
||||
NcaVersion_Nca3 = 2
|
||||
} NcaVersion;
|
||||
|
||||
typedef enum {
|
||||
NcaDistributionType_Download = 0,
|
||||
NcaDistributionType_GameCard = 1
|
||||
} NcaDistributionType;
|
||||
|
||||
typedef enum {
|
||||
NcaContentType_Program = 0,
|
||||
NcaContentType_Meta = 1,
|
||||
NcaContentType_Control = 2,
|
||||
NcaContentType_Manual = 3,
|
||||
NcaContentType_Data = 4,
|
||||
NcaContentType_PublicData = 5
|
||||
} NcaContentType;
|
||||
|
||||
typedef enum {
|
||||
NcaKeyGenerationOld_100_230 = 0,
|
||||
NcaKeyGenerationOld_300 = 2
|
||||
} NcaKeyGenerationOld;
|
||||
|
||||
typedef enum {
|
||||
NcaKeyAreaEncryptionKeyIndex_Application = 0,
|
||||
NcaKeyAreaEncryptionKeyIndex_Ocean = 1,
|
||||
NcaKeyAreaEncryptionKeyIndex_System = 2
|
||||
} NcaKeyAreaEncryptionKeyIndex;
|
||||
|
||||
/// 'NcaKeyGeneration_Latest' will always point to the last known key generation value.
|
||||
typedef enum {
|
||||
NcaKeyGeneration_301_302 = 3,
|
||||
NcaKeyGeneration_400_410 = 4,
|
||||
NcaKeyGeneration_500_510 = 5,
|
||||
NcaKeyGeneration_600_610 = 6,
|
||||
NcaKeyGeneration_620 = 7,
|
||||
NcaKeyGeneration_700_801 = 8,
|
||||
NcaKeyGeneration_810_811 = 9,
|
||||
NcaKeyGeneration_900_901 = 10,
|
||||
NcaKeyGeneration_910_920 = 11,
|
||||
NcaKeyGeneration_Latest = NcaKeyGeneration_910_920
|
||||
} NcaKeyGeneration;
|
||||
|
||||
typedef struct {
|
||||
u32 start_block_offset; ///< Expressed in NCA_FS_ENTRY_BLOCK_SIZE blocks.
|
||||
u32 end_block_offset; ///< Expressed in NCA_FS_ENTRY_BLOCK_SIZE blocks.
|
||||
u8 enable_entry;
|
||||
u8 reserved[0x7];
|
||||
} NcaFsEntry;
|
||||
|
||||
typedef struct {
|
||||
u8 hash[SHA256_HASH_SIZE];
|
||||
} NcaFsHash;
|
||||
|
||||
typedef struct {
|
||||
u8 key[0x10];
|
||||
} NcaEncryptedKey;
|
||||
|
||||
typedef enum {
|
||||
NcaFsType_RomFs = 0,
|
||||
NcaFsType_PartitionFs = 1
|
||||
} NcaFsType;
|
||||
|
||||
typedef enum {
|
||||
NcaHashType_Auto = 0,
|
||||
NcaHashType_None = 1,
|
||||
NcaHashType_HierarchicalSha256 = 2, ///< Used by NcaFsType_PartitionFs.
|
||||
NcaHashType_HierarchicalIntegrity = 3 ///< Used by NcaFsType_RomFs.
|
||||
} NcaHashType;
|
||||
|
||||
typedef enum {
|
||||
NcaEncryptionType_Auto = 0,
|
||||
NcaEncryptionType_None = 1,
|
||||
NcaEncryptionType_AesXts = 2,
|
||||
NcaEncryptionType_AesCtr = 3,
|
||||
NcaEncryptionType_AesCtrEx = 4
|
||||
} NcaEncryptionType;
|
||||
|
||||
typedef struct {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
} NcaHierarchicalSha256LayerInfo;
|
||||
|
||||
/// Used for NcaFsType_PartitionFs and NCA0 RomFS.
|
||||
typedef struct {
|
||||
u8 master_hash[SHA256_HASH_SIZE];
|
||||
u32 hash_block_size;
|
||||
u32 layer_count;
|
||||
NcaHierarchicalSha256LayerInfo hash_data_layer_info;
|
||||
NcaHierarchicalSha256LayerInfo hash_target_layer_info;
|
||||
} NcaHierarchicalSha256;
|
||||
|
||||
typedef struct {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 block_size; ///< Use NCA_IVFC_CALC_BLOCK_SIZE to calculate the actual block size using this value.
|
||||
u8 reserved[0x4];
|
||||
} NcaHierarchicalIntegrityLayerInfo;
|
||||
|
||||
/// Used for NcaFsType_RomFs.
|
||||
typedef struct {
|
||||
u32 magic; ///< "IVFC".
|
||||
u32 version;
|
||||
u32 master_hash_size;
|
||||
u32 layer_count;
|
||||
NcaHierarchicalIntegrityLayerInfo hash_data_layer_info[5];
|
||||
NcaHierarchicalIntegrityLayerInfo hash_target_layer_info;
|
||||
u8 signature_salt[0x20];
|
||||
u8 master_hash[0x20];
|
||||
} NcaHierarchicalIntegrity;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
///< Used if hash_type == NcaHashType_HierarchicalSha256 (NcaFsType_PartitionFs).
|
||||
NcaHierarchicalSha256 hierarchical_sha256;
|
||||
u8 reserved_1[0xB0];
|
||||
};
|
||||
struct {
|
||||
///< Used if hash_type == NcaHashType_HierarchicalIntegrity (NcaFsType_RomFs).
|
||||
NcaHierarchicalIntegrity hierarchical_integrity;
|
||||
u8 reserved_2[0x18];
|
||||
};
|
||||
};
|
||||
} NcaHashInfo;
|
||||
|
||||
typedef struct {
|
||||
u32 magic; ///< "BKTR".
|
||||
u32 bucket_count;
|
||||
u32 entry_count;
|
||||
u8 reserved[0x4];
|
||||
} NcaBucketTreeHeader;
|
||||
|
||||
/// Only used for NcaEncryptionType_AesCtrEx (PatchRomFs).
|
||||
typedef struct {
|
||||
u64 indirect_offset;
|
||||
u64 indirect_size;
|
||||
NcaBucketTreeHeader indirect_header;
|
||||
u64 aes_ctr_ex_offset;
|
||||
u64 aes_ctr_ex_size;
|
||||
NcaBucketTreeHeader aes_ctr_ex_header;
|
||||
} NcaPatchInfo;
|
||||
|
||||
/// Format unknown.
|
||||
typedef struct {
|
||||
u8 unknown[0x30];
|
||||
} NcaSparseInfo;
|
||||
|
||||
typedef struct {
|
||||
u16 version;
|
||||
u8 fs_type; ///< NcaFsType.
|
||||
u8 hash_type; ///< NcaHashType.
|
||||
u8 encryption_type; ///< NcaEncryptionType.
|
||||
u8 reserved_1[0x3];
|
||||
NcaHashInfo hash_info;
|
||||
NcaPatchInfo patch_info;
|
||||
union {
|
||||
u8 section_ctr[0x8];
|
||||
struct {
|
||||
u32 generation;
|
||||
u32 secure_value;
|
||||
};
|
||||
};
|
||||
NcaSparseInfo sparse_info;
|
||||
u8 reserved_2[0x88];
|
||||
} NcaFsHeader;
|
||||
|
||||
typedef struct {
|
||||
u8 main_signature[0x100]; ///< RSA-PSS signature over header with fixed key.
|
||||
u8 acid_signature[0x100]; ///< RSA-PSS signature over header with key in NPDM.
|
||||
u32 magic; ///< "NCA0" / "NCA2" / "NCA3".
|
||||
u8 distribution_type; ///< NcaDistributionType.
|
||||
u8 content_type; ///< NcaContentType.
|
||||
u8 key_generation_old; ///< NcaKeyGenerationOld.
|
||||
u8 kaek_index; ///< NcaKeyAreaEncryptionKeyIndex.
|
||||
u64 content_size;
|
||||
u64 program_id;
|
||||
u32 content_index;
|
||||
union {
|
||||
u32 sdk_addon_version;
|
||||
struct {
|
||||
u8 sdk_addon_revision;
|
||||
u8 sdk_addon_micro;
|
||||
u8 sdk_addon_minor;
|
||||
u8 sdk_addon_major;
|
||||
};
|
||||
};
|
||||
u8 key_generation; ///< NcaKeyGeneration.
|
||||
u8 main_signature_key_generation;
|
||||
u8 reserved_1[0xE];
|
||||
FsRightsId rights_id; ///< Used for titlekey crypto.
|
||||
NcaFsEntry fs_entries[4]; ///< Start and end offsets for each NCA FS section.
|
||||
NcaFsHash fs_hashes[4]; ///< SHA-256 hashes calculated over each NCA FS section header.
|
||||
NcaEncryptedKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted.
|
||||
u8 reserved_2[0xC0];
|
||||
NcaFsHeader fs_headers[4]; /// NCA FS section headers.
|
||||
} NcaHeader;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 section_num;
|
||||
NcaFsHeader *fs_header;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
u8 ctr
|
||||
} NcaFsContext;
|
||||
|
||||
typedef struct {
|
||||
u8 storage_id; ///< NcmStorageId.
|
||||
NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data.
|
||||
u64 gc_secure_area_base_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard.
|
||||
NcmContentId id; ///< Also used to read NCA data.
|
||||
char id_str[0x21];
|
||||
u8 hash[0x20];
|
||||
char hash_str[0x41];
|
||||
u8 format_version; ///< NcaVersion.
|
||||
u8 type; ///< NcmContentType. Retrieved from NcmContentInfo.
|
||||
u64 size; ///< Retrieved from NcmContentInfo.
|
||||
u8 key_generation; ///< NcaKeyGenerationOld / NcaKeyGeneration. Retrieved from the decrypted header.
|
||||
u8 id_offset; ///< Retrieved from NcmContentInfo.
|
||||
bool rights_id_available;
|
||||
NcaHeader header;
|
||||
bool dirty_header;
|
||||
NcaEncryptedKey decrypted_keys[4];
|
||||
NcaFsContext fs_contexts[4];
|
||||
} NcaContext;
|
||||
|
||||
static inline void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out)
|
||||
{
|
||||
if (!size || !out) return;
|
||||
*out = 0;
|
||||
memcpy(out, size, 6);
|
||||
}
|
||||
|
||||
static inline void ncaConvertU64ToNcmContentSize(const u64 *size, u8 *out)
|
||||
{
|
||||
if (!size || !out) return;
|
||||
memcpy(out, size, 6);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline u8 ncaGetKeyGenerationValue(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx) return 0;
|
||||
return (ctx->header.key_generation > ctx->header.key_generation_old ? ctx->header.key_generation : ctx->header.key_generation_old);
|
||||
}
|
||||
|
||||
static inline void ncaSetDownloadDistributionType(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx || ctx->header.distribution_type == NcaDistributionType_Download) return;
|
||||
ctx->header.distribution_type = NcaDistributionType_Download;
|
||||
ctx->dirty_header = true;
|
||||
}
|
||||
|
||||
static inline bool ncaCheckRightsIdAvailability(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
|
||||
bool rights_id_available = false;
|
||||
|
||||
for(u8 i = 0; i < 0x10; i++)
|
||||
{
|
||||
if (ctx->header.rights_id.c[i] != 0)
|
||||
{
|
||||
rights_id_available = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rights_id_available;
|
||||
}
|
||||
|
||||
static inline void ncaWipeRightsId(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
memset(ctx->header.rights_id, 0, sizeof(FsRightsId));
|
||||
ctx->dirty_header = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool ncaDecryptKeyArea(NcaContext *nca_ctx);
|
||||
bool ncaEncryptKeyArea(NcaContext *nca_ctx);
|
||||
|
||||
bool ncaDecryptHeader(NcaContext *ctx);
|
||||
bool ncaEncryptHeader(NcaContext *ctx);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __NCA_H__ */
|
|
@ -9,7 +9,9 @@
|
|||
#include "rsa.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* Self-generated private key */
|
||||
/* Global variables. */
|
||||
|
||||
/// Self-generated private key.
|
||||
static const char g_rsa2048CustomAcidPrivateKey[] = "-----BEGIN RSA PRIVATE KEY-----\r\n"
|
||||
"MIIEowIBAAKCAQEAvVRzt+8mE7oE4RkmSh3ws4CGlBj7uhHkfwCpPFsn4TNVdLRo\r\n"
|
||||
"YYY17jQYWTtcOYPMcHxwUpgJyspGN8QGXEkJqY8jILv2eO0jBGtg7Br2afUBp6/x\r\n"
|
||||
|
@ -38,7 +40,7 @@ static const char g_rsa2048CustomAcidPrivateKey[] = "-----BEGIN RSA PRIVATE KEY-
|
|||
"n5NEG+mY4WZaOFRNiZu8+4aJI1yycXMyA22iKcU8+nN/sMAJs3Nx\r\n"
|
||||
"-----END RSA PRIVATE KEY-----\r\n";
|
||||
|
||||
/* Self-generated public key */
|
||||
/// Self-generated public key.
|
||||
static const u8 g_rsa2048CustomAcidPublicKey[] = {
|
||||
0xBD, 0x54, 0x73, 0xB7, 0xEF, 0x26, 0x13, 0xBA, 0x04, 0xE1, 0x19, 0x26, 0x4A, 0x1D, 0xF0, 0xB3,
|
||||
0x80, 0x86, 0x94, 0x18, 0xFB, 0xBA, 0x11, 0xE4, 0x7F, 0x00, 0xA9, 0x3C, 0x5B, 0x27, 0xE1, 0x33,
|
||||
|
@ -58,6 +60,8 @@ static const u8 g_rsa2048CustomAcidPublicKey[] = {
|
|||
0x6D, 0x5D, 0x5B, 0xD2, 0x55, 0x20, 0xB9, 0x1E, 0x59, 0x13, 0x3C, 0x17, 0xC2, 0x25, 0x56, 0xC7
|
||||
};
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_src, size_t h_src_size);
|
||||
|
||||
bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, size_t size)
|
||||
|
@ -127,7 +131,7 @@ out:
|
|||
return success;
|
||||
}
|
||||
|
||||
const u8 rsa2048GetCustomAcidPublicKey(void)
|
||||
const u8 *rsa2048GetCustomAcidPublicKey(void)
|
||||
{
|
||||
return g_rsa2048CustomAcidPublicKey;
|
||||
}
|
||||
|
@ -205,6 +209,8 @@ static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_s
|
|||
|
||||
u32 seed = 0;
|
||||
size_t i, offset = 0;
|
||||
u8 *data_u8 = (u8*)data;
|
||||
|
||||
u8 mgf1_buf[SHA256_HASH_SIZE] = {0};
|
||||
u8 h_buf[RSA2048_SIGNATURE_SIZE] = {0};
|
||||
|
||||
|
@ -216,7 +222,7 @@ static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_s
|
|||
|
||||
sha256CalculateHash(mgf1_buf, h_buf, h_src_size + 4);
|
||||
|
||||
for(i = offset; i < data_size && i < (offset + 0x20); i++) data[i] ^= mgf1_buf[i - offset];
|
||||
for(i = offset; i < data_size && i < (offset + 0x20); i++) data_u8[i] ^= mgf1_buf[i - offset];
|
||||
|
||||
seed++;
|
||||
offset += 0x20;
|
|
@ -3,12 +3,12 @@
|
|||
#ifndef __RSA_H__
|
||||
#define __RSA_H__
|
||||
|
||||
#include <switch.h>
|
||||
#include <switch/types.h>
|
||||
|
||||
#define RSA2048_SIGNATURE_SIZE 0x100
|
||||
|
||||
bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, size_t size);
|
||||
const u8 rsa2048GetCustomAcidPublicKey(void);
|
||||
const u8 *rsa2048GetCustomAcidPublicKey(void);
|
||||
|
||||
bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signature, const void *modulus, const void *exponent, size_t exponent_size, const void *label_hash, size_t *out_size);
|
||||
|
233
source/services.c
Normal file
233
source/services.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include "services.h"
|
||||
#include "es.h"
|
||||
#include "fspusb.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* Type definitions. */
|
||||
|
||||
typedef bool (*ServiceCondFunction)(void *arg); /* Used to perform a runtime condition check (e.g. system version) before initializing the service */
|
||||
typedef Result (*ServiceInitFunction)(void); /* Used to initialize the service */
|
||||
typedef void (*ServiceCloseFunction)(void); /* Used to close the service */
|
||||
|
||||
typedef struct ServicesInfoEntry {
|
||||
bool initialized;
|
||||
char name[8];
|
||||
ServiceCondFunction cond_func;
|
||||
ServiceInitFunction init_func;
|
||||
ServiceCloseFunction close_func;
|
||||
} ServicesInfoEntry;
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static Result servicesNifmUserInitialize(void);
|
||||
static bool servicesClkGetServiceType(void *arg);
|
||||
static bool servicesSplCryptoCheckAvailability(void *arg);
|
||||
static bool servicesFspUsbCheckAvailability(void *arg);
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static ServicesInfoEntry g_serviceInfo[] = {
|
||||
{ false, "ncm", NULL, &ncmInitialize, &ncmExit },
|
||||
{ false, "ns", NULL, &nsInitialize, &nsExit },
|
||||
{ false, "csrng", NULL, &csrngInitialize, &csrngExit },
|
||||
{ false, "spl", NULL, &splInitialize, &splExit },
|
||||
{ false, "spl:mig", &servicesSplCryptoCheckAvailability, &splCryptoInitialize, &splCryptoExit }, /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice) */
|
||||
{ false, "pm:dmnt", NULL, &pmdmntInitialize, &pmdmntExit },
|
||||
{ false, "pl", NULL, &plInitialize, &plExit },
|
||||
{ false, "psm", NULL, &psmInitialize, &psmExit },
|
||||
{ false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit },
|
||||
{ false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst */
|
||||
{ false, "fsp-usb", &servicesFspUsbCheckAvailability, &fspusbInitialize, &fspusbExit }, /* Checks if fsp-usb is really available */
|
||||
{ false, "es", NULL, &esInitialize, &esExit },
|
||||
{ false, "set:cal", NULL, &setcalInitialize, &setcalExit }
|
||||
};
|
||||
|
||||
static const u32 g_serviceInfoCount = MAX_ELEMENTS(g_serviceInfo);
|
||||
|
||||
static bool g_clkSvcUsePcv = false;
|
||||
static ClkrstSession g_clkrstCpuSession = {0}, g_clkrstMemSession = {0};
|
||||
|
||||
bool servicesInitialize(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
bool ret = true;
|
||||
|
||||
for(u32 i = 0; i < g_serviceInfoCount; i++)
|
||||
{
|
||||
/* Check if this service has been already initialized */
|
||||
if (g_serviceInfo[i].initialized) continue;
|
||||
|
||||
/* Check if this service depends on a condition function */
|
||||
if (g_serviceInfo[i].cond_func != NULL)
|
||||
{
|
||||
/* Run the condition function - it will update the current service member */
|
||||
/* Skip this service if the required conditions aren't met */
|
||||
if (!g_serviceInfo[i].cond_func(&(g_serviceInfo[i]))) continue;
|
||||
}
|
||||
|
||||
/* Check if this service has a valid initialize function */
|
||||
if (g_serviceInfo[i].init_func == NULL) continue;
|
||||
|
||||
/* Initialize service */
|
||||
rc = g_serviceInfo[i].init_func();
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
utilsConsoleErrorScreen("%s: failed to initialize %s service! (0x%08X)", __func__, g_serviceInfo[i].name, rc);
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update initialized flag */
|
||||
g_serviceInfo[i].initialized = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void servicesClose(void)
|
||||
{
|
||||
for(u32 i = 0; i < g_serviceInfoCount; i++)
|
||||
{
|
||||
/* Check if this service has not been initialized, or if it doesn't have a valid close function */
|
||||
if (!g_serviceInfo[i].initialized || g_serviceInfo[i].close_func == NULL) continue;
|
||||
|
||||
/* Close service */
|
||||
g_serviceInfo[i].close_func();
|
||||
}
|
||||
}
|
||||
|
||||
bool servicesCheckRunningServiceByName(const char *name)
|
||||
{
|
||||
if (!name || !strlen(name)) return false;
|
||||
|
||||
Handle handle;
|
||||
SmServiceName service_name = smEncodeName(name);
|
||||
Result rc = smRegisterService(&handle, service_name, false, 1);
|
||||
bool running = R_FAILED(rc);
|
||||
|
||||
svcCloseHandle(handle);
|
||||
|
||||
if (!running) smUnregisterService(service_name);
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
bool servicesCheckInitializedServiceByName(const char *name)
|
||||
{
|
||||
if (!name || !strlen(name)) return false;
|
||||
|
||||
bool ret = false;
|
||||
size_t name_len = strlen(name);
|
||||
|
||||
for(u32 i = 0; i < g_serviceInfoCount; i++)
|
||||
{
|
||||
if (!strncmp(g_serviceInfo[i].name, name, name_len))
|
||||
{
|
||||
ret = g_serviceInfo[i].initialized;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate)
|
||||
{
|
||||
if (g_clkSvcUsePcv)
|
||||
{
|
||||
pcvSetClockRate(PcvModule_CpuBus, cpu_rate);
|
||||
pcvSetClockRate(PcvModule_EMC, mem_rate);
|
||||
} else {
|
||||
clkrstSetClockRate(&g_clkrstCpuSession, cpu_rate);
|
||||
clkrstSetClockRate(&g_clkrstMemSession, mem_rate);
|
||||
}
|
||||
}
|
||||
|
||||
static Result servicesNifmUserInitialize(void)
|
||||
{
|
||||
return nifmInitialize(NifmServiceType_User);
|
||||
}
|
||||
|
||||
static Result servicesClkrstInitialize(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
/* Open clkrst service handle */
|
||||
rc = clkrstInitialize();
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
/* Initialize CPU and MEM clkrst sessions */
|
||||
memset(&g_clkrstCpuSession, 0, sizeof(ClkrstSession));
|
||||
memset(&g_clkrstMemSession, 0, sizeof(ClkrstSession));
|
||||
|
||||
rc = clkrstOpenSession(&g_clkrstCpuSession, PcvModuleId_CpuBus, 3);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
clkrstExit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = clkrstOpenSession(&g_clkrstMemSession, PcvModuleId_EMC, 3);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
clkrstCloseSession(&g_clkrstCpuSession);
|
||||
clkrstExit();
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void servicesClkrstExit(void)
|
||||
{
|
||||
/* Close CPU and MEM clkrst sessions */
|
||||
clkrstCloseSession(&g_clkrstMemSession);
|
||||
clkrstCloseSession(&g_clkrstCpuSession);
|
||||
|
||||
/* Close clkrst service handle */
|
||||
clkrstExit();
|
||||
}
|
||||
|
||||
static bool servicesClkGetServiceType(void *arg)
|
||||
{
|
||||
if (!arg) return false;
|
||||
|
||||
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
|
||||
if (!strlen(info->name) || strncmp(info->name, "clk", 3) != 0 || info->init_func != NULL || info->close_func != NULL) return false;
|
||||
|
||||
/* Determine which service needs to be used to control hardware clock rates, depending on the system version */
|
||||
/* This may either be pcv (sysver lower than 8.0.0) or clkrst (sysver equal or greater than 8.0.0) */
|
||||
g_clkSvcUsePcv = hosversionBefore(8, 0, 0);
|
||||
|
||||
/* Fill service info */
|
||||
sprintf(info->name, "%s", (g_clkSvcUsePcv ? "pcv" : "clkrst"));
|
||||
info->init_func = (g_clkSvcUsePcv ? &pcvInitialize : &servicesClkrstInitialize);
|
||||
info->close_func = (g_clkSvcUsePcv ? &pcvExit : &servicesClkrstExit);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool servicesSplCryptoCheckAvailability(void *arg)
|
||||
{
|
||||
if (!arg) return false;
|
||||
|
||||
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
|
||||
if (!strlen(info->name) || strncmp(info->name, "spl:mig", 7) != 0 || info->init_func == NULL || info->close_func == NULL) return false;
|
||||
|
||||
/* Check if spl:mig is available (sysver equal or greater than 4.0.0) */
|
||||
return !hosversionBefore(4, 0, 0);
|
||||
}
|
||||
|
||||
static bool servicesFspUsbCheckAvailability(void *arg)
|
||||
{
|
||||
if (!arg) return false;
|
||||
|
||||
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
|
||||
if (!strlen(info->name) || strncmp(info->name, "fsp-usb", 7) != 0 || info->init_func == NULL || info->close_func == NULL) return false;
|
||||
|
||||
/* Check if fsp-usb is actually running in the background */
|
||||
return servicesCheckRunningServiceByName("fsp-usb");
|
||||
}
|
20
source/services.h
Normal file
20
source/services.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __SERVICES_H__
|
||||
#define __SERVICES_H__
|
||||
|
||||
/* Hardware clocks expressed in MHz */
|
||||
#define CPU_CLKRT_NORMAL 1020
|
||||
#define CPU_CLKRT_OVERCLOCKED 1785
|
||||
#define MEM_CLKRT_NORMAL 1331
|
||||
#define MEM_CLKRT_OVERCLOCKED 1600
|
||||
|
||||
bool servicesInitialize();
|
||||
void servicesClose();
|
||||
|
||||
bool servicesCheckRunningServiceByName(const char *name);
|
||||
bool servicesCheckInitializedServiceByName(const char *name);
|
||||
|
||||
void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate);
|
||||
|
||||
#endif /* __SERVICES_H__ */
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#define ETICKET_DEVKEY_PUBLIC_EXPONENT 0x10001
|
||||
|
||||
/* Type definitions. */
|
||||
|
||||
/// Everything after the AES CTR is encrypted.
|
||||
typedef struct {
|
||||
u8 ctr[0x10];
|
||||
|
@ -26,15 +28,19 @@ typedef struct {
|
|||
u8 ghash[0x10];
|
||||
} tikEticketDeviceKeyData;
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static SetCalRsa2048DeviceKey g_eTicketDeviceKey = {0};
|
||||
static bool g_eTicketDeviceKeyRetrieved = false;
|
||||
|
||||
/* Used during the RSA-OAEP titlekey decryption stage */
|
||||
/// Used during the RSA-OAEP titlekey decryption stage.
|
||||
static const u8 g_nullHash[0x20] = {
|
||||
0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
|
||||
0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55
|
||||
};
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count, bool personalized);
|
||||
static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id);
|
||||
static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_type, u64 *out_size);
|
||||
|
@ -176,7 +182,7 @@ out:
|
|||
return success;
|
||||
}
|
||||
|
||||
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TicketCommonBlock *tik_common_blk)
|
||||
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TikCommonBlock *tik_common_blk)
|
||||
{
|
||||
if (!dst || !tik_common_blk)
|
||||
{
|
||||
|
@ -184,7 +190,6 @@ bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TicketCommonBlock *tik_commo
|
|||
return false;
|
||||
}
|
||||
|
||||
Result rc = 0;
|
||||
size_t out_keydata_size = 0;
|
||||
u8 out_keydata[0x100] = {0};
|
||||
tikEticketDeviceKeyData *eticket_devkey = NULL;
|
||||
|
@ -288,7 +293,7 @@ void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik, const void *titleke
|
|||
if (!tik || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || !titlekey) return;
|
||||
|
||||
bool dev_cert = false;
|
||||
TicketCommonBlock *tik_common_blk = NULL;
|
||||
TikCommonBlock *tik_common_blk = NULL;
|
||||
|
||||
tik_common_blk = tikGetTicketCommonBlockFromTicket(tik);
|
||||
if (!tik_common_blk || tik_common_blk->title_key_type != TikTitleKeyType_Personalized) return;
|
|
@ -3,6 +3,7 @@
|
|||
#ifndef __TIK_H__
|
||||
#define __TIK_H__
|
||||
|
||||
#include <switch.h>
|
||||
#include "signature.h"
|
||||
|
||||
#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES system savefiles */
|
||||
|
@ -70,17 +71,17 @@ typedef struct {
|
|||
} TikCommonBlock;
|
||||
|
||||
typedef struct {
|
||||
SignatureRsa4096Block sig_block;
|
||||
SignatureBlockRsa4096 sig_block;
|
||||
TikCommonBlock tik_common_blk;
|
||||
} TikSigRsa4096;
|
||||
|
||||
typedef struct {
|
||||
SignatureRsa2048Block sig_block;
|
||||
SignatureBlockRsa2048 sig_block;
|
||||
TikCommonBlock tik_common_blk;
|
||||
} TikSigRsa2048;
|
||||
|
||||
typedef struct {
|
||||
SignatureEcsda240Block sig_block;
|
||||
SignatureBlockEcsda240 sig_block;
|
||||
TikCommonBlock tik_common_blk;
|
||||
} TikSigEcsda240;
|
||||
|
||||
|
@ -112,7 +113,7 @@ static inline TikCommonBlock *tikGetTicketCommonBlockFromTicket(Ticket *tik)
|
|||
|
||||
bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id);
|
||||
|
||||
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TicketCommonBlock *tik_common_blk);
|
||||
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TikCommonBlock *tik_common_blk);
|
||||
|
||||
static inline bool tikGetTitleKeyFromTicket(void *dst, Ticket *tik)
|
||||
{
|
164
source/utils.c
Normal file
164
source/utils.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <switch.h>
|
||||
|
||||
//#include "freetype_helper.h"
|
||||
//#include "lvgl_helper.h"
|
||||
#include "services.h"
|
||||
#include "gamecard.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static AppletHookCookie g_systemOverclockCookie = {0};
|
||||
|
||||
static Mutex g_logfileMutex = 0;
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param);
|
||||
|
||||
u64 utilsHidKeysAllDown(void)
|
||||
{
|
||||
u8 controller;
|
||||
u64 keys_down = 0;
|
||||
|
||||
for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_down |= hidKeysDown((HidControllerID)controller);
|
||||
|
||||
return keys_down;
|
||||
}
|
||||
|
||||
u64 utilsHidKeysAllHeld(void)
|
||||
{
|
||||
u8 controller;
|
||||
u64 keys_held = 0;
|
||||
|
||||
for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_held |= hidKeysHeld((HidControllerID)controller);
|
||||
|
||||
return keys_held;
|
||||
}
|
||||
|
||||
void utilsWaitForButtonPress(void)
|
||||
{
|
||||
u64 flag, keys_down;
|
||||
|
||||
/* Don't consider touch screen presses nor stick movement as button inputs */
|
||||
flag = ~(KEY_TOUCH | KEY_LSTICK_LEFT | KEY_LSTICK_RIGHT | KEY_LSTICK_UP | KEY_LSTICK_DOWN | KEY_RSTICK_LEFT | KEY_RSTICK_RIGHT | KEY_RSTICK_UP | KEY_RSTICK_DOWN);
|
||||
|
||||
while(appletMainLoop())
|
||||
{
|
||||
hidScanInput();
|
||||
keys_down = utilsHidKeysAllDown();
|
||||
if (keys_down & flag) break;
|
||||
}
|
||||
}
|
||||
|
||||
void utilsConsoleErrorScreen(const char *fmt, ...)
|
||||
{
|
||||
consoleInit(NULL);
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
vprintf(fmt, va);
|
||||
va_end(va);
|
||||
|
||||
printf("\nPress any button to exit.\n");
|
||||
|
||||
consoleUpdate(NULL);
|
||||
utilsWaitForButtonPress();
|
||||
consoleExit(NULL);
|
||||
}
|
||||
|
||||
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...)
|
||||
{
|
||||
mutexLock(&g_logfileMutex);
|
||||
|
||||
va_list args;
|
||||
FILE *logfile = NULL;
|
||||
|
||||
logfile = fopen(APP_BASE_PATH "nxdumptool.log", "a+");
|
||||
if (!logfile) goto out;
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm *ts = localtime(&now);
|
||||
|
||||
fprintf(logfile, "%d/%d/%d %d:%d:%d -> %s: ", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, func_name);
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(logfile, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
fprintf(logfile, "\r\n");
|
||||
fclose(logfile);
|
||||
|
||||
out:
|
||||
mutexUnlock(&g_logfileMutex);
|
||||
}
|
||||
|
||||
void utilsOverclockSystem(bool restore)
|
||||
{
|
||||
u32 cpuClkRate = ((restore ? CPU_CLKRT_NORMAL : CPU_CLKRT_OVERCLOCKED) * 1000000);
|
||||
u32 memClkRate = ((restore ? MEM_CLKRT_NORMAL : MEM_CLKRT_OVERCLOCKED) * 1000000);
|
||||
servicesChangeHardwareClockRates(cpuClkRate, memClkRate);
|
||||
}
|
||||
|
||||
bool utilsInitializeResources(void)
|
||||
{
|
||||
/* Initialize all needed services */
|
||||
if (!servicesInitialize()) return false;
|
||||
|
||||
/* Initialize FreeType */
|
||||
//if (!freeTypeHelperInitialize()) return false;
|
||||
|
||||
/* Initialize LVGL */
|
||||
//if (!lvglHelperInitialize()) return false;
|
||||
|
||||
/* Overclock system */
|
||||
utilsOverclockSystem(false);
|
||||
|
||||
/* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked) */
|
||||
appletHook(&g_systemOverclockCookie, utilsOverclockSystemAppletHook, NULL);
|
||||
|
||||
/* Initialize gamecard */
|
||||
Result rc = gamecardInitialize();
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
utilsConsoleErrorScreen("gamecard fail\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void utilsCloseResources(void)
|
||||
{
|
||||
/* Deinitialize gamecard */
|
||||
gamecardExit();
|
||||
|
||||
/* Unset our overclock applet hook */
|
||||
appletUnhook(&g_systemOverclockCookie);
|
||||
|
||||
/* Restore hardware clocks */
|
||||
utilsOverclockSystem(true);
|
||||
|
||||
/* Free LVGL resources */
|
||||
//lvglHelperExit();
|
||||
|
||||
/* Free FreeType resouces */
|
||||
//freeTypeHelperExit();
|
||||
|
||||
/* Close initialized services */
|
||||
servicesClose();
|
||||
}
|
||||
|
||||
static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param)
|
||||
{
|
||||
(void)param;
|
||||
|
||||
if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return;
|
||||
|
||||
/* To do: read config here to actually know the value to use with utilsOverclockSystem */
|
||||
utilsOverclockSystem(true);
|
||||
}
|
66
source/utils.h
Normal file
66
source/utils.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __UTILS_H__
|
||||
#define __UTILS_H__
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#define APP_BASE_PATH "sdmc:/switch/nxdumptool/"
|
||||
|
||||
#define LOGFILE(fmt, ...) utilsWriteLogMessage(__func__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member)
|
||||
|
||||
#define SLEEP(x) svcSleepThread((x) * (u64)1000000000)
|
||||
|
||||
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0])))
|
||||
|
||||
#define ROUND_UP(x, y) ((x) + (((y) - ((x) % (y))) % (y))) /* Aligns 'x' bytes to a 'y' bytes boundary. */
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
UtilsCustomFirmwareType_Atmosphere = 0,
|
||||
UtilsCustomFirmwareType_SXOS = 1,
|
||||
UtilsCustomFirmwareType_ReiNX = 2
|
||||
} UtilsCustomFirmwareType;
|
||||
|
||||
typedef struct {
|
||||
u16 major : 6;
|
||||
u16 minor : 6;
|
||||
u16 micro : 4;
|
||||
u16 bugfix;
|
||||
} TitleVersion;
|
||||
|
||||
|
||||
|
||||
|
||||
u64 utilsHidKeysAllDown(void);
|
||||
u64 utilsHidKeysAllHeld(void);
|
||||
|
||||
void utilsWaitForButtonPress(void);
|
||||
|
||||
void utilsConsoleErrorScreen(const char *fmt, ...);
|
||||
|
||||
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...);
|
||||
|
||||
void utilsOverclockSystem(bool restore);
|
||||
|
||||
bool utilsInitializeResources(void);
|
||||
void utilsCloseResources(void);
|
||||
|
||||
|
||||
|
||||
static inline FsStorage *utilsGetEmmcBisSystemStorage(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline u8 utilsGetCustomFirmwareType(void)
|
||||
{
|
||||
return UtilsCustomFirmwareType_Atmosphere;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* __UTILS_H__ */
|
Loading…
Reference in a new issue