1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 04:02:11 +00:00

More NCA parsing changes.

This commit is contained in:
Pablo Curiel 2022-06-24 02:22:01 +02:00
parent b8992d1fdc
commit 37b63aee60
8 changed files with 92 additions and 41 deletions

View file

@ -260,7 +260,7 @@ typedef enum {
} NacpRuntimeUpgrade; } NacpRuntimeUpgrade;
typedef enum { typedef enum {
NacpSupportingLimitedLicenses_Demo = BIT(1), NacpSupportingLimitedLicenses_Demo = BIT(0),
NacpSupportingLimitedLicenses_Count = 1 ///< Total values supported by this enum. NacpSupportingLimitedLicenses_Count = 1 ///< Total values supported by this enum.
} NacpSupportingLimitedLicenses; } NacpSupportingLimitedLicenses;
@ -368,7 +368,7 @@ typedef struct {
s64 device_save_data_journal_size; s64 device_save_data_journal_size;
s64 bcat_delivery_cache_storage_size; s64 bcat_delivery_cache_storage_size;
char application_error_code_category[0x8]; char application_error_code_category[0x8];
u64 local_communication_id[8]; u64 local_communication_id[0x8];
u8 logo_type; ///< NacpLogoType. u8 logo_type; ///< NacpLogoType.
u8 logo_handling; ///< NacpLogoHandling. u8 logo_handling; ///< NacpLogoHandling.
u8 runtime_add_on_content_install; ///< NacpRuntimeAddOnContentInstall. u8 runtime_add_on_content_install; ///< NacpRuntimeAddOnContentInstall.
@ -390,7 +390,7 @@ typedef struct {
s64 cache_storage_journal_size; s64 cache_storage_journal_size;
s64 cache_storage_data_and_journal_size_max; s64 cache_storage_data_and_journal_size_max;
u16 cache_storage_index_max; u16 cache_storage_index_max;
u8 reserved_1[0x1]; u8 reserved_1;
u8 runtime_upgrade; ///< NacpRuntimeUpgrade. u8 runtime_upgrade; ///< NacpRuntimeUpgrade.
u32 supporting_limited_licenses; ///< NacpSupportingLimitedLicenses. u32 supporting_limited_licenses; ///< NacpSupportingLimitedLicenses.
u64 play_log_queryable_application_id[0x10]; u64 play_log_queryable_application_id[0x10];

View file

@ -85,7 +85,7 @@ typedef enum {
NcaKeyGeneration_Since910NUP = 11, ///< 9.1.0 - 12.0.3. NcaKeyGeneration_Since910NUP = 11, ///< 9.1.0 - 12.0.3.
NcaKeyGeneration_Since1210NUP = 12, ///< 12.1.0. NcaKeyGeneration_Since1210NUP = 12, ///< 12.1.0.
NcaKeyGeneration_Since1300NUP = 13, ///< 13.0.0 - 13.2.1. NcaKeyGeneration_Since1300NUP = 13, ///< 13.0.0 - 13.2.1.
NcaKeyGeneration_Since1400NUP = 14, ///< 14.0.0 - 14.1.0. NcaKeyGeneration_Since1400NUP = 14, ///< 14.0.0 - 14.1.2.
NcaKeyGeneration_Current = NcaKeyGeneration_Since1400NUP, NcaKeyGeneration_Current = NcaKeyGeneration_Since1400NUP,
NcaKeyGeneration_Max = 32 NcaKeyGeneration_Max = 32
} NcaKeyGeneration; } NcaKeyGeneration;
@ -100,7 +100,7 @@ typedef enum {
/// 'NcaSignatureKeyGeneration_Current' will always point to the last known key generation value. /// 'NcaSignatureKeyGeneration_Current' will always point to the last known key generation value.
typedef enum { typedef enum {
NcaSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1. NcaSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1.
NcaSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 14.1.0. NcaSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 14.1.2.
NcaSignatureKeyGeneration_Current = NcaSignatureKeyGeneration_Since900NUP, NcaSignatureKeyGeneration_Current = NcaSignatureKeyGeneration_Since900NUP,
NcaSignatureKeyGeneration_Max = (NcaSignatureKeyGeneration_Current + 1) NcaSignatureKeyGeneration_Max = (NcaSignatureKeyGeneration_Current + 1)
} NcaSignatureKeyGeneration; } NcaSignatureKeyGeneration;
@ -164,18 +164,23 @@ typedef enum {
} NcaFsType; } NcaFsType;
typedef enum { typedef enum {
NcaHashType_Auto = 0, NcaHashType_Auto = 0,
NcaHashType_None = 1, NcaHashType_None = 1,
NcaHashType_HierarchicalSha256 = 2, ///< Used by NcaFsType_PartitionFs. NcaHashType_HierarchicalSha256 = 2, ///< Used by NcaFsType_PartitionFs.
NcaHashType_HierarchicalIntegrity = 3 ///< Used by NcaFsType_RomFs. NcaHashType_HierarchicalIntegrity = 3, ///< Used by NcaFsType_RomFs.
NcaHashType_AutoSha3 = 4,
NcaHashType_HierarchicalSha3256 = 5,
NcaHashType_HierarchicalIntegritySha3 = 6
} NcaHashType; } NcaHashType;
typedef enum { typedef enum {
NcaEncryptionType_Auto = 0, NcaEncryptionType_Auto = 0,
NcaEncryptionType_None = 1, NcaEncryptionType_None = 1,
NcaEncryptionType_AesXts = 2, NcaEncryptionType_AesXts = 2,
NcaEncryptionType_AesCtr = 3, NcaEncryptionType_AesCtr = 3,
NcaEncryptionType_AesCtrEx = 4 NcaEncryptionType_AesCtrEx = 4,
NcaEncryptionType_AesCtrSkipLayerHash = 5,
NcaEncryptionType_AesCtrExSkipLayerHash = 6
} NcaEncryptionType; } NcaEncryptionType;
typedef struct { typedef struct {
@ -298,9 +303,18 @@ NXDT_ASSERT(NcaSparseInfo, 0x30);
/// Used in NCAs with LZ4-compressed sections. /// Used in NCAs with LZ4-compressed sections.
typedef struct { typedef struct {
NcaBucketInfo bucket; NcaBucketInfo bucket;
u8 reserved[0x8];
} NcaCompressionInfo; } NcaCompressionInfo;
NXDT_ASSERT(NcaCompressionInfo, 0x20); NXDT_ASSERT(NcaCompressionInfo, 0x28);
typedef struct {
u64 offset;
u64 size;
u8 hash[SHA256_HASH_SIZE];
} NcaMetaDataHashDataInfo;
NXDT_ASSERT(NcaMetaDataHashDataInfo, 0x30);
/// Four NCA FS headers are placed right after the 0x400 byte long NCA header in NCA2 and NCA3. /// Four NCA FS headers are placed right after the 0x400 byte long NCA header in NCA2 and NCA3.
/// NCA0 place the FS headers at the start sector from the NcaFsInfo entries. /// NCA0 place the FS headers at the start sector from the NcaFsInfo entries.
@ -315,7 +329,8 @@ typedef struct {
NcaAesCtrUpperIv aes_ctr_upper_iv; NcaAesCtrUpperIv aes_ctr_upper_iv;
NcaSparseInfo sparse_info; NcaSparseInfo sparse_info;
NcaCompressionInfo compression_info; NcaCompressionInfo compression_info;
u8 reserved_2[0x68]; NcaMetaDataHashDataInfo hash_data_info;
u8 reserved_2[0x30];
} NcaFsHeader; } NcaFsHeader;
NXDT_ASSERT(NcaFsHeader, 0x200); NXDT_ASSERT(NcaFsHeader, 0x200);
@ -340,8 +355,9 @@ typedef struct {
u8 section_num; u8 section_num;
u64 section_offset; u64 section_offset;
u64 section_size; u64 section_size;
u8 section_type; ///< NcaFsSectionType. u8 hash_type; ///< NcaHashType.
u8 encryption_type; ///< NcaEncryptionType. u8 encryption_type; ///< NcaEncryptionType.
u8 section_type; ///< NcaFsSectionType.
u8 ctr[AES_BLOCK_SIZE]; ///< Used to update the AES CTR context IV based on the desired offset. u8 ctr[AES_BLOCK_SIZE]; ///< Used to update the AES CTR context IV based on the desired offset.
Aes128CtrContext ctr_ctx; Aes128CtrContext ctr_ctx;
Aes128XtsContext xts_decrypt_ctx; Aes128XtsContext xts_decrypt_ctx;

View file

@ -42,7 +42,7 @@ extern "C" {
/// 'NpdmSignatureKeyGeneration_Current' will always point to the last known key generation value. /// 'NpdmSignatureKeyGeneration_Current' will always point to the last known key generation value.
typedef enum { typedef enum {
NpdmSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1. NpdmSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1.
NpdmSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 14.1.0. NpdmSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 14.1.2.
NpdmSignatureKeyGeneration_Current = NpdmSignatureKeyGeneration_Since900NUP, NpdmSignatureKeyGeneration_Current = NpdmSignatureKeyGeneration_Since900NUP,
NpdmSignatureKeyGeneration_Max = (NpdmSignatureKeyGeneration_Current + 1) NpdmSignatureKeyGeneration_Max = (NpdmSignatureKeyGeneration_Current + 1)
} NpdmSignatureKeyGeneration; } NpdmSignatureKeyGeneration;
@ -92,8 +92,8 @@ NXDT_ASSERT(NpdmMetaHeader, 0x80);
typedef enum { typedef enum {
NpdmMemoryRegion_Application = 0, NpdmMemoryRegion_Application = 0,
NpdmMemoryRegion_Applet = 1, NpdmMemoryRegion_Applet = 1,
NpdmMemoryRegion_SystemSecure = 2, NpdmMemoryRegion_SecureSystem = 2,
NpdmMemoryRegion_SystemNonSecure = 3, NpdmMemoryRegion_NonSecureSystem = 3,
/// Old. /// Old.
NpdmMemoryRegion_NonSecure = NpdmMemoryRegion_Application, NpdmMemoryRegion_NonSecure = NpdmMemoryRegion_Application,
@ -103,8 +103,8 @@ typedef enum {
typedef struct { typedef struct {
u32 production : 1; u32 production : 1;
u32 unqualified_approval : 1; u32 unqualified_approval : 1;
u32 memory_region : 2; ///< NpdmMemoryRegion. u32 memory_region : 5; ///< NpdmMemoryRegion.
u32 reserved : 28; u32 reserved : 25;
} NpdmAcidFlags; } NpdmAcidFlags;
NXDT_ASSERT(NpdmAcidFlags, 0x4); NXDT_ASSERT(NpdmAcidFlags, 0x4);

View file

@ -148,7 +148,7 @@ NX_INLINE void gamecardCloseHandle(void);
static bool gamecardOpenStorageArea(u8 area); static bool gamecardOpenStorageArea(u8 area);
static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset); static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset);
static void gamecardCloseStorageArea(void); static void gamecardCloseStorageArea(bool switch_to_normal);
static bool gamecardGetStorageAreasSizes(void); static bool gamecardGetStorageAreasSizes(void);
NX_INLINE u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size); NX_INLINE u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size);
@ -864,7 +864,7 @@ static void gamecardFreeInfo(bool clear_status)
g_gameCardHfsCount = 0; g_gameCardHfsCount = 0;
gamecardCloseStorageArea(); gamecardCloseStorageArea(true);
if (clear_status) g_gameCardStatus = GameCardStatus_NotInserted; if (clear_status) g_gameCardStatus = GameCardStatus_NotInserted;
} }
@ -1033,9 +1033,6 @@ static bool gamecardGetHandleAndStorage(u32 partition)
NX_INLINE void gamecardCloseHandle(void) NX_INLINE void gamecardCloseHandle(void)
{ {
/* TODO: find a way to properly close a gamecard handle. */
if (!g_gameCardHandle.value) return;
svcCloseHandle(g_gameCardHandle.value);
g_gameCardHandle.value = 0; g_gameCardHandle.value = 0;
} }
@ -1051,7 +1048,7 @@ static bool gamecardOpenStorageArea(u8 area)
if (g_gameCardHandle.value && serviceIsActive(&(g_gameCardStorage.s)) && g_gameCardCurrentStorageArea == area) return true; if (g_gameCardHandle.value && serviceIsActive(&(g_gameCardStorage.s)) && g_gameCardCurrentStorageArea == area) return true;
/* Close both gamecard handle and open storage area. */ /* Close both gamecard handle and open storage area. */
gamecardCloseStorageArea(); gamecardCloseStorageArea(false);
/* Retrieve both a new gamecard handle and a storage area handle. */ /* Retrieve both a new gamecard handle and a storage area handle. */
if (!gamecardGetHandleAndStorage(area - 1)) /* Zero-based index. */ if (!gamecardGetHandleAndStorage(area - 1)) /* Zero-based index. */
@ -1143,8 +1140,22 @@ end:
return success; return success;
} }
static void gamecardCloseStorageArea(void) static void gamecardCloseStorageArea(bool switch_to_normal)
{ {
if (g_gameCardCurrentStorageArea == GameCardStorageArea_None) return;
/* Workaround: try to reset the Lotus driver by switching to the normal storage area before closing all gamecard comms. */
if (switch_to_normal && g_gameCardCurrentStorageArea == GameCardStorageArea_Secure)
{
LOG_MSG("Switching to normal area before shutting down.");
if (gamecardOpenStorageArea(GameCardStorageArea_Normal))
{
/* Perform a bogus read (just one page). */
u8 bogus[GAMECARD_PAGE_SIZE] = {0};
gamecardReadStorageArea(bogus, sizeof(bogus), 0);
}
}
if (serviceIsActive(&(g_gameCardStorage.s))) if (serviceIsActive(&(g_gameCardStorage.s)))
{ {
fsStorageClose(&g_gameCardStorage); fsStorageClose(&g_gameCardStorage);
@ -1172,7 +1183,7 @@ static bool gamecardGetStorageAreasSizes(void)
rc = fsStorageGetSize(&g_gameCardStorage, (s64*)&area_size); rc = fsStorageGetSize(&g_gameCardStorage, (s64*)&area_size);
gamecardCloseStorageArea(); gamecardCloseStorageArea(false);
if (R_FAILED(rc) || !area_size) if (R_FAILED(rc) || !area_size)
{ {

View file

@ -573,14 +573,14 @@ static bool keysDeriveNcaHeaderKey(void)
rc = splCryptoGenerateAesKey(g_ncaKeyset.nca_header_kek_sealed, g_ncaKeyset.nca_header_key_source, g_ncaKeyset.nca_header_key); rc = splCryptoGenerateAesKey(g_ncaKeyset.nca_header_kek_sealed, g_ncaKeyset.nca_header_key_source, g_ncaKeyset.nca_header_key);
if (R_FAILED(rc)) if (R_FAILED(rc))
{ {
LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X) (nca_header_key, part 1).", rc); LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X) (nca_header_key) (#1).", rc);
return false; return false;
} }
rc = splCryptoGenerateAesKey(g_ncaKeyset.nca_header_kek_sealed, g_ncaKeyset.nca_header_key_source + AES_128_KEY_SIZE, g_ncaKeyset.nca_header_key + AES_128_KEY_SIZE); rc = splCryptoGenerateAesKey(g_ncaKeyset.nca_header_kek_sealed, g_ncaKeyset.nca_header_key_source + AES_128_KEY_SIZE, g_ncaKeyset.nca_header_key + AES_128_KEY_SIZE);
if (R_FAILED(rc)) if (R_FAILED(rc))
{ {
LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X) (nca_header_key, part 2).", rc); LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X) (nca_header_key) (#2).", rc);
return false; return false;
} }

View file

@ -194,7 +194,26 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
/* Check if we're dealing with an invalid start offset or an empty size. */ /* Check if we're dealing with an invalid start offset or an empty size. */
if (fs_ctx->section_offset < sizeof(NcaHeader) || !fs_ctx->section_size) continue; if (fs_ctx->section_offset < sizeof(NcaHeader) || !fs_ctx->section_size) continue;
/* Determine encryption type. */ /* Determine hash and encryption types. */
fs_ctx->hash_type = fs_ctx->header.hash_type;
if (fs_ctx->hash_type == NcaHashType_Auto || fs_ctx->hash_type == NcaHashType_AutoSha3)
{
switch(fs_ctx->section_num)
{
case 0: /* ExeFS Partition FS. */
fs_ctx->hash_type = (fs_ctx->hash_type == NcaHashType_Auto ? NcaHashType_HierarchicalSha256 : NcaHashType_HierarchicalSha3256);
break;
case 1: /* RomFS. */
fs_ctx->hash_type = (fs_ctx->hash_type == NcaHashType_Auto ? NcaHashType_HierarchicalIntegrity : NcaHashType_HierarchicalIntegritySha3);
break;
case 2: /* Logo Partition FS. */
fs_ctx->hash_type = NcaHashType_None;
break;
default:
break;
}
}
fs_ctx->encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : fs_ctx->header.encryption_type); fs_ctx->encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : fs_ctx->header.encryption_type);
if (fs_ctx->encryption_type == NcaEncryptionType_Auto) if (fs_ctx->encryption_type == NcaEncryptionType_Auto)
{ {
@ -212,19 +231,21 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
} }
} }
/* Check if we're dealing with an invalid encryption type value. */ /* Check if we're dealing with invalid hash/encryption type values. */
if (fs_ctx->encryption_type == NcaEncryptionType_Auto || fs_ctx->encryption_type > NcaEncryptionType_AesCtrEx) continue; if (fs_ctx->hash_type == NcaHashType_Auto || fs_ctx->hash_type == NcaHashType_AutoSha3 || fs_ctx->hash_type > NcaHashType_HierarchicalIntegritySha3 || \
fs_ctx->encryption_type == NcaEncryptionType_Auto || fs_ctx->encryption_type > NcaEncryptionType_AesCtrExSkipLayerHash) continue;
/* Determine FS section type. */ /* Determine FS section type. */
if (fs_ctx->header.fs_type == NcaFsType_PartitionFs && fs_ctx->header.hash_type == NcaHashType_HierarchicalSha256) if (fs_ctx->header.fs_type == NcaFsType_PartitionFs && (fs_ctx->hash_type == NcaHashType_HierarchicalSha256 || fs_ctx->hash_type == NcaHashType_HierarchicalSha3256))
{ {
fs_ctx->section_type = NcaFsSectionType_PartitionFs; fs_ctx->section_type = NcaFsSectionType_PartitionFs;
} else } else
if (fs_ctx->header.fs_type == NcaFsType_RomFs && fs_ctx->header.hash_type == NcaHashType_HierarchicalIntegrity) if (fs_ctx->header.fs_type == NcaFsType_RomFs && (fs_ctx->hash_type == NcaHashType_HierarchicalIntegrity || fs_ctx->hash_type == NcaHashType_HierarchicalIntegritySha3))
{ {
fs_ctx->section_type = (fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx ? NcaFsSectionType_PatchRomFs : NcaFsSectionType_RomFs); fs_ctx->section_type = ((fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx || fs_ctx->encryption_type == NcaEncryptionType_AesCtrExSkipLayerHash) ? \
NcaFsSectionType_PatchRomFs : NcaFsSectionType_RomFs);
} else } else
if (fs_ctx->header.fs_type == NcaFsType_RomFs && fs_ctx->header.hash_type == NcaHashType_HierarchicalSha256 && out->format_version == NcaVersion_Nca0) if (fs_ctx->header.fs_type == NcaFsType_RomFs && fs_ctx->hash_type == NcaHashType_HierarchicalSha256 && out->format_version == NcaVersion_Nca0)
{ {
fs_ctx->section_type = NcaFsSectionType_Nca0RomFs; fs_ctx->section_type = NcaFsSectionType_Nca0RomFs;
} }
@ -753,7 +774,7 @@ static bool ncaEncryptKeyArea(NcaContext *ctx)
} }
/* Clear encrypted key area. */ /* Clear encrypted key area. */
memset(&(ctx->header.encrypted_key_area), 0, NCA_USED_KEY_AREA_SIZE); memset(&(ctx->header.encrypted_key_area), 0, sizeof(NcaEncryptedKeyArea));
/* Initialize AES-128-ECB encryption context using the retrieved KAEK. */ /* Initialize AES-128-ECB encryption context using the retrieved KAEK. */
aes128ContextCreate(&key_area_ctx, kaek, true); aes128ContextCreate(&key_area_ctx, kaek, true);

View file

@ -341,7 +341,7 @@ static u32 save_journal_storage_read(journal_storage_ctx_t *ctx, remap_storage_c
static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_ctx_t *ctx, u64 master_hash_offset, ivfc_save_hdr_t *ivfc) static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_ctx_t *ctx, u64 master_hash_offset, ivfc_save_hdr_t *ivfc)
{ {
if (!ctx || !ctx->levels || !ivfc || !ivfc->num_levels) if (!ctx || !ivfc || !ivfc->num_levels)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;

View file

@ -238,6 +238,7 @@ static const TitleSystemEntry g_systemTitles[] = {
{ 0x0100000000001014, "PlayReport" }, { 0x0100000000001014, "PlayReport" },
{ 0x0100000000001015, "MaintenanceMenu" }, { 0x0100000000001015, "MaintenanceMenu" },
{ 0x0100000000001016, "application_install" }, ///< Placeholder. { 0x0100000000001016, "application_install" }, ///< Placeholder.
{ 0x0100000000001017, "nn.am.SystemReportTask" }, ///< Placeholder.
{ 0x0100000000001018, "systemupdate_dl_throughput" }, ///< Placeholder. { 0x0100000000001018, "systemupdate_dl_throughput" }, ///< Placeholder.
{ 0x0100000000001019, "volume_update"}, ///< Placeholder. { 0x0100000000001019, "volume_update"}, ///< Placeholder.
{ 0x010000000000101A, "gift" }, { 0x010000000000101A, "gift" },
@ -256,6 +257,7 @@ static const TitleSystemEntry g_systemTitles[] = {
{ 0x0100000000001028, "ns_unknown_1" }, ///< Placeholder. { 0x0100000000001028, "ns_unknown_1" }, ///< Placeholder.
{ 0x010000000000102A, "am_unknown_2" }, ///< Placeholder. { 0x010000000000102A, "am_unknown_2" }, ///< Placeholder.
{ 0x010000000000102B, "glue_unknown_1" }, ///< Placeholder. { 0x010000000000102B, "glue_unknown_1" }, ///< Placeholder.
{ 0x010000000000102C, "am_unknown_3" }, ///< Placeholder.
{ 0x010000000000102E, "blacklist" }, { 0x010000000000102E, "blacklist" },
{ 0x010000000000102F, "content_delivery" }, { 0x010000000000102F, "content_delivery" },
{ 0x0100000000001031, "ns_unknown_2" }, ///< Placeholder. { 0x0100000000001031, "ns_unknown_2" }, ///< Placeholder.
@ -264,6 +266,7 @@ static const TitleSystemEntry g_systemTitles[] = {
{ 0x0100000000001034, "ngct_unknown" }, ///< Placeholder. { 0x0100000000001034, "ngct_unknown" }, ///< Placeholder.
{ 0x0100000000001037, "nim_unknown" }, ///< Placeholder. { 0x0100000000001037, "nim_unknown" }, ///< Placeholder.
{ 0x0100000000001038, "sample" }, { 0x0100000000001038, "sample" },
{ 0x010000000000103C, "mnpp" }, ///< Placeholder.
{ 0x0100000000001FFF, "EndOceanProgramId" }, { 0x0100000000001FFF, "EndOceanProgramId" },
/* Development system applets. */ /* Development system applets. */