mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-08 11:51:48 +00:00
List content infos as part of title list entries.
Finally got rid of location resolver stuff.
This commit is contained in:
parent
2521ac3f15
commit
158e424b96
8 changed files with 314 additions and 346 deletions
257
source/main.c
257
source/main.c
|
@ -22,6 +22,7 @@
|
|||
#include "bktr.h"
|
||||
#include "gamecard.h"
|
||||
#include "usb.h"
|
||||
#include "title.h"
|
||||
|
||||
#define TEST_BUF_SIZE 0x800000
|
||||
|
||||
|
@ -212,41 +213,14 @@ int main(int argc, char *argv[])
|
|||
goto out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
goto out;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Result rc = 0;
|
||||
|
||||
u8 *buf = NULL;
|
||||
|
||||
u64 base_tid = (u64)0x010082400BCC6000; // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000 | Untitled Goose Game 0x010082400BCC6000
|
||||
u64 update_tid = (base_tid | 0x800);
|
||||
u64 base_tid = (u64)0x01004AB00A260000; // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000 | Untitled Goose Game 0x010082400BCC6000
|
||||
u64 update_tid = titleGetPatchIdByApplicationId(base_tid);
|
||||
|
||||
Ticket base_tik = {0}, update_tik = {0};
|
||||
TitleInfo *base_title_info = NULL, *update_title_info = NULL;
|
||||
NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL;
|
||||
NcmContentStorage ncm_storage_sdcard = {0}, ncm_storage_emmc = {0};
|
||||
|
||||
char path[FS_MAX_PATH] = {0};
|
||||
LrLocationResolver resolver_sdcard = {0}, resolver_emmc = {0};
|
||||
NcmContentInfo content_info = {0};
|
||||
Ticket base_tik = {0}, update_tik = {0};
|
||||
|
||||
BktrContext bktr_ctx = {0};
|
||||
|
||||
|
@ -280,110 +254,29 @@ int main(int argc, char *argv[])
|
|||
|
||||
consolePrint("update nca ctx buf succeeded\n");
|
||||
|
||||
rc = ncmOpenContentStorage(&ncm_storage_sdcard, NcmStorageId_SdCard);
|
||||
if (R_FAILED(rc))
|
||||
base_title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_Any, base_tid);
|
||||
update_title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_Any, update_tid);
|
||||
|
||||
if (!base_title_info || !update_title_info)
|
||||
{
|
||||
consolePrint("ncm open storage sdcard failed\n");
|
||||
consolePrint("title info failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("ncm open storage sdcard succeeded\n");
|
||||
consolePrint("title info succeeded\n");
|
||||
|
||||
rc = ncmOpenContentStorage(&ncm_storage_emmc, NcmStorageId_BuiltInUser);
|
||||
if (R_FAILED(rc))
|
||||
if (!ncaInitializeContext(base_nca_ctx, base_title_info->storage_id, 0, titleGetContentInfoByTypeAndIdOffset(base_title_info, NcmContentType_Program, 0), &base_tik))
|
||||
{
|
||||
consolePrint("ncm open storage emmc failed\n");
|
||||
consolePrint("nca initialize base ctx failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("ncm open storage emmc succeeded\n");
|
||||
|
||||
rc = lrInitialize();
|
||||
if (R_FAILED(rc))
|
||||
if (!ncaInitializeContext(update_nca_ctx, update_title_info->storage_id, 0, titleGetContentInfoByTypeAndIdOffset(update_title_info, NcmContentType_Program, 0), &update_tik))
|
||||
{
|
||||
consolePrint("lrInitialize failed\n");
|
||||
consolePrint("nca initialize update ctx failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("lrInitialize succeeded\n");
|
||||
|
||||
rc = lrOpenLocationResolver(NcmStorageId_SdCard, &resolver_sdcard);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
consolePrint("lrOpenLocationResolver sdcard failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("lrOpenLocationResolver sdcard succeeded\n");
|
||||
|
||||
rc = lrOpenLocationResolver(NcmStorageId_BuiltInUser, &resolver_emmc);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
consolePrint("lrOpenLocationResolver emmc failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("lrOpenLocationResolver emmc succeeded\n");
|
||||
|
||||
for(u32 i = 0; i < 2; i++)
|
||||
{
|
||||
for(u32 j = 0; j < 2; j++)
|
||||
{
|
||||
NcmContentStorage *ncm_storage = (j == 0 ? &ncm_storage_sdcard : &ncm_storage_emmc);
|
||||
LrLocationResolver *resolver = (j == 0 ? &resolver_sdcard : &resolver_emmc);
|
||||
NcaContext *nca_ctx = (i == 0 ? base_nca_ctx : update_nca_ctx);
|
||||
Ticket *tik = (i == 0 ? &base_tik : &update_tik);
|
||||
|
||||
rc = lrLrResolveProgramPath(resolver, i == 0 ? base_tid : update_tid, path);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
consolePrint("lrLrResolveProgramPath %s,%s failed\n", i == 0 ? "base" : "update", j == 0 ? "sdcard" : "emmc");
|
||||
if (j == 0) continue;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("lrLrResolveProgramPath %s,%s succeeded\n", i == 0 ? "base" : "update", j == 0 ? "sdcard" : "emmc");
|
||||
|
||||
memset(&content_info, 0, sizeof(NcmContentInfo));
|
||||
|
||||
memmove(path, strrchr(path, '/') + 1, SHA256_HASH_SIZE + 4);
|
||||
path[SHA256_HASH_SIZE + 4] = '\0';
|
||||
|
||||
consolePrint("Program NCA (%s,%s): %s\n", i == 0 ? "base" : "update", j == 0 ? "sdcard" : "emmc", path);
|
||||
|
||||
for(u32 i = 0; i < SHA256_HASH_SIZE; i++)
|
||||
{
|
||||
char val = (('a' <= path[i] && path[i] <= 'f') ? (path[i] - 'a' + 0xA) : (path[i] - '0'));
|
||||
if ((i & 1) == 0) val <<= 4;
|
||||
content_info.content_id.c[i >> 1] |= val;
|
||||
}
|
||||
|
||||
content_info.content_type = NcmContentType_Program;
|
||||
|
||||
u64 content_size = 0;
|
||||
rc = ncmContentStorageGetSizeFromContentId(ncm_storage, (s64*)&content_size, &(content_info.content_id));
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
consolePrint("ncmContentStorageGetSizeFromContentId %s,%s failed\n", i == 0 ? "base" : "update", j == 0 ? "sdcard" : "emmc");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("ncmContentStorageGetSizeFromContentId %s,%s succeeded\n", i == 0 ? "base" : "update", j == 0 ? "sdcard" : "emmc");
|
||||
|
||||
memcpy(&(content_info.size), &content_size, 6);
|
||||
|
||||
if (!ncaInitializeContext(nca_ctx, i == 0 ? NcmStorageId_SdCard : NcmStorageId_BuiltInUser, ncm_storage, 0, &content_info, tik))
|
||||
{
|
||||
consolePrint("nca initialize ctx %s,%s failed\n", i == 0 ? "base" : "update", j == 0 ? "sdcard" : "emmc");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("nca initialize ctx %s,%s succeeded\n", i == 0 ? "base" : "update", j == 0 ? "sdcard" : "emmc");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bktrInitializeContext(&bktr_ctx, &(base_nca_ctx->fs_contexts[1]), &(update_nca_ctx->fs_contexts[1])))
|
||||
{
|
||||
consolePrint("bktr initialize ctx failed\n");
|
||||
|
@ -392,117 +285,6 @@ int main(int argc, char *argv[])
|
|||
|
||||
consolePrint("bktr initialize ctx succeeded\n");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
FILE *tmp_file = NULL;
|
||||
RomFileSystemFileEntry *romfs_file_entry = NULL;
|
||||
RomFileSystemFileEntryPatch romfs_patch = {0};
|
||||
|
||||
romfs_file_entry = romfsGetFileEntryByPath(&(bktr_ctx.base_romfs_ctx), "/Data/rawsettings");
|
||||
if (!romfs_file_entry)
|
||||
{
|
||||
consolePrint("romfs get file entry by path failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("romfs get file entry by path success: %s | 0x%lX | %p\n", romfs_file_entry->name, romfs_file_entry->size, romfs_file_entry);
|
||||
|
||||
if (!romfsReadFileEntryData(&(bktr_ctx.base_romfs_ctx), romfs_file_entry, buf, romfs_file_entry->size, 0))
|
||||
{
|
||||
consolePrint("romfs read file entry failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("romfs read file entry success\n");
|
||||
|
||||
memset(buf, 0xAA, romfs_file_entry->size);
|
||||
|
||||
if (!romfsGenerateFileEntryPatch(&(bktr_ctx.base_romfs_ctx), romfs_file_entry, buf, romfs_file_entry->size, 0, &romfs_patch))
|
||||
{
|
||||
consolePrint("romfs file entry patch failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("romfs file entry patch success\n");
|
||||
|
||||
if (!ncaEncryptHeader(base_nca_ctx))
|
||||
{
|
||||
consolePrint("nca header mod not encrypted\n");
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("nca header mod encrypted\n");
|
||||
|
||||
tmp_file = fopen("sdmc:/program_nca_mod.bin", "wb");
|
||||
if (!tmp_file)
|
||||
{
|
||||
consolePrint("program nca mod not saved\n");
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
u64 block_size = TEST_BUF_SIZE;
|
||||
for(u64 i = 0; i < base_nca_ctx->content_size; i += block_size)
|
||||
{
|
||||
if (block_size > (base_nca_ctx->content_size - i)) block_size = (base_nca_ctx->content_size - i);
|
||||
|
||||
if (!ncaReadContentFile(base_nca_ctx, buf, block_size, i))
|
||||
{
|
||||
consolePrint("failed to read 0x%lX chunk from offset 0x%lX\n", block_size, i);
|
||||
fclose(tmp_file);
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
memcpy(buf, &(base_nca_ctx->header), sizeof(NcaHeader));
|
||||
for(u64 j = 0; j < 4; j++) memcpy(buf + sizeof(NcaHeader) + (j * sizeof(NcaFsHeader)), &(base_nca_ctx->fs_contexts[j].header), sizeof(NcaFsHeader));
|
||||
}
|
||||
|
||||
romfsWriteFileEntryPatchToMemoryBuffer(&(bktr_ctx.base_romfs_ctx), &romfs_patch, buf, block_size, i);
|
||||
|
||||
fwrite(buf, 1, block_size, tmp_file);
|
||||
fflush(tmp_file);
|
||||
|
||||
consolePrint("wrote 0x%lX bytes to offset 0x%lX\n", block_size, i);
|
||||
}
|
||||
|
||||
fclose(tmp_file);
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
|
||||
goto out2;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
shared_data.bktr_ctx = &bktr_ctx;
|
||||
shared_data.data = buf;
|
||||
shared_data.data_size = 0;
|
||||
|
@ -616,15 +398,6 @@ out2:
|
|||
|
||||
bktrFreeContext(&bktr_ctx);
|
||||
|
||||
if (serviceIsActive(&(resolver_emmc.s))) serviceClose(&(resolver_emmc.s));
|
||||
if (serviceIsActive(&(resolver_sdcard.s))) serviceClose(&(resolver_sdcard.s));
|
||||
|
||||
lrExit();
|
||||
|
||||
if (serviceIsActive(&(ncm_storage_emmc.s))) ncmContentStorageClose(&ncm_storage_emmc);
|
||||
|
||||
if (serviceIsActive(&(ncm_storage_sdcard.s))) ncmContentStorageClose(&ncm_storage_sdcard);
|
||||
|
||||
if (update_nca_ctx) free(update_nca_ctx);
|
||||
|
||||
if (base_nca_ctx) free(base_nca_ctx);
|
||||
|
|
11
source/nca.c
11
source/nca.c
|
@ -24,6 +24,7 @@
|
|||
#include "aes.h"
|
||||
#include "rsa.h"
|
||||
#include "gamecard.h"
|
||||
#include "title.h"
|
||||
|
||||
#define NCA_CRYPTO_BUFFER_SIZE 0x800000 /* 8 MiB. */
|
||||
|
||||
|
@ -82,10 +83,12 @@ void ncaFreeCryptoBuffer(void)
|
|||
mutexUnlock(&g_ncaCryptoBufferMutex);
|
||||
}
|
||||
|
||||
bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm_storage, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik)
|
||||
bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik)
|
||||
{
|
||||
if (!out || !tik || (storage_id != NcmStorageId_GameCard && !ncm_storage) || (storage_id == NcmStorageId_GameCard && hfs_partition_type > GameCardHashFileSystemPartitionType_Secure) || \
|
||||
!content_info || content_info->content_type > NcmContentType_DeltaFragment)
|
||||
NcmContentStorage *ncm_storage = NULL;
|
||||
|
||||
if (!out || (storage_id != NcmStorageId_GameCard && !(ncm_storage = titleGetNcmStorageByStorageId(storage_id))) || \
|
||||
(storage_id == NcmStorageId_GameCard && hfs_partition_type > GameCardHashFileSystemPartitionType_Boot) || !content_info || content_info->content_type > NcmContentType_DeltaFragment || !tik)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -104,7 +107,7 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
out->content_type = content_info->content_type;
|
||||
out->id_offset = content_info->id_offset;
|
||||
|
||||
ncaConvertNcmContentSizeToU64(content_info->size, &(out->content_size));
|
||||
titleConvertNcmContentSizeToU64(content_info->size, &(out->content_size));
|
||||
if (out->content_size < NCA_FULL_HEADER_LENGTH)
|
||||
{
|
||||
LOGFILE("Invalid size for NCA \"%s\"!", out->content_id_str);
|
||||
|
|
15
source/nca.h
15
source/nca.h
|
@ -348,11 +348,10 @@ bool ncaAllocateCryptoBuffer(void);
|
|||
void ncaFreeCryptoBuffer(void);
|
||||
|
||||
/// Initializes a NCA context.
|
||||
/// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' argument must point to a valid NcmContentStorage instance, previously opened using the same NcmStorageId value.
|
||||
/// If 'storage_id' == NcmStorageId_GameCard, the 'hfs_partition_type' argument must be a valid GameCardHashFileSystemPartitionType value.
|
||||
/// If the NCA holds a populated Rights ID field, and if the Ticket element pointed to by 'tik' hasn't been filled, ticket data will be retrieved.
|
||||
/// If ticket data can't be retrieved, the context will still be initialized, but anything that involves working with encrypted NCA FS section blocks won't be possible (e.g. ncaReadFsSection()).
|
||||
bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm_storage, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik);
|
||||
bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik);
|
||||
|
||||
/// Reads raw encrypted data from a NCA using an input context, previously initialized by ncaInitializeContext().
|
||||
/// Input offset must be relative to the start of the NCA content file.
|
||||
|
@ -428,18 +427,6 @@ bool ncaEncryptHeader(NcaContext *ctx);
|
|||
|
||||
/// Miscellaneous functions.
|
||||
|
||||
NX_INLINE void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out)
|
||||
{
|
||||
if (!size || !out) return;
|
||||
*out = 0;
|
||||
memcpy(out, size, 6);
|
||||
}
|
||||
|
||||
NX_INLINE void ncaConvertU64ToNcmContentSize(const u64 *size, u8 *out)
|
||||
{
|
||||
if (size && out) memcpy(out, size, 6);
|
||||
}
|
||||
|
||||
NX_INLINE void ncaSetDownloadDistributionType(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx || ctx->header.distribution_type == NcaDistributionType_Download) return;
|
||||
|
|
269
source/title.c
269
source/title.c
|
@ -42,6 +42,11 @@ static u32 g_titleInfoCount = 0, g_titleInfoGameCardStartIndex = 0, g_titleInfoG
|
|||
|
||||
/* Function prototypes. */
|
||||
|
||||
NX_INLINE void titleFreeApplicationMetadata(void);
|
||||
NX_INLINE void titleFreeTitleInfo(void);
|
||||
|
||||
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id);
|
||||
|
||||
static bool titleRetrieveApplicationMetadataFromNsRecords(void);
|
||||
static bool titleRetrieveApplicationMetadataByTitleId(u64 title_id, TitleApplicationMetadata *out);
|
||||
|
||||
|
@ -55,13 +60,12 @@ static bool titleOpenNcmDatabaseAndStorageFromGameCard(void);
|
|||
static void titleCloseNcmDatabaseAndStorageFromGameCard(void);
|
||||
|
||||
static bool titleLoadTitleInfo(void);
|
||||
static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id, NcmContentMetaDatabase *ncm_db);
|
||||
static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id);
|
||||
static bool titleGetContentInfosFromTitle(u8 storage_id, const NcmContentMetaKey *meta_key, NcmContentInfo **out_content_infos, u32 *out_content_count);
|
||||
|
||||
static bool _titleRefreshGameCardTitleInfo(bool lock);
|
||||
static void titleRemoveGameCardTitleInfoEntries(void);
|
||||
|
||||
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id);
|
||||
|
||||
bool titleInitialize(void)
|
||||
{
|
||||
mutexLock(&g_titleMutex);
|
||||
|
@ -70,7 +74,7 @@ bool titleInitialize(void)
|
|||
if (ret) goto end;
|
||||
|
||||
/* Allocate memory for the ns application control data. */
|
||||
/* This will be used each time we need to retrieve application metadata. */
|
||||
/* This will be used each time we need to retrieve the metadata from an application. */
|
||||
g_nsAppControlData = calloc(1, sizeof(NsApplicationControlData));
|
||||
if (!g_nsAppControlData)
|
||||
{
|
||||
|
@ -120,7 +124,6 @@ bool titleInitialize(void)
|
|||
|
||||
|
||||
|
||||
|
||||
if (g_titleInfo && g_titleInfoCount)
|
||||
{
|
||||
mkdir("sdmc:/records", 0777);
|
||||
|
@ -133,16 +136,33 @@ bool titleInitialize(void)
|
|||
{
|
||||
for(u32 i = 0; i < g_titleInfoCount; i++)
|
||||
{
|
||||
TitleVersion version;
|
||||
memcpy(&version, &(g_titleInfo[i].meta_key.version), sizeof(u32));
|
||||
|
||||
fprintf(title_infos_txt, "Storage ID: 0x%02X\r\n", g_titleInfo[i].storage_id);
|
||||
fprintf(title_infos_txt, "Title ID: %016lX\r\n", g_titleInfo[i].meta_key.id);
|
||||
fprintf(title_infos_txt, "Version: %u (%u.%u.%u-%u.%u)\r\n", g_titleInfo[i].meta_key.version, version.TitleVersion_Major, version.TitleVersion_Minor, version.TitleVersion_Micro, \
|
||||
version.TitleVersion_MajorRelstep, version.TitleVersion_MinorRelstep);
|
||||
fprintf(title_infos_txt, "Version: %u (%u.%u.%u-%u.%u)\r\n", g_titleInfo[i].meta_key.version, g_titleInfo[i].dot_version.TitleVersion_Major, \
|
||||
g_titleInfo[i].dot_version.TitleVersion_Minor, g_titleInfo[i].dot_version.TitleVersion_Micro, g_titleInfo[i].dot_version.TitleVersion_MajorRelstep, \
|
||||
g_titleInfo[i].dot_version.TitleVersion_MinorRelstep);
|
||||
fprintf(title_infos_txt, "Type: 0x%02X\r\n", g_titleInfo[i].meta_key.type);
|
||||
fprintf(title_infos_txt, "Install Type: 0x%02X\r\n", g_titleInfo[i].meta_key.install_type);
|
||||
fprintf(title_infos_txt, "Title Size: 0x%lX\r\n", g_titleInfo[i].title_size);
|
||||
fprintf(title_infos_txt, "Title Size: %s (0x%lX)\r\n", g_titleInfo[i].title_size_str, g_titleInfo[i].title_size);
|
||||
|
||||
fprintf(title_infos_txt, "Content Count: %u\r\n", g_titleInfo[i].content_count);
|
||||
for(u32 j = 0; j < g_titleInfo[i].content_count; j++)
|
||||
{
|
||||
char content_id_str[SHA256_HASH_SIZE + 1] = {0};
|
||||
utilsGenerateHexStringFromData(content_id_str, sizeof(content_id_str), g_titleInfo[i].content_infos[j].content_id.c, SHA256_HASH_SIZE / 2);
|
||||
|
||||
u64 content_size = 0;
|
||||
titleConvertNcmContentSizeToU64(g_titleInfo[i].content_infos[j].size, &content_size);
|
||||
|
||||
char content_size_str[32] = {0};
|
||||
utilsGenerateFormattedSizeString(content_size, content_size_str, sizeof(content_size_str));
|
||||
|
||||
fprintf(title_infos_txt, " Content #%u:\r\n", j + 1);
|
||||
fprintf(title_infos_txt, " Content ID: %s\r\n", content_id_str);
|
||||
fprintf(title_infos_txt, " Content Size: %s (0x%lX)\r\n", content_size_str, content_size);
|
||||
fprintf(title_infos_txt, " Content Type: 0x%02X\r\n", g_titleInfo[i].content_infos[j].content_type);
|
||||
fprintf(title_infos_txt, " ID Offset: 0x%02X\r\n", g_titleInfo[i].content_infos[j].id_offset);
|
||||
}
|
||||
|
||||
if (g_titleInfo[i].app_metadata)
|
||||
{
|
||||
|
@ -196,26 +216,12 @@ void titleExit(void)
|
|||
{
|
||||
mutexLock(&g_titleMutex);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Free title info. */
|
||||
titleFreeTitleInfo();
|
||||
|
||||
/* Close gamecard ncm database and storage. */
|
||||
titleCloseNcmDatabaseAndStorageFromGameCard();
|
||||
|
||||
/* Free title info. */
|
||||
if (g_titleInfo) free(g_titleInfo);
|
||||
g_titleInfoCount = g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = 0;
|
||||
|
||||
/* Close eMMC System, eMMC User and SD card ncm storages. */
|
||||
titleCloseNcmStorages();
|
||||
|
||||
|
@ -223,8 +229,7 @@ void titleExit(void)
|
|||
titleCloseNcmDatabases();
|
||||
|
||||
/* Free application metadata. */
|
||||
if (g_appMetadata) free(g_appMetadata);
|
||||
g_appMetadataCount = 0;
|
||||
titleFreeApplicationMetadata();
|
||||
|
||||
/* Free ns application control data. */
|
||||
if (g_nsAppControlData) free(g_nsAppControlData);
|
||||
|
@ -289,6 +294,34 @@ bool titleRefreshGameCardTitleInfo(void)
|
|||
return _titleRefreshGameCardTitleInfo(true);
|
||||
}
|
||||
|
||||
TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id)
|
||||
{
|
||||
mutexLock(&g_titleMutex);
|
||||
|
||||
TitleInfo *info = NULL;
|
||||
|
||||
if (!g_titleInfo || !g_titleInfoCount || storage_id < NcmStorageId_GameCard || storage_id > NcmStorageId_Any || !title_id)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < g_titleInfoCount; i++)
|
||||
{
|
||||
if (g_titleInfo[i].meta_key.id == title_id && (storage_id == NcmStorageId_Any || (storage_id != NcmStorageId_Any && g_titleInfo[i].storage_id == storage_id)))
|
||||
{
|
||||
info = &(g_titleInfo[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!info) LOGFILE("Unable to find TitleInfo entry with ID \"%016lX\"! (storage ID %u).", title_id, storage_id);
|
||||
|
||||
end:
|
||||
mutexUnlock(&g_titleMutex);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -304,9 +337,44 @@ bool titleRefreshGameCardTitleInfo(void)
|
|||
|
||||
|
||||
|
||||
NX_INLINE void titleFreeApplicationMetadata(void)
|
||||
{
|
||||
if (g_appMetadata)
|
||||
{
|
||||
free(g_appMetadata);
|
||||
g_appMetadata = NULL;
|
||||
}
|
||||
|
||||
g_appMetadataCount = 0;
|
||||
}
|
||||
|
||||
NX_INLINE void titleFreeTitleInfo(void)
|
||||
{
|
||||
if (g_titleInfo)
|
||||
{
|
||||
for(u32 i = 0; i < g_titleInfoCount; i++)
|
||||
{
|
||||
if (g_titleInfo[i].content_infos) free(g_titleInfo[i].content_infos);
|
||||
}
|
||||
|
||||
free(g_titleInfo);
|
||||
g_titleInfo = NULL;
|
||||
}
|
||||
|
||||
g_titleInfoCount = g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = 0;
|
||||
}
|
||||
|
||||
|
||||
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id)
|
||||
{
|
||||
if (!g_appMetadata || !g_appMetadataCount || !title_id) return NULL;
|
||||
|
||||
for(u32 i = 0; i < g_appMetadataCount; i++)
|
||||
{
|
||||
if (g_appMetadata[i].title_id == title_id) return &(g_appMetadata[i]);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool titleRetrieveApplicationMetadataFromNsRecords(void)
|
||||
{
|
||||
|
@ -585,21 +653,12 @@ static bool titleLoadTitleInfo(void)
|
|||
/* Return right away if title info has already been retrieved. */
|
||||
if (g_titleInfo || g_titleInfoCount) return true;
|
||||
|
||||
NcmContentMetaDatabase *ncm_db = NULL;
|
||||
|
||||
g_titleInfoCount = 0;
|
||||
|
||||
for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++)
|
||||
{
|
||||
/* Retrieve ncm database pointer. */
|
||||
ncm_db = titleGetNcmDatabaseByStorageId(i);
|
||||
if (!ncm_db) continue;
|
||||
|
||||
/* Check if the ncm database handle has already been retrieved. */
|
||||
if (!serviceIsActive(&(ncm_db->s))) continue;
|
||||
|
||||
/* Retrieve content meta keys from this ncm database. */
|
||||
if (!titleRetrieveContentMetaKeysFromDatabase(i, ncm_db))
|
||||
/* Retrieve content meta keys from the current storage. */
|
||||
if (!titleRetrieveContentMetaKeysFromDatabase(i))
|
||||
{
|
||||
LOGFILE("Failed to retrieve content meta keys from storage ID %u!", i);
|
||||
return false;
|
||||
|
@ -609,9 +668,11 @@ static bool titleLoadTitleInfo(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id, NcmContentMetaDatabase *ncm_db)
|
||||
static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id)
|
||||
{
|
||||
if (storage_id < NcmStorageId_GameCard || storage_id > NcmStorageId_SdCard || !ncm_db || !serviceIsActive(&(ncm_db->s)))
|
||||
NcmContentMetaDatabase *ncm_db = NULL;
|
||||
|
||||
if (!(ncm_db = titleGetNcmDatabaseByStorageId(storage_id)) || !serviceIsActive(&(ncm_db->s)))
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -705,10 +766,26 @@ static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id, NcmContentMe
|
|||
{
|
||||
TitleInfo *cur_title_info = &(g_titleInfo[g_titleInfoCount + i]);
|
||||
|
||||
/* Fill information. */
|
||||
cur_title_info->storage_id = storage_id;
|
||||
memcpy(&(cur_title_info->dot_version), &(meta_keys[i].version), sizeof(u32));
|
||||
memcpy(&(cur_title_info->meta_key), &(meta_keys[i]), sizeof(NcmContentMetaKey));
|
||||
/* TO DO: RETRIEVE TITLE SIZE HERE. */
|
||||
cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(meta_keys[i].id);
|
||||
|
||||
/* Retrieve content infos. */
|
||||
if (titleGetContentInfosFromTitle(storage_id, &(meta_keys[i]), &(cur_title_info->content_infos), &(cur_title_info->content_count)))
|
||||
{
|
||||
/* Calculate title size. */
|
||||
for(u32 j = 0; j < cur_title_info->content_count; j++)
|
||||
{
|
||||
u64 tmp_size = 0;
|
||||
titleConvertNcmContentSizeToU64(cur_title_info->content_infos[j].size, &tmp_size);
|
||||
cur_title_info->title_size += tmp_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate formatted title size string. */
|
||||
utilsGenerateFormattedSizeString(cur_title_info->title_size, cur_title_info->title_size_str, sizeof(cur_title_info->title_size_str));
|
||||
}
|
||||
|
||||
/* Update title info count. */
|
||||
|
@ -722,6 +799,82 @@ end:
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool titleGetContentInfosFromTitle(u8 storage_id, const NcmContentMetaKey *meta_key, NcmContentInfo **out_content_infos, u32 *out_content_count)
|
||||
{
|
||||
NcmContentMetaDatabase *ncm_db = NULL;
|
||||
|
||||
if (!(ncm_db = titleGetNcmDatabaseByStorageId(storage_id)) || !serviceIsActive(&(ncm_db->s)) || !meta_key || !out_content_infos || !out_content_count)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Result rc = 0;
|
||||
|
||||
NcmContentMetaHeader content_meta_header = {0};
|
||||
u64 content_meta_header_read_size = 0;
|
||||
|
||||
NcmContentInfo *content_infos = NULL;
|
||||
u32 content_count = 0, written = 0;
|
||||
|
||||
bool success = false;
|
||||
|
||||
/* Retrieve content meta header. */
|
||||
rc = ncmContentMetaDatabaseGet(ncm_db, meta_key, &content_meta_header_read_size, &content_meta_header, sizeof(NcmContentMetaHeader));
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("ncmContentMetaDatabaseGet failed! (0x%08X).", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (content_meta_header_read_size != sizeof(NcmContentMetaHeader))
|
||||
{
|
||||
LOGFILE("Content meta header size mismatch! (0x%lX != 0x%lX).", rc, content_meta_header_read_size, sizeof(NcmContentMetaHeader));
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Get content count. */
|
||||
content_count = (u32)content_meta_header.content_count;
|
||||
if (!content_count)
|
||||
{
|
||||
LOGFILE("Content count is zero!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Allocate memory for the content infos. */
|
||||
content_infos = calloc(content_count, sizeof(NcmContentInfo));
|
||||
if (!content_infos)
|
||||
{
|
||||
LOGFILE("Unable to allocate memory for the content infos buffer! (%u content[s]).", content_count);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Retrieve content infos. */
|
||||
rc = ncmContentMetaDatabaseListContentInfo(ncm_db, (s32*)&written, content_infos, (s32)content_count, meta_key, 0);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("ncmContentMetaDatabaseListContentInfo failed! (0x%08X).", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (written != content_count)
|
||||
{
|
||||
LOGFILE("Content count mismatch! (%u != %u).", written, content_count);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update output. */
|
||||
*out_content_infos = content_infos;
|
||||
*out_content_count = content_count;
|
||||
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (!success && content_infos) free(content_infos);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool _titleRefreshGameCardTitleInfo(bool lock)
|
||||
{
|
||||
if (lock) mutexLock(&g_titleMutex);
|
||||
|
@ -749,7 +902,7 @@ static bool _titleRefreshGameCardTitleInfo(bool lock)
|
|||
g_titleInfoGameCardStartIndex = g_titleInfoCount;
|
||||
|
||||
/* Retrieve content meta keys from the gamecard ncm database. */
|
||||
if (!titleRetrieveContentMetaKeysFromDatabase(NcmStorageId_GameCard, &g_ncmDbGameCard))
|
||||
if (!titleRetrieveContentMetaKeysFromDatabase(NcmStorageId_GameCard))
|
||||
{
|
||||
LOGFILE("Failed to retrieve content meta keys from gamecard!");
|
||||
goto end;
|
||||
|
@ -856,29 +1009,21 @@ static void titleRemoveGameCardTitleInfoEntries(void)
|
|||
|
||||
if (g_titleInfoGameCardCount == g_titleInfoCount)
|
||||
{
|
||||
free(g_titleInfo);
|
||||
g_titleInfo = NULL;
|
||||
titleFreeTitleInfo();
|
||||
} else {
|
||||
for(u32 i = (g_titleInfoCount - g_titleInfoGameCardCount); i < g_titleInfoCount; i++)
|
||||
{
|
||||
if (g_titleInfo[i].content_infos) free(g_titleInfo[i].content_infos);
|
||||
}
|
||||
|
||||
TitleInfo *tmp_title_info = realloc(g_titleInfo, (g_titleInfoCount - g_titleInfoGameCardCount) * sizeof(TitleInfo));
|
||||
if (tmp_title_info)
|
||||
{
|
||||
g_titleInfo = tmp_title_info;
|
||||
tmp_title_info = NULL;
|
||||
}
|
||||
|
||||
g_titleInfoCount = (g_titleInfoCount - g_titleInfoGameCardCount);
|
||||
g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = 0;
|
||||
}
|
||||
|
||||
g_titleInfoCount = (g_titleInfoCount - g_titleInfoGameCardCount);
|
||||
g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = 0;
|
||||
}
|
||||
|
||||
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id)
|
||||
{
|
||||
if (!g_appMetadata || !g_appMetadataCount || !title_id) return NULL;
|
||||
|
||||
for(u32 i = 0; i < g_appMetadataCount; i++)
|
||||
{
|
||||
if (g_appMetadata[i].title_id == title_id) return &(g_appMetadata[i]);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -34,44 +34,76 @@ typedef struct {
|
|||
u32 TitleVersion_Major : 6;
|
||||
} TitleVersion;
|
||||
|
||||
/// Retrieved from ns application records.
|
||||
/// Retrieved using ns application records and/or ncm content meta keys.
|
||||
typedef struct {
|
||||
u64 title_id; ///< Title ID from the application this data belongs to.
|
||||
NacpLanguageEntry lang_entry; ///< UTF-8 strings in the language set in the console settings.
|
||||
NacpLanguageEntry lang_entry; ///< UTF-8 strings in the console language.
|
||||
u32 icon_size; ///< JPEG icon size.
|
||||
u8 icon[0x20000]; ///< JPEG icon data.
|
||||
} TitleApplicationMetadata;
|
||||
|
||||
/// Retrieved from ncm databases.
|
||||
/// Retrieved using ncm databases.
|
||||
typedef struct {
|
||||
u8 storage_id; ///< NcmStorageId.
|
||||
TitleVersion dot_version; ///< Holds the same value from meta_key.version. Used to display version numbers in dot notation (major.minor.micro-major_relstep.minor_relstep).
|
||||
NcmContentMetaKey meta_key; ///< Used with ncm calls.
|
||||
u32 content_count; ///< Content info count.
|
||||
NcmContentInfo *content_infos; ///< Content info entries from this title.
|
||||
u64 title_size; ///< Total title size.
|
||||
TitleApplicationMetadata *app_metadata; ///< Not available for all titles.
|
||||
char title_size_str[32]; ///< Total title size string.
|
||||
TitleApplicationMetadata *app_metadata; ///< Only available for applications.
|
||||
|
||||
|
||||
/* Pointers to patches / AOC? */
|
||||
|
||||
|
||||
} TitleInfo;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Initializes the title interface.
|
||||
bool titleInitialize(void);
|
||||
|
||||
/// Closes the title interface.
|
||||
void titleExit(void);
|
||||
|
||||
/// Returns a pointer to a ncm database handle using a NcmStorageId value.
|
||||
NcmContentMetaDatabase *titleGetNcmDatabaseByStorageId(u8 storage_id);
|
||||
|
||||
/// Returns a pointer to a ncm storage handle using a NcmStorageId value.
|
||||
NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id);
|
||||
|
||||
/// Returns true if gamecard title info could be loaded.
|
||||
/// Suitable for being called between UI updates.
|
||||
bool titleRefreshGameCardTitleInfo(void);
|
||||
|
||||
/// Retrieves a pointer to a TitleInfo entry with a matching storage ID and title ID.
|
||||
/// If NcmStorageId_Any is used, the first entry with a matching title ID is returned.
|
||||
/// Returns NULL if an error occurs.
|
||||
TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Miscellaneous functions.
|
||||
|
||||
NX_INLINE void titleConvertNcmContentSizeToU64(const u8 *size, u64 *out)
|
||||
{
|
||||
if (!size || !out) return;
|
||||
*out = 0;
|
||||
memcpy(out, size, 6);
|
||||
}
|
||||
|
||||
NX_INLINE void titleConvertU64ToNcmContentSize(const u64 *size, u8 *out)
|
||||
{
|
||||
if (size && out) memcpy(out, size, 6);
|
||||
}
|
||||
|
||||
NX_INLINE u64 titleGetPatchIdByApplicationId(u64 app_id)
|
||||
{
|
||||
return (app_id | TITLE_PATCH_ID_MASK);
|
||||
|
@ -92,4 +124,16 @@ NX_INLINE bool titleCheckIfAddOnContentIdBelongsToApplicationId(u64 app_id, u64
|
|||
return ((app_id & TITLE_ADDONCONTENT_ID_MASK) == (aoc_id & TITLE_ADDONCONTENT_ID_MASK));
|
||||
}
|
||||
|
||||
NX_INLINE NcmContentInfo *titleGetContentInfoByTypeAndIdOffset(TitleInfo *info, u8 content_type, u8 id_offset)
|
||||
{
|
||||
if (!info || !info->content_count || !info->content_infos || content_type > NcmContentType_DeltaFragment) return NULL;
|
||||
|
||||
for(u32 i = 0; i < info->content_count; i++)
|
||||
{
|
||||
if (info->content_infos[i].content_type == content_type && info->content_infos[i].id_offset == id_offset) return &(info->content_infos[i]);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* __TITLE_H__ */
|
||||
|
|
|
@ -52,6 +52,9 @@ static AppletHookCookie g_systemOverclockCookie = {0};
|
|||
|
||||
static Mutex g_logfileMutex = 0;
|
||||
|
||||
static const char *g_sizeSuffixes[] = { "B", "KiB", "MiB", "GiB" };
|
||||
static const u32 g_sizeSuffixesCount = MAX_ELEMENTS(g_sizeSuffixes);
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static u64 utilsHidKeysAllDown(void);
|
||||
|
@ -352,6 +355,22 @@ void utilsGenerateHexStringFromData(char *dst, size_t dst_size, const void *src,
|
|||
dst[j] = '\0';
|
||||
}
|
||||
|
||||
void utilsGenerateFormattedSizeString(u64 size, char *dst, size_t dst_size)
|
||||
{
|
||||
if (!dst || dst_size < 2) return;
|
||||
|
||||
double converted_size = (double)size;
|
||||
|
||||
for(u32 i = 0; i < g_sizeSuffixesCount; i++)
|
||||
{
|
||||
if (converted_size >= pow(1024.0, i + 1) && (i + 1) < g_sizeSuffixesCount) continue;
|
||||
|
||||
converted_size /= pow(1024.0, i);
|
||||
snprintf(dst, dst_size, "%.*f %s", (converted_size >= 100.0 ? 0 : (converted_size >= 10.0 ? 1 : 2)), converted_size, g_sizeSuffixes[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool utilsGetFreeSdCardSpace(u64 *out)
|
||||
{
|
||||
return utilsGetFreeFileSystemSpace(g_sdCardFileSystem, out);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <threads.h>
|
||||
|
@ -95,6 +96,8 @@ void utilsTrimString(char *str);
|
|||
|
||||
void utilsGenerateHexStringFromData(char *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
|
||||
void utilsGenerateFormattedSizeString(u64 size, char *dst, size_t dst_size);
|
||||
|
||||
bool utilsGetFreeSdCardSpace(u64 *out);
|
||||
bool utilsGetFreeFileSystemSpace(FsFileSystem *fs, u64 *out);
|
||||
|
||||
|
|
18
todo.txt
18
todo.txt
|
@ -1,5 +1,6 @@
|
|||
todo:
|
||||
|
||||
nca: functions for fs section lookup? (could just let the user choose...)
|
||||
nca: function to write re-encrypted nca headers / nca fs headers (don't forget nca0 please)
|
||||
nca: function to patch the private npdm acid signature from a program nca + patch the acid signature from the nca header
|
||||
|
||||
|
@ -18,17 +19,10 @@ todo:
|
|||
|
||||
bktr: filelist generation functions (wrappers for romfs filelist generation functions)
|
||||
|
||||
title: linked lists for patch / aoc info?
|
||||
title: hardcode names for system titles
|
||||
title: more functions for title lookup (filters, patches / aoc, etc.)
|
||||
title: more functions for content lookup (based on id?)
|
||||
title: find a nice way to deal with orphan content
|
||||
|
||||
|
||||
|
||||
|
||||
char content_info_path[FS_MAX_PATH] = {0};
|
||||
sprintf(content_info_path, "sdmc:/%016lX.bin", xml_program_info.title_id);
|
||||
|
||||
FILE *content_info = fopen(content_info_path, "wb");
|
||||
if (content_info)
|
||||
{
|
||||
fwrite(titleContentInfos, 1, titleContentInfoCnt * sizeof(NcmContentInfo), content_info);
|
||||
fclose(content_info);
|
||||
}
|
||||
|
Loading…
Reference in a new issue