1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-10-18 11:21:59 +01:00
nxdumptool/source/core/nxdt_bfsar.c

213 lines
6.1 KiB
C
Raw Permalink Normal View History

/*
* nxdt_bfsar.c
*
2024-04-12 10:47:36 +01:00
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <core/nxdt_utils.h>
#include <core/nxdt_bfsar.h>
#include <core/romfs.h>
#include <core/title.h>
#define BFSAR_FILENAME "qlaunch.bfsar"
#define BFSAR_ROMFS_PATH "/sound/" BFSAR_FILENAME
/* Global variables. */
static Mutex g_bfsarMutex = 0;
static bool g_bfsarInterfaceInit = false;
static char g_bfsarPath[FS_MAX_PATH] = {0};
bool bfsarInitialize(void)
{
bool use_root = true;
const char *launch_path = utilsGetLaunchPath();
char *ptr1 = NULL, *ptr2 = NULL;
TitleInfo *title_info = NULL;
NcaContext *nca_ctx = NULL;
RomFileSystemContext romfs_ctx = {0};
RomFileSystemFileEntry *romfs_file_entry = NULL;
FILE *bfsar_file = NULL;
u8 *bfsar_data = NULL;
size_t bfsar_size = 0, wr = 0;
bool ret = false;
SCOPED_LOCK(&g_bfsarMutex)
{
ret = g_bfsarInterfaceInit;
if (ret) break;
/* Generate BFSAR file path. */
if (launch_path)
{
ptr1 = strchr(launch_path, '/');
ptr2 = strrchr(launch_path, '/');
if (ptr1 && ptr2 && ptr1 != ptr2)
{
/* Create BFSAR file in the current working directory. */
I'm a terrible person and an even worse developer. And I don't need anyone to tell me so, thank you very much. * PoC: remove gc_dumper and nsp_dumper PoC; create nxdt_rw_poc with all gc_dumper and nsp_dumper capabilities + standalone ticket dumping + raw NCA dumping; use ftruncate() to set output file sizes whenever possible. PoC code is a mess, as always. Expect the features from the rest of the PoCs to be implemented into nxdt_rw_poc soon. * workflow: temporarily disable borealis build generation; comment out manual installation of up-to-date packages from Leseratte's mirrors because the latest devkitA64 Docker image has them all. * borealis: update to fix building issues with latest devkitA64. * bfttf: error out on invalid NCA signatures. * config: save configuration to the current working directory; parse and validate new "gamecard/write_raw_hfs_partition" flag. * defines: remove CONFIG_PATH macro; rename CONFIG_FILE_NAME. * gamecard: rename fs_ctx -> hfs_ctx everywhere; use HFS function calls to retrieve partition names. * hfs: move GameCardHashFileSystemPartitionType enum from gamecard.h and rename it to HashFileSystemPartitionType; add hfsIsValidContext(); add hfsGetPartitionNameString(). * nca/npdm: update comments to reflect latest HOS version. * nxdt_bfsar: always generate absolute SD card paths with the device name; error out on an invalid NCA signature. * nxdt_includes: include dirent.h; refactor Version struct to make it a union of all known *Version structs. * nxdt_log: don't write session separator if the logfile is empty. * nxdt_utils: log appletIsGamePlayRecordingSupported() errors; add utilsDeleteDirectoryRecursively(). * rsa: provide clearer function descriptions in header file. * services: handle usb:ds initialization. * tik: update tikConvertPersonalizedTicketToCommonTicket() to allow NULL input pointers as raw certificate chain arguments (much needed for standalone ticket dumping). * title: add titleGetApplicationIdByMetaKey(). * usb: refactor interface (de)initialization code; slightly improve ABI usage (console-side only); redefine ABI version field in StartSession command blocks; upgrade ABI to v1.1. * FatFs: rename DIR -> FDIR to avoid conflicts with definitions from stdlib's dirent.h. * gamecard_tab: display package ID from the inserted gamecard; fix displayed version numbers from bundled system updates below 3.0.0. * todo: add notes about creating devoptab devices for HFS/PFS/RomFS file tree dumping.
2023-05-24 20:05:34 +01:00
snprintf(g_bfsarPath, sizeof(g_bfsarPath), "%.*s" BFSAR_FILENAME, (int)((ptr2 - launch_path) + 1), launch_path);
use_root = false;
}
}
/* Create BFSAR file in the SD card root directory. */
I'm a terrible person and an even worse developer. And I don't need anyone to tell me so, thank you very much. * PoC: remove gc_dumper and nsp_dumper PoC; create nxdt_rw_poc with all gc_dumper and nsp_dumper capabilities + standalone ticket dumping + raw NCA dumping; use ftruncate() to set output file sizes whenever possible. PoC code is a mess, as always. Expect the features from the rest of the PoCs to be implemented into nxdt_rw_poc soon. * workflow: temporarily disable borealis build generation; comment out manual installation of up-to-date packages from Leseratte's mirrors because the latest devkitA64 Docker image has them all. * borealis: update to fix building issues with latest devkitA64. * bfttf: error out on invalid NCA signatures. * config: save configuration to the current working directory; parse and validate new "gamecard/write_raw_hfs_partition" flag. * defines: remove CONFIG_PATH macro; rename CONFIG_FILE_NAME. * gamecard: rename fs_ctx -> hfs_ctx everywhere; use HFS function calls to retrieve partition names. * hfs: move GameCardHashFileSystemPartitionType enum from gamecard.h and rename it to HashFileSystemPartitionType; add hfsIsValidContext(); add hfsGetPartitionNameString(). * nca/npdm: update comments to reflect latest HOS version. * nxdt_bfsar: always generate absolute SD card paths with the device name; error out on an invalid NCA signature. * nxdt_includes: include dirent.h; refactor Version struct to make it a union of all known *Version structs. * nxdt_log: don't write session separator if the logfile is empty. * nxdt_utils: log appletIsGamePlayRecordingSupported() errors; add utilsDeleteDirectoryRecursively(). * rsa: provide clearer function descriptions in header file. * services: handle usb:ds initialization. * tik: update tikConvertPersonalizedTicketToCommonTicket() to allow NULL input pointers as raw certificate chain arguments (much needed for standalone ticket dumping). * title: add titleGetApplicationIdByMetaKey(). * usb: refactor interface (de)initialization code; slightly improve ABI usage (console-side only); redefine ABI version field in StartSession command blocks; upgrade ABI to v1.1. * FatFs: rename DIR -> FDIR to avoid conflicts with definitions from stdlib's dirent.h. * gamecard_tab: display package ID from the inserted gamecard; fix displayed version numbers from bundled system updates below 3.0.0. * todo: add notes about creating devoptab devices for HFS/PFS/RomFS file tree dumping.
2023-05-24 20:05:34 +01:00
if (use_root) sprintf(g_bfsarPath, DEVOPTAB_SDMC_DEVICE "/" BFSAR_FILENAME);
LOG_MSG_DEBUG("BFSAR path: \"%s\".", g_bfsarPath);
/* Check if the BFSAR file is already available and not empty. */
bfsar_file = fopen(g_bfsarPath, "rb");
if (bfsar_file)
{
fseek(bfsar_file, 0, SEEK_END);
bfsar_size = ftell(bfsar_file);
if (bfsar_size)
{
ret = g_bfsarInterfaceInit = true;
break;
}
}
/* Get title info. */
title: init gc title storage by HFS as fallback Makes it possible to generate TitleInfo and TitleApplicationMetadata elements for gamecards that can't be used with ncm (e.g. Kiosk / Quest gamecards under retail, non-Quest units). This is achieved by: 1. Retrieving a Hash FS context for the gamecard's secure partition. 2. Initializing NCA and CNMT contexts for all of the Meta NCAs within the secure partition. 3. Manually generating NcmContentMetaKey and NcmContentInfo elements per each CNMT context. 4. Creating TitleInfo elements using the generated NcmContentMetaKey and NcmContentInfo elements. 5. Initializing NCA and NACP contexts for the base (or update) Control NCAs within the secure partition. 6. Manually generating a NsApplicationControlData element per each NACP context. 7. Creating TitleApplicationMetadata elements using the generated NsApplicationControlData elements. Afterwards, gamecard title/content enumeration and all other features that rely on it (e.g. NSP, NCA, NCA FS) work as expected. Please note that this process is only carried out if regular title storage initialization fails. Other changes include: * title: reorder code. * title: add TitleGameCardContentMetaContext struct. * title: rename titleGetInfoFromStorageByTitleId() -> titleGetTitleInfoEntryFromStorageByTitleId(). * title: add titleInitializeGameCardTitleStorageByHashFileSystem(). * title: rename titleGenerateDummySystemMetadataEntry() -> titleGetSystemMetadataEntry(). * title: rename titleRetrieveUserApplicationMetadataByTitleId() -> titleGenerateUserMetadataEntryFromNs(). * title: move ns logic from titleRetrieveUserApplicationMetadataByTitleId() into a new function: titleGetApplicationControlDataFromNs(). * title: add titleGenerateUserMetadataEntryFromControlNca(). * title: add titleGetApplicationControlDataFromControlNca(). * title: add titleInitializeUserMetadataEntryFromControlData(). * title: add titleGenerateTitleInfoEntriesByHashFileSystemForGameCardTitleStorage(). * title: move TitleInfo generation logic from titleGenerateTitleInfoEntriesForTitleStorage() into a new function: titleGenerateTitleInfoEntry(). * title: add titleInitializeTitleInfoApplicationMetadataFromControlNca(). * title: add titleGetGameCardContentMetaContexts(). * title: add titleFreeGameCardContentMetaContexts(). * title: add titleGetContentInfosByGameCardContentMetaContext(). * title: rename _titleGetInfoFromStorageByTitleId() -> _titleGetTitleInfoEntryFromStorageByTitleId(). * title: rename titleSystemTitleMetadataEntrySortFunction() -> titleSystemMetadataSortFunction(). * title: rename titleUserApplicationMetadataEntrySortFunction() -> titleUserMetadataSortFunction(). * title: rename titleInfoEntrySortFunction() -> titleInfoSortFunction(). * title: add titleGameCardContentMetaContextSortFunction(). * codebase: update to reflect the rest of the changes.
2024-08-18 14:29:57 +01:00
if (!(title_info = titleGetTitleInfoEntryFromStorageByTitleId(NcmStorageId_BuiltInSystem, QLAUNCH_TID)))
{
LOG_MSG_ERROR("Failed to get title info for qlaunch!");
break;
}
/* Allocate memory for a temporary NCA context. */
nca_ctx = calloc(1, sizeof(NcaContext));
if (!nca_ctx)
{
LOG_MSG_ERROR("Failed to allocate memory for temporary NCA context!");
break;
}
/* Initialize NCA context. */
I'm a terrible person and an even worse developer. And I don't need anyone to tell me so, thank you very much. * PoC: remove gc_dumper and nsp_dumper PoC; create nxdt_rw_poc with all gc_dumper and nsp_dumper capabilities + standalone ticket dumping + raw NCA dumping; use ftruncate() to set output file sizes whenever possible. PoC code is a mess, as always. Expect the features from the rest of the PoCs to be implemented into nxdt_rw_poc soon. * workflow: temporarily disable borealis build generation; comment out manual installation of up-to-date packages from Leseratte's mirrors because the latest devkitA64 Docker image has them all. * borealis: update to fix building issues with latest devkitA64. * bfttf: error out on invalid NCA signatures. * config: save configuration to the current working directory; parse and validate new "gamecard/write_raw_hfs_partition" flag. * defines: remove CONFIG_PATH macro; rename CONFIG_FILE_NAME. * gamecard: rename fs_ctx -> hfs_ctx everywhere; use HFS function calls to retrieve partition names. * hfs: move GameCardHashFileSystemPartitionType enum from gamecard.h and rename it to HashFileSystemPartitionType; add hfsIsValidContext(); add hfsGetPartitionNameString(). * nca/npdm: update comments to reflect latest HOS version. * nxdt_bfsar: always generate absolute SD card paths with the device name; error out on an invalid NCA signature. * nxdt_includes: include dirent.h; refactor Version struct to make it a union of all known *Version structs. * nxdt_log: don't write session separator if the logfile is empty. * nxdt_utils: log appletIsGamePlayRecordingSupported() errors; add utilsDeleteDirectoryRecursively(). * rsa: provide clearer function descriptions in header file. * services: handle usb:ds initialization. * tik: update tikConvertPersonalizedTicketToCommonTicket() to allow NULL input pointers as raw certificate chain arguments (much needed for standalone ticket dumping). * title: add titleGetApplicationIdByMetaKey(). * usb: refactor interface (de)initialization code; slightly improve ABI usage (console-side only); redefine ABI version field in StartSession command blocks; upgrade ABI to v1.1. * FatFs: rename DIR -> FDIR to avoid conflicts with definitions from stdlib's dirent.h. * gamecard_tab: display package ID from the inserted gamecard; fix displayed version numbers from bundled system updates below 3.0.0. * todo: add notes about creating devoptab devices for HFS/PFS/RomFS file tree dumping.
2023-05-24 20:05:34 +01:00
/* Don't allow invalid NCA signatures. */
if (!ncaInitializeContext(nca_ctx, NcmStorageId_BuiltInSystem, 0, &(title_info->meta_key), titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Program, 0), NULL) || \
I'm a terrible person and an even worse developer. And I don't need anyone to tell me so, thank you very much. * PoC: remove gc_dumper and nsp_dumper PoC; create nxdt_rw_poc with all gc_dumper and nsp_dumper capabilities + standalone ticket dumping + raw NCA dumping; use ftruncate() to set output file sizes whenever possible. PoC code is a mess, as always. Expect the features from the rest of the PoCs to be implemented into nxdt_rw_poc soon. * workflow: temporarily disable borealis build generation; comment out manual installation of up-to-date packages from Leseratte's mirrors because the latest devkitA64 Docker image has them all. * borealis: update to fix building issues with latest devkitA64. * bfttf: error out on invalid NCA signatures. * config: save configuration to the current working directory; parse and validate new "gamecard/write_raw_hfs_partition" flag. * defines: remove CONFIG_PATH macro; rename CONFIG_FILE_NAME. * gamecard: rename fs_ctx -> hfs_ctx everywhere; use HFS function calls to retrieve partition names. * hfs: move GameCardHashFileSystemPartitionType enum from gamecard.h and rename it to HashFileSystemPartitionType; add hfsIsValidContext(); add hfsGetPartitionNameString(). * nca/npdm: update comments to reflect latest HOS version. * nxdt_bfsar: always generate absolute SD card paths with the device name; error out on an invalid NCA signature. * nxdt_includes: include dirent.h; refactor Version struct to make it a union of all known *Version structs. * nxdt_log: don't write session separator if the logfile is empty. * nxdt_utils: log appletIsGamePlayRecordingSupported() errors; add utilsDeleteDirectoryRecursively(). * rsa: provide clearer function descriptions in header file. * services: handle usb:ds initialization. * tik: update tikConvertPersonalizedTicketToCommonTicket() to allow NULL input pointers as raw certificate chain arguments (much needed for standalone ticket dumping). * title: add titleGetApplicationIdByMetaKey(). * usb: refactor interface (de)initialization code; slightly improve ABI usage (console-side only); redefine ABI version field in StartSession command blocks; upgrade ABI to v1.1. * FatFs: rename DIR -> FDIR to avoid conflicts with definitions from stdlib's dirent.h. * gamecard_tab: display package ID from the inserted gamecard; fix displayed version numbers from bundled system updates below 3.0.0. * todo: add notes about creating devoptab devices for HFS/PFS/RomFS file tree dumping.
2023-05-24 20:05:34 +01:00
!nca_ctx->valid_main_signature)
{
LOG_MSG_ERROR("Failed to initialize qlaunch Program NCA context!");
break;
}
/* Initialize RomFS context. */
if (!romfsInitializeContext(&romfs_ctx, &(nca_ctx->fs_ctx[1]), NULL))
{
LOG_MSG_ERROR("Failed to initialize RomFS context for qlaunch Program NCA!");
break;
}
/* Get RomFS file entry. */
if (!(romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, BFSAR_ROMFS_PATH)))
{
LOG_MSG_ERROR("Failed to retrieve RomFS file entry for \"" BFSAR_ROMFS_PATH "\"!");
break;
}
/* Check file size. */
bfsar_size = romfs_file_entry->size;
if (!bfsar_size)
{
LOG_MSG_ERROR("File size for qlaunch's \"" BFSAR_ROMFS_PATH "\" is zero!");
break;
}
/* Allocate memory for BFSAR data. */
if (!(bfsar_data = malloc(bfsar_size)))
{
LOG_MSG_ERROR("Failed to allocate 0x%lX bytes for qlaunch's \"" BFSAR_ROMFS_PATH "\"!", bfsar_size);
break;
}
/* Read BFSAR data. */
if (!romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, bfsar_data, bfsar_size, 0))
{
LOG_MSG_ERROR("Failed to read 0x%lX bytes long \"" BFSAR_ROMFS_PATH "\" from qlaunch!", bfsar_size);
break;
}
/* Create BFSAR file. */
bfsar_file = fopen(g_bfsarPath, "wb");
if (!bfsar_file)
{
LOG_MSG_ERROR("Failed to open \"%s\" for writing!", g_bfsarPath);
break;
}
/* Write BFSAR data. */
wr = fwrite(bfsar_data, 1, bfsar_size, bfsar_file);
if (wr != bfsar_size)
{
LOG_MSG_ERROR("Failed to write 0x%lX bytes block to \"%s\"!", bfsar_size, g_bfsarPath);
break;
}
/* Update flags. */
ret = g_bfsarInterfaceInit = true;
}
if (bfsar_file)
{
fclose(bfsar_file);
/* Commit SD card filesystem changes. */
utilsCommitSdCardFileSystemChanges();
}
if (bfsar_data) free(bfsar_data);
romfsFreeContext(&romfs_ctx);
if (nca_ctx) free(nca_ctx);
titleFreeTitleInfo(&title_info);
return ret;
}
void bfsarExit(void)
{
SCOPED_LOCK(&g_bfsarMutex)
{
/* Clear BFSAR file path. */
*g_bfsarPath = '\0';
g_bfsarInterfaceInit = false;
}
}
const char *bfsarGetFilePath(void)
{
const char *ret = NULL;
SCOPED_TRY_LOCK(&g_bfsarMutex)
{
if (g_bfsarInterfaceInit) ret = (const char*)g_bfsarPath;
}
return ret;
}