2020-07-25 06:56:35 +01:00
/*
* title . c
*
2023-04-08 12:42:22 +01:00
* Copyright ( c ) 2020 - 2023 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-07-25 06:56:35 +01:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 19:26:58 +00:00
* 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 .
2020-07-25 06:56:35 +01:00
*
2021-03-25 19:26:58 +00:00
* 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 .
2020-07-25 06:56:35 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-07-25 06:56:35 +01:00
*/
2021-03-26 04:35:14 +00:00
# include "nxdt_utils.h"
2020-07-25 06:56:35 +01:00
# include "title.h"
# include "gamecard.h"
2021-06-05 22:06:29 +01:00
# include "nacp.h"
2020-07-25 06:56:35 +01:00
2021-07-19 22:09:58 +01:00
# define NS_APPLICATION_RECORD_BLOCK_SIZE 1024
2021-06-01 02:12:15 +01:00
2021-07-19 22:09:58 +01:00
# define TITLE_STORAGE_COUNT 4 /* GameCard, BuiltInSystem, BuiltInUser, SdCard. */
# define TITLE_STORAGE_INDEX(storage_id) ((storage_id) - NcmStorageId_GameCard)
2020-07-25 06:56:35 +01:00
2023-07-21 22:56:50 +01:00
# define NCM_CMT_APP_OFFSET 0x7A
2020-07-26 09:00:54 +01:00
/* Type definitions. */
typedef struct {
u64 title_id ;
char name [ 32 ] ;
2021-04-20 23:43:37 +01:00
} TitleSystemEntry ;
2020-07-26 09:00:54 +01:00
2021-06-01 02:12:15 +01:00
typedef struct {
u8 storage_id ; ///< NcmStorageId.
NcmContentMetaDatabase ncm_db ;
NcmContentStorage ncm_storage ;
TitleInfo * * titles ;
u32 title_count ;
} TitleStorage ;
2020-07-25 06:56:35 +01:00
/* Global variables. */
static Mutex g_titleMutex = 0 ;
2020-07-30 22:43:50 +01:00
2021-06-01 02:12:15 +01:00
static Thread g_titleGameCardInfoThread = { 0 } ;
static UEvent g_titleGameCardInfoThreadExitEvent = { 0 } , * g_titleGameCardStatusChangeUserEvent = NULL ;
2020-07-30 22:43:50 +01:00
static bool g_titleInterfaceInit = false , g_titleGameCardInfoThreadCreated = false , g_titleGameCardAvailable = false , g_titleGameCardInfoUpdated = false ;
2020-07-25 06:56:35 +01:00
static NsApplicationControlData * g_nsAppControlData = NULL ;
2021-06-01 02:12:15 +01:00
static TitleApplicationMetadata * * g_systemMetadata = NULL , * * g_userMetadata = NULL ;
static u32 g_systemMetadataCount = 0 , g_userMetadataCount = 0 ;
2020-07-25 06:56:35 +01:00
2021-06-01 02:12:15 +01:00
static TitleStorage g_titleStorage [ TITLE_STORAGE_COUNT ] = { 0 } ;
2021-03-10 13:10:43 +00:00
static TitleInfo * * g_orphanTitleInfo = NULL ;
static u32 g_orphanTitleInfoCount = 0 ;
2020-07-25 06:56:35 +01:00
2022-07-12 01:27:03 +01:00
static const char * g_titleNcmStorageIdNames [ ] = {
[ NcmStorageId_None ] = " None " ,
[ NcmStorageId_Host ] = " Host " ,
[ NcmStorageId_GameCard ] = " Gamecard " ,
[ NcmStorageId_BuiltInSystem ] = " eMMC (system) " ,
[ NcmStorageId_BuiltInUser ] = " eMMC (user) " ,
[ NcmStorageId_SdCard ] = " SD card " ,
[ NcmStorageId_Any ] = " Any "
} ;
2020-07-29 22:02:21 +01:00
static const char * g_titleNcmContentTypeNames [ ] = {
[ NcmContentType_Meta ] = " Meta " ,
[ NcmContentType_Program ] = " Program " ,
[ NcmContentType_Data ] = " Data " ,
[ NcmContentType_Control ] = " Control " ,
[ NcmContentType_HtmlDocument ] = " HtmlDocument " ,
[ NcmContentType_LegalInformation ] = " LegalInformation " ,
2020-10-03 18:09:29 +01:00
[ NcmContentType_DeltaFragment ] = " DeltaFragment "
2020-07-29 22:02:21 +01:00
} ;
2020-08-14 03:31:02 +01:00
static const char * g_titleNcmContentMetaTypeNames [ ] = {
2023-07-21 22:56:50 +01:00
[ NcmContentMetaType_Unknown ] = " Unknown " ,
[ NcmContentMetaType_SystemProgram ] = " SystemProgram " ,
[ NcmContentMetaType_SystemData ] = " SystemData " ,
[ NcmContentMetaType_SystemUpdate ] = " SystemUpdate " ,
[ NcmContentMetaType_BootImagePackage ] = " BootImagePackage " ,
[ NcmContentMetaType_BootImagePackageSafe ] = " BootImagePackageSafe " ,
[ NcmContentMetaType_Application - NCM_CMT_APP_OFFSET ] = " Application " ,
[ NcmContentMetaType_Patch - NCM_CMT_APP_OFFSET ] = " Patch " ,
[ NcmContentMetaType_AddOnContent - NCM_CMT_APP_OFFSET ] = " AddOnContent " ,
[ NcmContentMetaType_Delta - NCM_CMT_APP_OFFSET ] = " Delta " ,
[ NcmContentMetaType_DataPatch - NCM_CMT_APP_OFFSET ] = " DataPatch "
2020-08-14 03:31:02 +01:00
} ;
2020-10-22 05:38:14 +01:00
static const char * g_filenameTypeStrings [ ] = {
2023-07-21 22:56:50 +01:00
[ NcmContentMetaType_Unknown ] = " UNK " ,
[ NcmContentMetaType_SystemProgram ] = " SYSPRG " ,
[ NcmContentMetaType_SystemData ] = " SYSDAT " ,
[ NcmContentMetaType_SystemUpdate ] = " SYSUPD " ,
[ NcmContentMetaType_BootImagePackage ] = " BIP " ,
[ NcmContentMetaType_BootImagePackageSafe ] = " BIPS " ,
[ NcmContentMetaType_Application - NCM_CMT_APP_OFFSET ] = " BASE " ,
[ NcmContentMetaType_Patch - NCM_CMT_APP_OFFSET ] = " UPD " ,
[ NcmContentMetaType_AddOnContent - NCM_CMT_APP_OFFSET ] = " DLC " ,
[ NcmContentMetaType_Delta - NCM_CMT_APP_OFFSET ] = " DELTA " ,
[ NcmContentMetaType_DataPatch - NCM_CMT_APP_OFFSET ] = " DLCUPD "
2020-10-22 05:38:14 +01:00
} ;
2020-07-26 09:00:54 +01:00
/* Info retrieved from https://switchbrew.org/wiki/Title_list. */
/* Titles bundled with the kernel are excluded. */
2021-04-20 23:43:37 +01:00
static const TitleSystemEntry g_systemTitles [ ] = {
2020-07-26 09:00:54 +01:00
/* System modules. */
/* Meta + Program NCAs. */
2021-06-18 06:34:02 +01:00
{ 0x0100000000000000 , " fs " } , ///< Unused, bundled with kernel.
{ 0x0100000000000001 , " ldr " } , ///< Unused, bundled with kernel.
{ 0x0100000000000002 , " ncm " } , ///< Unused, bundled with kernel.
{ 0x0100000000000003 , " pm " } , ///< Unused, bundled with kernel.
{ 0x0100000000000004 , " sm " } , ///< Unused, bundled with kernel.
{ 0x0100000000000005 , " boot " } , ///< Unused, bundled with kernel.
2020-07-26 09:00:54 +01:00
{ 0x0100000000000006 , " usb " } ,
2021-06-18 06:34:02 +01:00
{ 0x0100000000000007 , " htc " } ,
2020-07-26 09:00:54 +01:00
{ 0x0100000000000008 , " boot2 " } ,
{ 0x0100000000000009 , " settings " } ,
2023-03-29 22:14:21 +01:00
{ 0x010000000000000A , " Bus " } ,
2020-07-26 09:00:54 +01:00
{ 0x010000000000000B , " bluetooth " } ,
{ 0x010000000000000C , " bcat " } ,
{ 0x010000000000000D , " dmnt " } ,
{ 0x010000000000000E , " friends " } ,
{ 0x010000000000000F , " nifm " } ,
{ 0x0100000000000010 , " ptm " } ,
{ 0x0100000000000011 , " shell " } ,
{ 0x0100000000000012 , " bsdsockets " } ,
{ 0x0100000000000013 , " hid " } ,
{ 0x0100000000000014 , " audio " } ,
{ 0x0100000000000015 , " LogManager " } ,
{ 0x0100000000000016 , " wlan " } ,
{ 0x0100000000000017 , " cs " } ,
{ 0x0100000000000018 , " ldn " } ,
{ 0x0100000000000019 , " nvservices " } ,
{ 0x010000000000001A , " pcv " } ,
2020-12-04 06:38:44 +00:00
{ 0x010000000000001B , " capmtp " } ,
2020-07-26 09:00:54 +01:00
{ 0x010000000000001C , " nvnflinger " } ,
{ 0x010000000000001D , " pcie " } ,
{ 0x010000000000001E , " account " } ,
{ 0x010000000000001F , " ns " } ,
{ 0x0100000000000020 , " nfc " } ,
{ 0x0100000000000021 , " psc " } ,
{ 0x0100000000000022 , " capsrv " } ,
{ 0x0100000000000023 , " am " } ,
{ 0x0100000000000024 , " ssl " } ,
{ 0x0100000000000025 , " nim " } ,
{ 0x0100000000000026 , " cec " } ,
{ 0x0100000000000027 , " tspm " } ,
2021-06-18 06:34:02 +01:00
{ 0x0100000000000028 , " spl " } , ///< Unused, bundled with kernel.
2020-07-26 09:00:54 +01:00
{ 0x0100000000000029 , " lbl " } ,
{ 0x010000000000002A , " btm " } ,
{ 0x010000000000002B , " erpt " } ,
{ 0x010000000000002C , " time " } ,
{ 0x010000000000002D , " vi " } ,
{ 0x010000000000002E , " pctl " } ,
{ 0x010000000000002F , " npns " } ,
{ 0x0100000000000030 , " eupld " } ,
{ 0x0100000000000031 , " glue " } ,
{ 0x0100000000000032 , " eclct " } ,
{ 0x0100000000000033 , " es " } ,
{ 0x0100000000000034 , " fatal " } ,
{ 0x0100000000000035 , " grc " } ,
{ 0x0100000000000036 , " creport " } ,
{ 0x0100000000000037 , " ro " } ,
{ 0x0100000000000038 , " profiler " } ,
{ 0x0100000000000039 , " sdb " } ,
{ 0x010000000000003A , " migration " } ,
{ 0x010000000000003B , " jit " } ,
{ 0x010000000000003C , " jpegdec " } ,
{ 0x010000000000003D , " safemode " } ,
{ 0x010000000000003E , " olsc " } ,
{ 0x010000000000003F , " dt " } ,
{ 0x0100000000000040 , " nd " } ,
{ 0x0100000000000041 , " ngct " } ,
{ 0x0100000000000042 , " pgl " } ,
2023-03-29 22:14:21 +01:00
{ 0x0100000000000043 , " sys_applet_unknown_00 " } , ///< Placeholder.
{ 0x0100000000000044 , " sys_applet_unknown_01 " } , ///< Placeholder.
2022-03-22 01:20:09 +00:00
{ 0x0100000000000045 , " omm " } ,
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
{ 0x0100000000000046 , " eth " } ,
2023-03-29 22:14:21 +01:00
{ 0x0100000000000047 , " sys_applet_unknown_02 " } , ///< Placeholder.
{ 0x0100000000000048 , " sys_applet_unknown_03 " } , ///< Placeholder.
{ 0x0100000000000049 , " sys_applet_unknown_04 " } , ///< Placeholder.
{ 0x010000000000004A , " sys_applet_unknown_05 " } , ///< Placeholder.
{ 0x010000000000004B , " sys_applet_unknown_06 " } , ///< Placeholder.
{ 0x010000000000004C , " sys_applet_unknown_07 " } , ///< Placeholder.
{ 0x010000000000004D , " sys_applet_unknown_08 " } , ///< Placeholder.
{ 0x010000000000004E , " sys_applet_unknown_09 " } , ///< Placeholder.
{ 0x010000000000004F , " sys_applet_unknown_0a " } , ///< Placeholder.
{ 0x0100000000000050 , " ngc " } ,
2022-07-05 02:04:28 +01:00
2020-07-26 09:00:54 +01:00
/* System data archives. */
/* Meta + Data NCAs. */
{ 0x0100000000000800 , " CertStore " } ,
{ 0x0100000000000801 , " ErrorMessage " } ,
{ 0x0100000000000802 , " MiiModel " } ,
{ 0x0100000000000803 , " BrowserDll " } ,
{ 0x0100000000000804 , " Help " } ,
{ 0x0100000000000805 , " SharedFont " } ,
{ 0x0100000000000806 , " NgWord " } ,
{ 0x0100000000000807 , " SsidList " } ,
{ 0x0100000000000808 , " Dictionary " } ,
{ 0x0100000000000809 , " SystemVersion " } ,
{ 0x010000000000080A , " AvatarImage " } ,
{ 0x010000000000080B , " LocalNews " } ,
{ 0x010000000000080C , " Eula " } ,
{ 0x010000000000080D , " UrlBlackList " } ,
{ 0x010000000000080E , " TimeZoneBinary " } ,
{ 0x010000000000080F , " CertStoreCruiser " } ,
{ 0x0100000000000810 , " FontNintendoExtension " } ,
{ 0x0100000000000811 , " FontStandard " } ,
{ 0x0100000000000812 , " FontKorean " } ,
{ 0x0100000000000813 , " FontChineseTraditional " } ,
{ 0x0100000000000814 , " FontChineseSimple " } ,
{ 0x0100000000000815 , " FontBfcpx " } ,
{ 0x0100000000000816 , " SystemUpdate " } ,
{ 0x0100000000000818 , " FirmwareDebugSettings " } ,
{ 0x0100000000000819 , " BootImagePackage " } ,
{ 0x010000000000081A , " BootImagePackageSafe " } ,
{ 0x010000000000081B , " BootImagePackageExFat " } ,
{ 0x010000000000081C , " BootImagePackageExFatSafe " } ,
{ 0x010000000000081D , " FatalMessage " } ,
{ 0x010000000000081E , " ControllerIcon " } ,
{ 0x010000000000081F , " PlatformConfigIcosa " } ,
{ 0x0100000000000820 , " PlatformConfigCopper " } ,
{ 0x0100000000000821 , " PlatformConfigHoag " } ,
{ 0x0100000000000822 , " ControllerFirmware " } ,
{ 0x0100000000000823 , " NgWord2 " } ,
{ 0x0100000000000824 , " PlatformConfigIcosaMariko " } ,
{ 0x0100000000000825 , " ApplicationBlackList " } ,
{ 0x0100000000000826 , " RebootlessSystemUpdateVersion " } ,
{ 0x0100000000000827 , " ContentActionTable " } ,
{ 0x0100000000000828 , " FunctionBlackList " } ,
2021-04-20 23:43:37 +01:00
{ 0x0100000000000829 , " PlatformConfigCalcio " } ,
2020-07-26 09:00:54 +01:00
{ 0x0100000000000830 , " NgWordT " } ,
2021-04-20 23:43:37 +01:00
{ 0x0100000000000831 , " PlatformConfigAula " } ,
2021-06-18 06:34:02 +01:00
{ 0x0100000000000832 , " CradleFirmwareAula " } , ///< Placeholder.
2023-03-29 22:14:21 +01:00
{ 0x0100000000000835 , " NewErrorMessage " } , ///< Placeholder.
2022-07-05 02:04:28 +01:00
2020-07-26 09:00:54 +01:00
/* System applets. */
/* Meta + Program NCAs. */
{ 0x0100000000001000 , " qlaunch " } ,
{ 0x0100000000001001 , " auth " } ,
{ 0x0100000000001002 , " cabinet " } ,
{ 0x0100000000001003 , " controller " } ,
{ 0x0100000000001004 , " dataErase " } ,
{ 0x0100000000001005 , " error " } ,
{ 0x0100000000001006 , " netConnect " } ,
{ 0x0100000000001007 , " playerSelect " } ,
{ 0x0100000000001008 , " swkbd " } ,
{ 0x0100000000001009 , " miiEdit " } ,
2022-12-04 10:29:47 +00:00
{ 0x010000000000100A , " LibAppletWeb " } ,
{ 0x010000000000100B , " LibAppletShop " } ,
2020-07-26 09:00:54 +01:00
{ 0x010000000000100C , " overlayDisp " } ,
{ 0x010000000000100D , " photoViewer " } ,
{ 0x010000000000100E , " set " } ,
2022-12-04 10:29:47 +00:00
{ 0x010000000000100F , " LibAppletOff " } ,
{ 0x0100000000001010 , " LibAppletLns " } ,
{ 0x0100000000001011 , " LibAppletAuth " } ,
2020-07-26 09:00:54 +01:00
{ 0x0100000000001012 , " starter " } ,
{ 0x0100000000001013 , " myPage " } ,
{ 0x0100000000001014 , " PlayReport " } ,
2023-03-29 22:14:21 +01:00
{ 0x0100000000001015 , " maintenance " } ,
2021-06-18 06:34:02 +01:00
{ 0x0100000000001016 , " application_install " } , ///< Placeholder.
2022-06-24 01:22:01 +01:00
{ 0x0100000000001017 , " nn.am.SystemReportTask " } , ///< Placeholder.
2021-06-18 06:34:02 +01:00
{ 0x0100000000001018 , " systemupdate_dl_throughput " } , ///< Placeholder.
{ 0x0100000000001019 , " volume_update " } , ///< Placeholder.
2020-07-26 09:00:54 +01:00
{ 0x010000000000101A , " gift " } ,
{ 0x010000000000101B , " DummyECApplet " } ,
{ 0x010000000000101C , " userMigration " } ,
{ 0x010000000000101D , " EncounterSys " } ,
2023-03-29 22:14:21 +01:00
{ 0x010000000000101E , " nim_unknown_00 " } , ///< Placeholder.
{ 0x010000000000101F , " nim_glue_unknown_00 " } , ///< Placeholder.
2020-07-26 09:00:54 +01:00
{ 0x0100000000001020 , " story " } ,
2021-06-18 06:34:02 +01:00
{ 0x0100000000001021 , " systemupdate_pass " } , ///< Placeholder.
2023-03-29 22:14:21 +01:00
{ 0x0100000000001023 , " statistics " } , ///< Placeholder.
{ 0x0100000000001024 , " syslog " } , ///< Placeholder.
{ 0x0100000000001025 , " am_unknown_00 " } , ///< Placeholder.
{ 0x0100000000001026 , " olsc_unknown_00 " } , ///< Placeholder.
{ 0x0100000000001027 , " account_unknown_00 " } , ///< Placeholder.
{ 0x0100000000001028 , " ns_unknown_00 " } , ///< Placeholder.
{ 0x0100000000001029 , " request_count " } , ///< Placeholder.
{ 0x010000000000102A , " am_unknown_01 " } , ///< Placeholder.
{ 0x010000000000102B , " glue_unknown_00 " } , ///< Placeholder.
{ 0x010000000000102C , " am_unknown_02 " } , ///< Placeholder.
{ 0x010000000000102E , " blacklist " } , ///< Placeholder.
{ 0x010000000000102F , " content_delivery " } , ///< Placeholder.
{ 0x0100000000001030 , " npns_create_token " } , ///< Placeholder.
{ 0x0100000000001031 , " ns_unknown_01 " } , ///< Placeholder.
{ 0x0100000000001032 , " glue_unknown_01 " } , ///< Placeholder.
{ 0x0100000000001033 , " promotion " } , ///< Placeholder.
{ 0x0100000000001034 , " ngct_bcat_unknown_00 " } , ///< Placeholder.
{ 0x0100000000001037 , " nim_unknown_01 " } , ///< Placeholder.
2020-07-26 09:00:54 +01:00
{ 0x0100000000001038 , " sample " } ,
2022-06-24 01:22:01 +01:00
{ 0x010000000000103C , " mnpp " } , ///< Placeholder.
2023-03-29 22:14:21 +01:00
{ 0x010000000000103D , " bsdsocket_setting " } , ///< Placeholder.
{ 0x010000000000103E , " ntf_mission_completed " } , ///< Placeholder.
{ 0x0100000000001042 , " am_unknown_03 " } , ///< Placeholder.
{ 0x0100000000001043 , " am_unknown_04 " } , ///< Placeholder.
2020-07-26 09:00:54 +01:00
{ 0x0100000000001FFF , " EndOceanProgramId " } ,
2022-07-05 02:04:28 +01:00
2021-06-18 06:34:02 +01:00
/* Development system applets. */
2020-07-26 09:00:54 +01:00
{ 0x0100000000002000 , " A2BoardFunction " } ,
{ 0x0100000000002001 , " A3Wireless " } ,
{ 0x0100000000002002 , " C1LcdAndKey " } ,
{ 0x0100000000002003 , " C2UsbHpmic " } ,
{ 0x0100000000002004 , " C3Aging " } ,
{ 0x0100000000002005 , " C4SixAxis " } ,
{ 0x0100000000002006 , " C5Wireless " } ,
{ 0x0100000000002007 , " C7FinalCheck " } ,
{ 0x010000000000203F , " AutoCapture " } ,
{ 0x0100000000002040 , " DevMenuCommandSystem " } ,
{ 0x0100000000002041 , " recovery " } ,
{ 0x0100000000002042 , " DevMenuSystem " } ,
{ 0x0100000000002044 , " HB-TBIntegrationTest " } ,
{ 0x010000000000204D , " BackupSaveData " } ,
{ 0x010000000000204E , " A4BoardCalWriti " } ,
{ 0x0100000000002054 , " RepairSslCertificate " } ,
{ 0x0100000000002055 , " GameCardWriter " } ,
{ 0x0100000000002056 , " UsbPdTestTool " } ,
{ 0x0100000000002057 , " RepairDeletePctl " } ,
{ 0x0100000000002058 , " RepairBackup " } ,
{ 0x0100000000002059 , " RepairRestore " } ,
{ 0x010000000000205A , " RepairAccountTransfer " } ,
{ 0x010000000000205B , " RepairAutoNetworkUpdater " } ,
{ 0x010000000000205C , " RefurbishReset " } ,
{ 0x010000000000205D , " RepairAssistCup " } ,
{ 0x010000000000205E , " RepairPairingCutter " } ,
{ 0x0100000000002064 , " DevMenu " } ,
{ 0x0100000000002065 , " DevMenuApp " } ,
{ 0x0100000000002066 , " GetGameCardAsicInfo " } ,
{ 0x0100000000002068 , " NfpDebugToolSystem " } ,
{ 0x0100000000002069 , " AlbumSynchronizer " } ,
{ 0x0100000000002071 , " SnapShotDumper " } ,
{ 0x0100000000002073 , " DevMenuSystemApp " } ,
{ 0x0100000000002099 , " DevOverlayDisp " } ,
{ 0x010000000000209A , " NandVerifier " } ,
{ 0x010000000000209B , " GpuCoreDumper " } ,
{ 0x010000000000209C , " TestApplication " } ,
{ 0x010000000000209E , " HelloWorld " } ,
{ 0x01000000000020A0 , " XcieWriter " } ,
{ 0x01000000000020A1 , " GpuOverrunNotifier " } ,
{ 0x01000000000020C8 , " NfpDebugTool " } ,
{ 0x01000000000020CA , " NoftWriter " } ,
{ 0x01000000000020D0 , " BcatSystemDebugTool " } ,
{ 0x01000000000020D1 , " DevSafeModeUpdater " } ,
{ 0x01000000000020D3 , " ControllerConnectionAnalyzer " } ,
{ 0x01000000000020D4 , " DevKitUpdater " } ,
{ 0x01000000000020D6 , " RepairTimeReviser " } ,
{ 0x01000000000020D7 , " RepairReinitializeFuelGauge " } ,
{ 0x01000000000020DA , " RepairAbortMigration " } ,
{ 0x01000000000020DC , " RepairShowDeviceId " } ,
{ 0x01000000000020DD , " RepairSetCycleCountReliability " } ,
{ 0x01000000000020E0 , " Interface " } ,
{ 0x01000000000020E1 , " AlbumDownloader " } ,
{ 0x01000000000020E3 , " FuelGaugeDumper " } ,
{ 0x01000000000020E4 , " UnsafeExtract " } ,
{ 0x01000000000020E5 , " UnsafeEngrave " } ,
{ 0x01000000000020EE , " BluetoothSettingTool " } ,
{ 0x01000000000020F0 , " ApplicationInstallerRomfs " } ,
{ 0x0100000000002100 , " DevMenuLotcheckDownloader " } ,
{ 0x0100000000002101 , " DevMenuCommand " } ,
{ 0x0100000000002102 , " ExportPartition " } ,
{ 0x0100000000002103 , " SystemInitializer " } ,
{ 0x0100000000002104 , " SystemUpdaterHostFs " } ,
{ 0x0100000000002105 , " WriteToStorage " } ,
{ 0x0100000000002106 , " CalWriter " } ,
{ 0x0100000000002107 , " SettingsManager " } ,
{ 0x0100000000002109 , " testBuildSystemIris " } ,
{ 0x010000000000210A , " SystemUpdater " } ,
{ 0x010000000000210B , " nvnflinger_util " } ,
{ 0x010000000000210C , " ControllerFirmwareUpdater " } ,
{ 0x010000000000210D , " testBuildSystemNintendoWare " } ,
{ 0x0100000000002110 , " TestSaveDataCreator " } ,
{ 0x0100000000002111 , " C9LcdSpker " } ,
{ 0x0100000000002114 , " RankTurn " } ,
{ 0x0100000000002116 , " BleTestTool " } ,
{ 0x010000000000211A , " PreinstallAppWriter " } ,
{ 0x010000000000211C , " ControllerSerialFlashTool " } ,
{ 0x010000000000211D , " ControllerFlashWriter " } ,
2023-09-03 09:57:20 +01:00
{ 0x010000000000211E , " C13Handling " } ,
{ 0x010000000000211F , " HidTest " } ,
2020-07-26 09:00:54 +01:00
{ 0x0100000000002120 , " ControllerTestApp " } ,
{ 0x0100000000002121 , " HidInspectionTool " } ,
{ 0x0100000000002124 , " BatteryCyclesEditor " } ,
{ 0x0100000000002125 , " UsbFirmwareUpdater " } ,
{ 0x0100000000002126 , " PalmaSerialCodeTool " } ,
{ 0x0100000000002127 , " renderdoccmd " } ,
{ 0x0100000000002128 , " HidInspectionToolProd " } ,
{ 0x010000000000212C , " ExhibitionMenu " } ,
{ 0x010000000000212F , " ExhibitionSaveData " } ,
{ 0x0100000000002130 , " LuciaConverter " } ,
{ 0x0100000000002133 , " CalDumper " } ,
{ 0x0100000000002134 , " AnalogStickEvaluationTool " } ,
2023-09-03 09:57:20 +01:00
{ 0x010000000000216A , " ButtonTest " } ,
2021-06-18 06:34:02 +01:00
{ 0x010000000000216D , " ExhibitionSaveDataSnapshot " } , ///< Placeholder.
2023-09-03 09:57:20 +01:00
{ 0x010000000000216E , " HandlingA " } ,
2021-06-18 06:34:02 +01:00
{ 0x0100000000002178 , " SecureStartupSettings " } , ///< Placeholder.
2023-09-03 09:57:20 +01:00
{ 0x010000000000217A , " WirelessInterference " } ,
2023-03-29 22:14:21 +01:00
{ 0x010000000000217D , " CradleFirmwareUpdater " } ,
{ 0x0100000000002184 , " HttpInstallSettings " } , ///< Placeholder.
{ 0x0100000000002187 , " ExhibitionMovieAssetData " } , ///< Placeholder.
{ 0x0100000000002191 , " ExhibitionPlayData " } , ///< Placeholder.
2022-07-05 02:04:28 +01:00
2021-06-18 06:34:02 +01:00
/* Debug system modules. */
2020-07-26 09:00:54 +01:00
{ 0x0100000000003002 , " DummyProcess " } ,
{ 0x0100000000003003 , " DebugMonitor0 " } ,
{ 0x0100000000003004 , " SystemHelloWorld " } ,
2022-07-05 02:04:28 +01:00
2021-06-18 06:34:02 +01:00
/* Development system modules. */
2020-07-26 09:00:54 +01:00
{ 0x010000000000B120 , " nvdbgsvc " } ,
2023-03-29 22:14:21 +01:00
{ 0x010000000000B123 , " acc:CORNX " } ,
2020-07-26 09:00:54 +01:00
{ 0x010000000000B14A , " manu " } ,
{ 0x010000000000B14B , " ManuUsbLoopBack " } ,
{ 0x010000000000B1B8 , " DevFwdbgHbPackage " } ,
{ 0x010000000000B1B9 , " DevFwdbgUsbPackage " } ,
{ 0x010000000000B1BA , " ProdFwdbgPackage " } ,
{ 0x010000000000B22A , " scs " } ,
{ 0x010000000000B22B , " ControllerFirmwareDebug " } ,
2021-06-03 05:00:16 +01:00
{ 0x010000000000B240 , " htc " } ,
2022-07-05 02:04:28 +01:00
2021-06-18 06:34:02 +01:00
/* Bdk system modules. */
2020-07-26 09:00:54 +01:00
{ 0x010000000000C600 , " BdkSample01 " } ,
{ 0x010000000000C601 , " BdkSample02 " } ,
{ 0x010000000000C602 , " BdkSample03 " } ,
{ 0x010000000000C603 , " BdkSample04 " } ,
2022-07-05 02:04:28 +01:00
2023-03-29 22:14:21 +01:00
/* New development system modules. */
2020-07-26 09:00:54 +01:00
{ 0x010000000000D609 , " dmnt.gen2 " } ,
2023-03-29 22:14:21 +01:00
{ 0x010000000000D60A , " msm_unknown_00 " } , ///< Placeholder.
{ 0x010000000000D60B , " msm_unknown_01 " } , ///< Placeholder.
{ 0x010000000000D60C , " msm_unknown_02 " } , ///< Placeholder.
{ 0x010000000000D60D , " msm_unknown_03 " } , ///< Placeholder.
{ 0x010000000000D60E , " msm_unknown_04 " } , ///< Placeholder.
{ 0x010000000000D610 , " msm_unknown_05 " } , ///< Placeholder.
{ 0x010000000000D611 , " msm_unknown_06 " } , ///< Placeholder.
{ 0x010000000000D612 , " msm_unknown_07 " } , ///< Placeholder.
{ 0x010000000000D613 , " msm_unknown_08 " } , ///< Placeholder.
{ 0x010000000000D614 , " msm_unknown_09 " } , ///< Placeholder.
{ 0x010000000000D615 , " msm_unknown_0a " } , ///< Placeholder.
{ 0x010000000000D616 , " msm_unknown_0b " } , ///< Placeholder.
{ 0x010000000000D617 , " msm_unknown_0c " } , ///< Placeholder.
{ 0x010000000000D619 , " msm_unknown_0d " } , ///< Placeholder.
2023-09-03 09:57:20 +01:00
{ 0x010000000000D621 , " msm_unknown_0e " } , ///< Placeholder.
2020-12-04 06:38:44 +00:00
{ 0x010000000000D623 , " DevServer " } ,
2023-09-03 09:57:20 +01:00
{ 0x010000000000D633 , " msm_unknown_0f " } , ///< Placeholder.
2023-03-29 22:14:21 +01:00
{ 0x010000000000D640 , " htcnet " } ,
2022-07-05 02:04:28 +01:00
2020-07-26 09:00:54 +01:00
/* System applications. */
{ 0x01008BB00013C000 , " flog " } ,
{ 0x0100069000078000 , " RetailInteractiveDisplayMenu " } ,
{ 0x010000B003486000 , " AudioUsbMicDebugTool " } ,
{ 0x0100458001E04000 , " BcatTestApp01 " } ,
{ 0x0100F910020F8000 , " BcatTestApp02 " } ,
{ 0x0100B7D0020FC000 , " BcatTestApp03 " } ,
{ 0x0100132002100000 , " BcatTestApp04 " } ,
{ 0x0100935002116000 , " BcatTestApp05 " } ,
{ 0x0100DA4002130000 , " BcatTestApp06 " } ,
{ 0x0100B0F002104000 , " BcatTestApp07 " } ,
{ 0x010051E002132000 , " BcatTestApp08 " } ,
{ 0x01004CB0015C8000 , " BcatTestApp09 " } ,
{ 0x01009720015CA000 , " BcatTestApp10 " } ,
{ 0x01002F20015C6000 , " BcatTestApp11 " } ,
{ 0x0100204001F90000 , " BcatTestApp12 " } ,
{ 0x0100060001F92000 , " BcatTestApp13 " } ,
{ 0x0100C26001F94000 , " BcatTestApp14 " } ,
{ 0x0100462001F96000 , " BcatTestApp15 " } ,
{ 0x01005C6001F98000 , " BcatTestApp16 " } ,
{ 0x010070000E3C0000 , " EncounterUsr " } ,
{ 0x010086000E49C000 , " EncounterUsrDummy " } ,
{ 0x0100810002D5A000 , " ShopMonitaringTool " } ,
2021-06-18 06:34:02 +01:00
{ 0x010023D002B98000 , " DeltaStress " } ,
2023-03-29 22:14:21 +01:00
{ 0x010099F00D810000 , " sysapp_unknown_00 " } , ///< Placeholder.
{ 0x0100E6C01163C000 , " sysapp_unknown_01 " } , ///< Placeholder.
2022-07-05 02:04:28 +01:00
2021-06-18 06:34:02 +01:00
/* Pre-release system applets. */
{ 0x1000000000000001 , " SystemInitializer " } ,
{ 0x1000000000000004 , " CalWriter " } ,
{ 0x1000000000000005 , " DevMenuCommand " } ,
{ 0x1000000000000006 , " SettingsManager " } ,
{ 0x1000000000000007 , " DevMenu " } ,
{ 0x100000000000000B , " SnapShotDumper " } ,
{ 0x100000000000000C , " SystemUpdater " } ,
{ 0x100000000000000E , " ControllerFirmwareUpdater " } ,
2022-07-05 02:04:28 +01:00
2021-06-18 06:34:02 +01:00
/* Pre-release system modules. */
{ 0x1000000000000201 , " usb " } ,
{ 0x1000000000000202 , " tma " } ,
{ 0x1000000000000203 , " boot2 " } ,
{ 0x1000000000000204 , " settings " } ,
{ 0x1000000000000205 , " bus " } ,
{ 0x1000000000000206 , " bluetooth " } ,
{ 0x1000000000000208 , " DebugMonitor0 " } ,
{ 0x1000000000000209 , " dmnt " } ,
{ 0x100000000000020B , " nifm " } ,
{ 0x100000000000020C , " ptm " } ,
{ 0x100000000000020D , " shell " } ,
2023-03-29 22:14:21 +01:00
{ 0x100000000000020E , " bsdsocket " } ,
2021-06-18 06:34:02 +01:00
{ 0x100000000000020F , " hid " } ,
{ 0x1000000000000210 , " audio " } ,
{ 0x1000000000000212 , " LogManager " } ,
{ 0x1000000000000213 , " wlan " } ,
{ 0x1000000000000214 , " cs " } ,
{ 0x1000000000000215 , " ldn " } ,
{ 0x1000000000000216 , " nvservices " } ,
{ 0x1000000000000217 , " pcv " } ,
{ 0x1000000000000218 , " ppc " } ,
{ 0x100000000000021A , " lbl0 " } ,
{ 0x100000000000021B , " nvnflinger " } ,
{ 0x100000000000021C , " pcie " } ,
{ 0x100000000000021D , " account " } ,
{ 0x100000000000021E , " ns " } ,
{ 0x100000000000021F , " nfc " } ,
{ 0x1000000000000220 , " psc " } ,
{ 0x1000000000000221 , " capsrv " } ,
2021-07-19 22:09:58 +01:00
{ 0x1000000000000222 , " am " } ,
2023-03-29 22:14:21 +01:00
{ 0x1000000000000223 , " ssl " } ,
{ 0x1000000000000224 , " nim " }
2020-07-26 09:00:54 +01:00
} ;
static const u32 g_systemTitlesCount = MAX_ELEMENTS ( g_systemTitles ) ;
2020-07-25 06:56:35 +01:00
/* Function prototypes. */
2020-07-26 05:57:12 +01:00
NX_INLINE void titleFreeApplicationMetadata ( void ) ;
2021-06-01 02:12:15 +01:00
static bool titleReallocateApplicationMetadata ( u32 extra_app_count , bool is_system , bool free_entries ) ;
2021-03-10 01:12:01 +00:00
2021-06-01 02:12:15 +01:00
NX_INLINE bool titleInitializePersistentTitleStorages ( void ) ;
NX_INLINE void titleCloseTitleStorages ( void ) ;
2020-07-26 05:57:12 +01:00
2021-06-01 02:12:15 +01:00
static bool titleInitializeTitleStorage ( u8 storage_id ) ;
static void titleCloseTitleStorage ( u8 storage_id ) ;
static bool titleReallocateTitleInfoFromStorage ( TitleStorage * title_storage , u32 extra_title_count , bool free_entries ) ;
2021-03-10 13:10:43 +00:00
2021-06-01 02:12:15 +01:00
NX_INLINE void titleFreeOrphanTitleInfoEntries ( void ) ;
static void titleAddOrphanTitleInfoEntry ( TitleInfo * orphan_title ) ;
2020-07-26 05:57:12 +01:00
2020-07-28 04:32:08 +01:00
static bool titleGenerateMetadataEntriesFromSystemTitles ( void ) ;
static bool titleGenerateMetadataEntriesFromNsRecords ( void ) ;
2021-06-01 02:12:15 +01:00
static TitleApplicationMetadata * titleGenerateDummySystemMetadataEntry ( u64 title_id ) ;
static bool titleRetrieveUserApplicationMetadataByTitleId ( u64 title_id , TitleApplicationMetadata * out ) ;
2020-07-25 06:56:35 +01:00
2021-06-15 06:44:10 +01:00
NX_INLINE TitleApplicationMetadata * titleFindApplicationMetadataByTitleId ( u64 title_id , bool is_system , u32 extra_app_count ) ;
2020-07-25 06:56:35 +01:00
2023-11-26 21:17:52 +00:00
NX_INLINE u64 titleGetApplicationIdByContentMetaKey ( const NcmContentMetaKey * meta_key ) ;
2023-05-24 20:05:34 +01:00
2021-06-01 02:12:15 +01:00
static bool titleGenerateTitleInfoEntriesForTitleStorage ( TitleStorage * title_storage ) ;
static bool titleGetMetaKeysFromContentDatabase ( NcmContentMetaDatabase * ncm_db , NcmContentMetaKey * * out_meta_keys , u32 * out_meta_key_count ) ;
static bool titleGetContentInfosForMetaKey ( NcmContentMetaDatabase * ncm_db , const NcmContentMetaKey * meta_key , NcmContentInfo * * out_content_infos , u32 * out_content_count ) ;
2020-07-25 19:50:42 +01:00
2021-02-22 21:30:47 +00:00
static void titleUpdateTitleInfoLinkedLists ( void ) ;
2020-07-25 06:56:35 +01:00
2020-07-30 22:43:50 +01:00
static bool titleCreateGameCardInfoThread ( void ) ;
static void titleDestroyGameCardInfoThread ( void ) ;
2020-08-18 06:04:13 +01:00
static void titleGameCardInfoThreadFunc ( void * arg ) ;
2020-07-30 22:43:50 +01:00
static bool titleRefreshGameCardTitleInfo ( void ) ;
2020-07-25 19:50:42 +01:00
2020-07-28 04:32:08 +01:00
static bool titleIsUserApplicationContentAvailable ( u64 app_id ) ;
2021-05-18 13:32:43 +01:00
static TitleInfo * _titleGetInfoFromStorageByTitleId ( u8 storage_id , u64 title_id ) ;
2020-07-28 04:32:08 +01:00
2023-05-27 19:10:35 +01:00
static TitleInfo * titleDuplicateTitleInfoFull ( TitleInfo * title_info , TitleInfo * previous , TitleInfo * next ) ;
static TitleInfo * titleDuplicateTitleInfo ( TitleInfo * title_info ) ;
2021-06-01 02:12:15 +01:00
2020-08-21 01:18:05 +01:00
static int titleSystemTitleMetadataEntrySortFunction ( const void * a , const void * b ) ;
static int titleUserApplicationMetadataEntrySortFunction ( const void * a , const void * b ) ;
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
static int titleInfoEntrySortFunction ( const void * a , const void * b ) ;
2020-07-28 04:32:08 +01:00
2021-06-05 22:06:29 +01:00
static char * titleGetPatchVersionString ( TitleInfo * title_info ) ;
2020-07-25 06:56:35 +01:00
bool titleInitialize ( void )
{
2021-05-18 13:32:43 +01:00
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_titleMutex )
2020-07-25 06:56:35 +01:00
{
2021-05-18 13:32:43 +01:00
ret = g_titleInterfaceInit ;
if ( ret ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Allocate memory for the ns application control data. */
/* This will be used each time we need to retrieve the metadata from an application. */
g_nsAppControlData = calloc ( 1 , sizeof ( NsApplicationControlData ) ) ;
if ( ! g_nsAppControlData )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for the ns application control data! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Generate application metadata entries from hardcoded system titles, since we can't retrieve their names via ns. */
if ( ! titleGenerateMetadataEntriesFromSystemTitles ( ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to generate application metadata from hardcoded system titles! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Generate application metadata entries from ns records. */
/* Theoretically speaking, we should only need to do this once. */
/* However, if any new gamecard is inserted while the application is running, we *will* have to retrieve the metadata from its application(s). */
if ( ! titleGenerateMetadataEntriesFromNsRecords ( ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to generate application metadata from ns records! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Initialize persistent title storages (BuiltInSystem, BuiltInUser, SdCard). */
/* The background gamecard title thread will take care of initializing the gamecard title storage. */
if ( ! titleInitializePersistentTitleStorages ( ) )
2021-05-18 13:32:43 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to initialize persistent title storages! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Create user-mode exit event. */
ueventCreate ( & g_titleGameCardInfoThreadExitEvent , true ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Retrieve gamecard status change user event. */
g_titleGameCardStatusChangeUserEvent = gamecardGetStatusChangeUserEvent ( ) ;
if ( ! g_titleGameCardStatusChangeUserEvent )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to retrieve gamecard status change user event! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Create gamecard title info thread. */
if ( ! ( g_titleGameCardInfoThreadCreated = titleCreateGameCardInfoThread ( ) ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Update flags. */
ret = g_titleInterfaceInit = true ;
2020-07-30 22:43:50 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
return ret ;
}
void titleExit ( void )
{
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_titleMutex )
2020-07-30 22:43:50 +01:00
{
2021-05-18 13:32:43 +01:00
/* Destroy gamecard detection thread. */
if ( g_titleGameCardInfoThreadCreated )
{
titleDestroyGameCardInfoThread ( ) ;
g_titleGameCardInfoThreadCreated = false ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Close title storages. */
titleCloseTitleStorages ( ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free orphan title info entries. */
titleFreeOrphanTitleInfoEntries ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Free application metadata. */
titleFreeApplicationMetadata ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Free ns application control data. */
2021-06-01 02:12:15 +01:00
if ( g_nsAppControlData )
{
free ( g_nsAppControlData ) ;
g_nsAppControlData = NULL ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
g_titleInterfaceInit = false ;
2020-07-30 22:43:50 +01:00
}
2020-07-25 06:56:35 +01:00
}
NcmContentMetaDatabase * titleGetNcmDatabaseByStorageId ( u8 storage_id )
{
2021-06-01 02:12:15 +01:00
u8 idx = TITLE_STORAGE_INDEX ( storage_id ) ;
return ( idx < TITLE_STORAGE_COUNT ? & ( g_titleStorage [ idx ] . ncm_db ) : NULL ) ;
2020-07-25 06:56:35 +01:00
}
NcmContentStorage * titleGetNcmStorageByStorageId ( u8 storage_id )
{
2021-06-01 02:12:15 +01:00
u8 idx = TITLE_STORAGE_INDEX ( storage_id ) ;
return ( idx < TITLE_STORAGE_COUNT ? & ( g_titleStorage [ idx ] . ncm_storage ) : NULL ) ;
2020-07-25 06:56:35 +01:00
}
2020-07-28 04:32:08 +01:00
TitleApplicationMetadata * * titleGetApplicationMetadataEntries ( bool is_system , u32 * out_count )
{
u32 app_count = 0 ;
TitleApplicationMetadata * * app_metadata = NULL , * * tmp_app_metadata = NULL ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_titleMutex )
2020-07-28 04:32:08 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! g_titleInterfaceInit | | ( is_system & & ( ! g_systemMetadata | | ! g_systemMetadataCount ) ) | | ( ! is_system & & ( ! g_userMetadata | | ! g_userMetadataCount ) ) | | ! out_count )
2021-05-18 13:32:43 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * * cached_app_metadata = ( is_system ? g_systemMetadata : g_userMetadata ) ;
u32 cached_app_metadata_count = ( is_system ? g_systemMetadataCount : g_userMetadataCount ) ;
2021-05-18 13:32:43 +01:00
bool error = false ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u32 i = 0 ; i < cached_app_metadata_count ; i + + )
2020-07-28 04:32:08 +01:00
{
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * cur_app_metadata = cached_app_metadata [ i ] ;
2021-05-18 13:32:43 +01:00
if ( ! cur_app_metadata ) continue ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Skip current metadata entry if content data for this title isn't available. */
if ( ( is_system & & ! _titleGetInfoFromStorageByTitleId ( NcmStorageId_BuiltInSystem , cur_app_metadata - > title_id ) ) | | \
( ! is_system & & ! titleIsUserApplicationContentAvailable ( cur_app_metadata - > title_id ) ) ) continue ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Reallocate application metadata pointer array. */
tmp_app_metadata = realloc ( app_metadata , ( app_count + 1 ) * sizeof ( TitleApplicationMetadata * ) ) ;
if ( ! tmp_app_metadata )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate application metadata pointer array! " ) ;
2021-05-18 13:32:43 +01:00
if ( app_metadata ) free ( app_metadata ) ;
app_metadata = NULL ;
error = true ;
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
app_metadata = tmp_app_metadata ;
tmp_app_metadata = NULL ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Set current pointer and increase counter. */
app_metadata [ app_count + + ] = cur_app_metadata ;
2020-07-28 04:32:08 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
if ( error ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Update output counter. */
* out_count = app_count ;
2022-07-05 02:04:28 +01:00
2022-07-12 17:34:49 +01:00
if ( ! app_metadata | | ! app_count ) LOG_MSG_ERROR ( " No content data found for %s! " , is_system ? " system titles " : " user applications " ) ;
2020-07-28 04:32:08 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-28 04:32:08 +01:00
return app_metadata ;
}
2022-07-27 23:53:52 +01:00
TitleApplicationMetadata * * titleGetGameCardApplicationMetadataEntries ( u32 * out_count )
{
u32 app_count = 0 ;
TitleApplicationMetadata * * app_metadata = NULL , * * tmp_app_metadata = NULL ;
SCOPED_LOCK ( & g_titleMutex )
{
if ( ! g_titleInterfaceInit | | ! g_userMetadata | | ! g_userMetadataCount | | ! g_titleGameCardAvailable | | ! out_count )
{
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
break ;
}
bool error = false ;
for ( u32 i = 0 ; i < g_userMetadataCount ; i + + )
{
TitleApplicationMetadata * cur_app_metadata = g_userMetadata [ i ] ;
if ( ! cur_app_metadata ) continue ;
/* Skip current metadata entry if content data for this title isn't available on the inserted gamecard. */
if ( ! _titleGetInfoFromStorageByTitleId ( NcmStorageId_GameCard , cur_app_metadata - > title_id ) ) continue ;
/* Reallocate application metadata pointer array. */
tmp_app_metadata = realloc ( app_metadata , ( app_count + 1 ) * sizeof ( TitleApplicationMetadata * ) ) ;
if ( ! tmp_app_metadata )
{
LOG_MSG_ERROR ( " Failed to reallocate application metadata pointer array! " ) ;
if ( app_metadata ) free ( app_metadata ) ;
app_metadata = NULL ;
error = true ;
break ;
}
app_metadata = tmp_app_metadata ;
tmp_app_metadata = NULL ;
/* Set current pointer and increase counter. */
app_metadata [ app_count + + ] = cur_app_metadata ;
}
if ( error ) break ;
/* Update output counter. */
* out_count = app_count ;
if ( ! app_metadata | | ! app_count ) LOG_MSG_ERROR ( " No gamecard content data found for user applications! " ) ;
}
return app_metadata ;
}
2020-07-26 05:57:12 +01:00
TitleInfo * titleGetInfoFromStorageByTitleId ( u8 storage_id , u64 title_id )
2020-07-28 04:32:08 +01:00
{
2021-05-18 13:32:43 +01:00
TitleInfo * ret = NULL ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
SCOPED_LOCK ( & g_titleMutex )
{
TitleInfo * title_info = ( g_titleInterfaceInit ? _titleGetInfoFromStorageByTitleId ( storage_id , title_id ) : NULL ) ;
if ( title_info )
{
2023-05-27 19:10:35 +01:00
ret = titleDuplicateTitleInfoFull ( title_info , NULL , NULL ) ;
2022-07-12 17:34:49 +01:00
if ( ! ret ) LOG_MSG_ERROR ( " Failed to duplicate title info for %016lX! " , title_id ) ;
2021-06-01 02:12:15 +01:00
}
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
return ret ;
2020-07-28 04:32:08 +01:00
}
2021-06-01 02:12:15 +01:00
void titleFreeTitleInfo ( TitleInfo * * info )
{
TitleInfo * ptr = NULL , * tmp1 = NULL , * tmp2 = NULL ;
if ( ! info | | ! ( ptr = * info ) ) return ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free content infos. */
if ( ptr - > content_infos ) free ( ptr - > content_infos ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free previous sibling(s). */
tmp1 = ptr - > previous ;
while ( tmp1 )
{
tmp2 = tmp1 - > previous ;
2022-12-04 10:29:47 +00:00
tmp1 - > previous = tmp1 - > next = NULL ;
2021-06-01 02:12:15 +01:00
titleFreeTitleInfo ( & tmp1 ) ;
tmp1 = tmp2 ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free next sibling(s). */
tmp1 = ptr - > next ;
while ( tmp1 )
{
tmp2 = tmp1 - > next ;
2022-12-04 10:29:47 +00:00
tmp1 - > previous = tmp1 - > next = NULL ;
2021-06-01 02:12:15 +01:00
titleFreeTitleInfo ( & tmp1 ) ;
tmp1 = tmp2 ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
free ( ptr ) ;
* info = NULL ;
}
2020-07-28 04:32:08 +01:00
bool titleGetUserApplicationData ( u64 app_id , TitleUserApplicationData * out )
2020-07-26 05:57:12 +01:00
{
2021-05-18 13:32:43 +01:00
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_titleMutex )
2020-07-26 05:57:12 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! g_titleInterfaceInit | | ! app_id | | ! out )
2020-07-26 05:57:12 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-07-26 05:57:12 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
bool error = false ;
TitleInfo * app_info = NULL , * patch_info = NULL , * aoc_info = NULL , * aoc_patch_info = NULL ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Clear output. */
2021-06-01 02:12:15 +01:00
titleFreeUserApplicationData ( out ) ;
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
# define TITLE_ALLOCATE_USER_APP_DATA(elem, msg, decl) \
if ( elem # # _info & & ! out - > elem # # _info ) { \
2023-05-27 19:10:35 +01:00
out - > elem # # _info = titleDuplicateTitleInfoFull ( elem # # _info , NULL , NULL ) ; \
2022-12-04 10:29:47 +00:00
if ( ! out - > elem # # _info ) { \
LOG_MSG_ERROR ( " Failed to duplicate %s info for %016lX! " , msg , app_id ) ; \
decl ; \
} \
}
2021-05-18 13:32:43 +01:00
/* Get info for the first user application title. */
2021-06-01 02:12:15 +01:00
app_info = _titleGetInfoFromStorageByTitleId ( NcmStorageId_Any , app_id ) ;
2022-12-04 10:29:47 +00:00
TITLE_ALLOCATE_USER_APP_DATA ( app , " user application " , break ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Get info for the first patch title. */
2021-06-01 02:12:15 +01:00
patch_info = _titleGetInfoFromStorageByTitleId ( NcmStorageId_Any , titleGetPatchIdByApplicationId ( app_id ) ) ;
2022-12-04 10:29:47 +00:00
TITLE_ALLOCATE_USER_APP_DATA ( patch , " patch " , break ) ;
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
/* Get info for the first add-on content and add-on content patch titles. */
2021-06-01 02:12:15 +01:00
for ( u8 i = NcmStorageId_GameCard ; i < = NcmStorageId_SdCard ; i + + )
{
if ( i = = NcmStorageId_BuiltInSystem ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( i ) ] ) ;
if ( ! title_storage - > titles | | ! title_storage - > title_count ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u32 j = 0 ; j < title_storage - > title_count ; j + + )
{
TitleInfo * title_info = title_storage - > titles [ j ] ;
2022-12-04 10:29:47 +00:00
if ( ! title_info ) continue ;
if ( title_info - > meta_key . type = = NcmContentMetaType_AddOnContent & & titleCheckIfAddOnContentIdBelongsToApplicationId ( app_id , title_info - > meta_key . id ) )
2021-06-01 02:12:15 +01:00
{
aoc_info = title_info ;
break ;
2022-12-04 10:29:47 +00:00
} else
if ( title_info - > meta_key . type = = NcmContentMetaType_DataPatch & & titleCheckIfDataPatchIdBelongsToApplicationId ( app_id , title_info - > meta_key . id ) )
{
aoc_patch_info = title_info ;
break ;
2021-06-01 02:12:15 +01:00
}
}
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
TITLE_ALLOCATE_USER_APP_DATA ( aoc , " add-on content " , error = true ; break ) ;
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
TITLE_ALLOCATE_USER_APP_DATA ( aoc_patch , " add-on content patch " , error = true ; break ) ;
if ( out - > aoc_info & & out - > aoc_patch_info ) break ;
2021-05-18 13:32:43 +01:00
}
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
if ( error ) break ;
# undef TITLE_ALLOCATE_USER_APP_DATA
2021-05-18 13:32:43 +01:00
/* Check retrieved title info. */
2022-12-04 10:29:47 +00:00
ret = ( app_info | | patch_info | | aoc_info | | aoc_patch_info ) ;
2022-07-12 17:34:49 +01:00
if ( ! ret ) LOG_MSG_ERROR ( " Failed to retrieve user application data for ID \" %016lX \" ! " , app_id ) ;
2020-07-26 05:57:12 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Clear output. */
if ( ! ret ) titleFreeUserApplicationData ( out ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
return ret ;
2020-07-26 05:57:12 +01:00
}
2020-07-25 06:56:35 +01:00
2021-06-01 02:12:15 +01:00
void titleFreeUserApplicationData ( TitleUserApplicationData * user_app_data )
{
if ( ! user_app_data ) return ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free user application info. */
titleFreeTitleInfo ( & ( user_app_data - > app_info ) ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free patch info. */
titleFreeTitleInfo ( & ( user_app_data - > patch_info ) ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free add-on content info. */
titleFreeTitleInfo ( & ( user_app_data - > aoc_info ) ) ;
2022-12-04 10:29:47 +00:00
/* Free add-on content patch info. */
titleFreeTitleInfo ( & ( user_app_data - > aoc_patch_info ) ) ;
2021-06-01 02:12:15 +01:00
}
2023-05-27 19:10:35 +01:00
TitleInfo * titleGetAddOnContentBaseOrPatchList ( TitleInfo * title_info )
{
TitleInfo * out = NULL ;
bool success = false ;
SCOPED_LOCK ( & g_titleMutex )
{
if ( ! g_titleInterfaceInit | | ! titleIsValidInfoBlock ( title_info ) | | ( title_info - > meta_key . type ! = NcmContentMetaType_AddOnContent & & \
title_info - > meta_key . type ! = NcmContentMetaType_DataPatch ) )
{
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
break ;
}
TitleInfo * aoc_info = NULL , * tmp = NULL ;
u64 ref_tid = title_info - > meta_key . id ;
u64 lookup_tid = ( title_info - > meta_key . type = = NcmContentMetaType_AddOnContent ? titleGetDataPatchIdByAddOnContentId ( ref_tid ) : titleGetAddOnContentIdByDataPatchId ( ref_tid ) ) ;
bool error = false ;
/* Get info for the first add-on content (patch) title matching the lookup title ID. */
aoc_info = _titleGetInfoFromStorageByTitleId ( NcmStorageId_Any , lookup_tid ) ;
if ( ! aoc_info ) break ;
/* Create our own custom linked list using entries that match our lookup title ID. */
while ( aoc_info )
{
/* Check if this entry's title ID matches our lookup title ID. */
if ( aoc_info - > meta_key . id ! = lookup_tid )
{
aoc_info = aoc_info - > next ;
continue ;
}
/* Duplicate current entry. */
tmp = titleDuplicateTitleInfo ( aoc_info ) ;
if ( ! tmp )
{
LOG_MSG_ERROR ( " Failed to duplicate TitleInfo object! " ) ;
error = true ;
break ;
}
/* Update pointer. */
if ( out )
{
out - > next = tmp ;
} else {
out = tmp ;
}
tmp = NULL ;
/* Proceed onto the next entry. */
aoc_info = aoc_info - > next ;
}
if ( error ) break ;
/* Update flag. */
success = true ;
}
if ( ! success & & out ) titleFreeTitleInfo ( & out ) ;
return out ;
}
2020-07-30 23:37:45 +01:00
bool titleAreOrphanTitlesAvailable ( void )
{
2021-05-18 13:32:43 +01:00
bool ret = false ;
2021-08-25 21:48:01 +01:00
SCOPED_TRY_LOCK ( & g_titleMutex ) ret = ( g_titleInterfaceInit & & g_orphanTitleInfo & & * g_orphanTitleInfo & & g_orphanTitleInfoCount > 0 ) ;
2020-07-30 23:37:45 +01:00
return ret ;
}
2021-06-01 02:12:15 +01:00
TitleInfo * * titleGetOrphanTitles ( u32 * out_count )
2020-07-30 23:37:45 +01:00
{
TitleInfo * * orphan_info = NULL ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_titleMutex )
2020-07-30 23:37:45 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! g_titleInterfaceInit | | ! g_orphanTitleInfo | | ! * g_orphanTitleInfo | | ! g_orphanTitleInfoCount | | ! out_count )
2021-05-18 13:32:43 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Allocate orphan title info pointer array. */
2023-11-26 21:17:52 +00:00
/* titleFreeOrphanTitles() depends on the last NULL element. */
2021-06-01 02:12:15 +01:00
orphan_info = calloc ( g_orphanTitleInfoCount + 1 , sizeof ( TitleInfo * ) ) ;
2021-05-18 13:32:43 +01:00
if ( ! orphan_info )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for orphan title info pointer array! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Duplicate orphan title info entries. */
for ( u32 i = 0 ; i < g_orphanTitleInfoCount ; i + + )
{
2023-05-27 19:10:35 +01:00
orphan_info [ i ] = titleDuplicateTitleInfoFull ( g_orphanTitleInfo [ i ] , NULL , NULL ) ;
2021-06-01 02:12:15 +01:00
if ( ! orphan_info [ i ] )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to duplicate info for orphan title %016lX! " , g_orphanTitleInfo [ i ] - > meta_key . id ) ;
2021-06-01 02:12:15 +01:00
titleFreeOrphanTitles ( & orphan_info ) ;
break ;
}
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Update output counter. */
2021-06-01 02:12:15 +01:00
if ( orphan_info ) * out_count = g_orphanTitleInfoCount ;
2020-07-30 23:37:45 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-30 23:37:45 +01:00
return orphan_info ;
}
2021-06-01 02:12:15 +01:00
void titleFreeOrphanTitles ( TitleInfo * * * orphan_info )
{
TitleInfo * * ptr = NULL , * tmp = NULL ;
if ( ! orphan_info | | ! ( ptr = * orphan_info ) ) return ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
tmp = * ptr ;
while ( tmp )
{
titleFreeTitleInfo ( & tmp ) ;
tmp + + ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
free ( ptr ) ;
* orphan_info = NULL ;
}
2020-07-30 22:43:50 +01:00
bool titleIsGameCardInfoUpdated ( void )
{
2021-05-18 13:32:43 +01:00
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-06-09 05:48:17 +01:00
SCOPED_TRY_LOCK ( & g_titleMutex )
2021-05-18 13:32:43 +01:00
{
/* Check if the gamecard thread detected a gamecard status change. */
2021-06-01 02:12:15 +01:00
ret = ( g_titleInterfaceInit & & g_titleGameCardInfoUpdated ) ;
2021-05-18 13:32:43 +01:00
if ( ret ) g_titleGameCardInfoUpdated = false ;
}
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
return ret ;
}
2021-07-23 08:24:00 +01:00
char * titleGenerateFileName ( TitleInfo * title_info , u8 naming_convention , u8 illegal_char_replace_type )
2020-08-14 03:31:02 +01:00
{
2023-07-21 22:56:50 +01:00
if ( ! title_info | | ( title_info - > meta_key . type > NcmContentMetaType_BootImagePackageSafe & & title_info - > meta_key . type < NcmContentMetaType_Application ) | | \
title_info - > meta_key . type > NcmContentMetaType_DataPatch | | naming_convention > TitleNamingConvention_IdAndVersionOnly | | \
( naming_convention = = TitleNamingConvention_Full & & illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly ) )
2020-08-14 03:31:02 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-05-18 13:32:43 +01:00
return NULL ;
2020-08-14 03:31:02 +01:00
}
2022-07-05 02:04:28 +01:00
2023-07-21 22:56:50 +01:00
u8 type_idx = title_info - > meta_key . type ;
if ( type_idx > = NcmContentMetaType_Application ) type_idx - = NCM_CMT_APP_OFFSET ;
2023-12-31 17:16:49 +00:00
char title_name [ 0x300 ] = { 0 } , * filename = NULL ;
size_t title_name_len = 0 ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Generate filename for this title. */
2021-07-23 08:24:00 +01:00
if ( naming_convention = = TitleNamingConvention_Full )
2020-08-14 03:31:02 +01:00
{
2021-06-01 02:12:15 +01:00
if ( title_info - > app_metadata & & * ( title_info - > app_metadata - > lang_entry . name ) )
2020-08-14 03:31:02 +01:00
{
2023-12-31 17:16:49 +00:00
snprintf ( title_name , MAX_ELEMENTS ( title_name ) , " %s " , title_info - > app_metadata - > lang_entry . name ) ;
2022-07-05 02:04:28 +01:00
2023-12-31 17:16:49 +00:00
/* Retrieve display version string if we're dealing with a Patch. */
char * version_str = ( title_info - > meta_key . type = = NcmContentMetaType_Patch ? titleGetPatchVersionString ( title_info ) : NULL ) ;
2021-06-05 22:06:29 +01:00
if ( version_str )
{
2023-12-31 17:16:49 +00:00
title_name_len = strlen ( title_name ) ;
snprintf ( title_name + title_name_len , MAX_ELEMENTS ( title_name ) - title_name_len , " %s " , version_str ) ;
2021-06-05 22:06:29 +01:00
free ( version_str ) ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( illegal_char_replace_type ) utilsReplaceIllegalCharacters ( title_name , illegal_char_replace_type = = TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly ) ;
2020-08-14 03:31:02 +01:00
}
2022-07-05 02:04:28 +01:00
2023-12-31 17:16:49 +00:00
title_name_len = strlen ( title_name ) ;
snprintf ( title_name + title_name_len , MAX_ELEMENTS ( title_name ) - title_name_len , " [%016lX][v%u][%s] " , title_info - > meta_key . id , title_info - > meta_key . version , \
g_filenameTypeStrings [ type_idx ] ) ;
2021-06-01 02:12:15 +01:00
} else
2021-07-23 08:24:00 +01:00
if ( naming_convention = = TitleNamingConvention_IdAndVersionOnly )
2021-06-01 02:12:15 +01:00
{
2023-12-31 17:16:49 +00:00
snprintf ( title_name , MAX_ELEMENTS ( title_name ) , " %016lX_v%u_%s " , title_info - > meta_key . id , title_info - > meta_key . version , g_filenameTypeStrings [ type_idx ] ) ;
2020-08-14 03:31:02 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Duplicate generated filename. */
filename = strdup ( title_name ) ;
2022-07-12 17:34:49 +01:00
if ( ! filename ) LOG_MSG_ERROR ( " Failed to duplicate generated filename! " ) ;
2022-07-05 02:04:28 +01:00
2020-08-14 03:31:02 +01:00
return filename ;
}
2021-07-23 08:24:00 +01:00
char * titleGenerateGameCardFileName ( u8 naming_convention , u8 illegal_char_replace_type )
2020-08-14 03:31:02 +01:00
{
2021-05-18 13:32:43 +01:00
char * filename = NULL ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_titleMutex )
2020-08-14 03:31:02 +01:00
{
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( NcmStorageId_GameCard ) ] ) ;
TitleInfo * * titles = title_storage - > titles ;
u32 title_count = title_storage - > title_count ;
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
GameCardHeader gc_header = { 0 } ;
2023-12-31 17:16:49 +00:00
size_t cur_filename_len = 0 , app_name_len = 0 ;
char app_name [ 0x300 ] = { 0 } ;
2021-06-23 19:27:06 +01:00
bool error = false ;
2022-07-05 02:04:28 +01:00
2021-07-23 08:24:00 +01:00
if ( ! g_titleInterfaceInit | | ! g_titleGameCardAvailable | | naming_convention > TitleNamingConvention_IdAndVersionOnly | | \
( naming_convention = = TitleNamingConvention_Full & & illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly ) )
2020-08-14 03:31:02 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-05-18 13:32:43 +01:00
break ;
2020-08-14 03:31:02 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
/* Check if the gamecard title storage is empty. */
/* This is especially true for Kiosk / Quest gamecards. */
if ( ! titles | | ! title_count ) goto fallback ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u32 i = 0 ; i < title_count ; i + + )
2020-08-14 03:31:02 +01:00
{
2021-06-05 22:06:29 +01:00
TitleInfo * app_info = titles [ i ] , * patch_info = NULL ;
2021-05-18 13:32:43 +01:00
if ( ! app_info | | app_info - > meta_key . type ! = NcmContentMetaType_Application ) continue ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
u32 app_version = app_info - > meta_key . version ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Check if the inserted gamecard holds any bundled patches for the current user application. */
/* If so, we'll use the highest patch version available as part of the filename. */
2021-06-01 02:12:15 +01:00
for ( u32 j = 0 ; j < title_count ; j + + )
2021-05-18 13:32:43 +01:00
{
if ( j = = i ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
TitleInfo * cur_title_info = titles [ j ] ;
2023-12-31 17:16:49 +00:00
if ( ! cur_title_info | | cur_title_info - > meta_key . type ! = NcmContentMetaType_Patch | | \
! titleCheckIfPatchIdBelongsToApplicationId ( app_info - > meta_key . id , cur_title_info - > meta_key . id ) | | cur_title_info - > meta_key . version < = app_version ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
patch_info = cur_title_info ;
app_version = cur_title_info - > meta_key . version ;
2021-05-18 13:32:43 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Generate current user application name. */
* app_name = ' \0 ' ;
2022-07-05 02:04:28 +01:00
2021-07-23 08:24:00 +01:00
if ( naming_convention = = TitleNamingConvention_Full )
2020-08-14 03:31:02 +01:00
{
2021-05-18 13:32:43 +01:00
if ( cur_filename_len ) strcat ( app_name , " + " ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
if ( app_info - > app_metadata & & * ( app_info - > app_metadata - > lang_entry . name ) )
{
2023-12-31 17:16:49 +00:00
app_name_len = strlen ( app_name ) ;
snprintf ( app_name + app_name_len , MAX_ELEMENTS ( app_name ) - app_name_len , " %s " , app_info - > app_metadata - > lang_entry . name ) ;
2022-07-05 02:04:28 +01:00
2023-12-31 17:16:49 +00:00
/* Retrieve display version string if the inserted gamecard holds a patch for the current user application. */
char * version_str = ( patch_info ? titleGetPatchVersionString ( patch_info ) : NULL ) ;
2021-06-05 22:06:29 +01:00
if ( version_str )
{
2023-12-31 17:16:49 +00:00
app_name_len = strlen ( app_name ) ;
snprintf ( app_name + app_name_len , MAX_ELEMENTS ( app_name ) - app_name_len , " %s " , version_str ) ;
2021-06-05 22:06:29 +01:00
free ( version_str ) ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
if ( illegal_char_replace_type ) utilsReplaceIllegalCharacters ( app_name , illegal_char_replace_type = = TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly ) ;
}
2022-07-05 02:04:28 +01:00
2023-12-31 17:16:49 +00:00
app_name_len = strlen ( app_name ) ;
snprintf ( app_name + app_name_len , MAX_ELEMENTS ( app_name ) - app_name_len , " [%016lX][v%u] " , app_info - > meta_key . id , app_version ) ;
2021-05-18 13:32:43 +01:00
} else
2021-07-23 08:24:00 +01:00
if ( naming_convention = = TitleNamingConvention_IdAndVersionOnly )
2021-05-18 13:32:43 +01:00
{
if ( cur_filename_len ) strcat ( app_name , " + " ) ;
2023-12-31 17:16:49 +00:00
app_name_len = strlen ( app_name ) ;
snprintf ( app_name + app_name_len , MAX_ELEMENTS ( app_name ) - app_name_len , " %016lX_v%u " , app_info - > meta_key . id , app_version ) ;
2020-08-14 03:31:02 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Reallocate output buffer. */
2023-12-31 17:16:49 +00:00
app_name_len = strlen ( app_name ) ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
char * tmp_filename = realloc ( filename , ( cur_filename_len + app_name_len + 1 ) * sizeof ( char ) ) ;
2021-05-18 13:32:43 +01:00
if ( ! tmp_filename )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate filename buffer! " ) ;
2021-05-18 13:32:43 +01:00
if ( filename ) free ( filename ) ;
filename = NULL ;
error = true ;
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
filename = tmp_filename ;
tmp_filename = NULL ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Concatenate current user application name. */
filename [ cur_filename_len ] = ' \0 ' ;
strcat ( filename , app_name ) ;
cur_filename_len + = app_name_len ;
2020-08-14 03:31:02 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
fallback :
2021-06-01 02:12:15 +01:00
if ( ! filename & & ! error )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Error: the inserted gamecard doesn't hold any user applications! " ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Fallback string if no applications can be found. */
sprintf ( app_name , " gamecard " ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( gamecardGetHeader ( & gc_header ) )
{
strcat ( app_name , " _ " ) ;
cur_filename_len = strlen ( app_name ) ;
2023-11-03 01:22:47 +00:00
utilsGenerateHexString ( app_name + cur_filename_len , sizeof ( app_name ) - cur_filename_len , gc_header . package_id , sizeof ( gc_header . package_id ) , false ) ;
2021-06-01 02:12:15 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
filename = strdup ( app_name ) ;
2022-07-12 17:34:49 +01:00
if ( ! filename ) LOG_MSG_ERROR ( " Failed to duplicate fallback filename! " ) ;
2021-06-01 02:12:15 +01:00
}
2020-08-14 03:31:02 +01:00
}
2022-07-05 02:04:28 +01:00
2020-08-14 03:31:02 +01:00
return filename ;
}
2022-07-12 01:27:03 +01:00
const char * titleGetNcmStorageIdName ( u8 storage_id )
{
return ( storage_id < = NcmStorageId_Any ? g_titleNcmStorageIdNames [ storage_id ] : NULL ) ;
}
2020-07-29 22:02:21 +01:00
const char * titleGetNcmContentTypeName ( u8 content_type )
{
2020-10-03 18:09:29 +01:00
return ( content_type < = NcmContentType_DeltaFragment ? g_titleNcmContentTypeNames [ content_type ] : NULL ) ;
2020-07-29 22:02:21 +01:00
}
2020-07-25 06:56:35 +01:00
2020-08-14 03:31:02 +01:00
const char * titleGetNcmContentMetaTypeName ( u8 content_meta_type )
{
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
if ( ( content_meta_type > NcmContentMetaType_BootImagePackageSafe & & content_meta_type < NcmContentMetaType_Application ) | | content_meta_type > NcmContentMetaType_DataPatch ) return NULL ;
2023-07-21 22:56:50 +01:00
return ( content_meta_type < = NcmContentMetaType_BootImagePackageSafe ? g_titleNcmContentMetaTypeNames [ content_meta_type ] : g_titleNcmContentMetaTypeNames [ content_meta_type - NCM_CMT_APP_OFFSET ] ) ;
2020-08-14 03:31:02 +01:00
}
2020-07-26 05:57:12 +01:00
NX_INLINE void titleFreeApplicationMetadata ( void )
{
2021-06-23 19:27:06 +01:00
for ( u8 i = 0 ; i < 2 ; i + + )
2020-07-26 05:57:12 +01:00
{
2021-06-23 19:27:06 +01:00
TitleApplicationMetadata * * cached_app_metadata = ( i = = 0 ? g_systemMetadata : g_userMetadata ) ;
u32 cached_app_metadata_count = ( i = = 0 ? g_systemMetadataCount : g_userMetadataCount ) ;
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
if ( cached_app_metadata )
2021-06-01 02:12:15 +01:00
{
2021-06-23 19:27:06 +01:00
for ( u32 j = 0 ; j < cached_app_metadata_count ; j + + )
2021-06-01 02:12:15 +01:00
{
2021-06-23 19:27:06 +01:00
TitleApplicationMetadata * cur_app_metadata = cached_app_metadata [ j ] ;
if ( cur_app_metadata )
{
if ( cur_app_metadata - > icon ) free ( cur_app_metadata - > icon ) ;
free ( cur_app_metadata ) ;
}
2021-06-01 02:12:15 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
free ( cached_app_metadata ) ;
2021-06-01 02:12:15 +01:00
}
2021-03-10 01:12:01 +00:00
}
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
g_systemMetadata = g_userMetadata = NULL ;
g_systemMetadataCount = g_userMetadataCount = 0 ;
2021-06-01 02:12:15 +01:00
}
static bool titleReallocateApplicationMetadata ( u32 extra_app_count , bool is_system , bool free_entries )
{
TitleApplicationMetadata * * cached_app_metadata = ( is_system ? g_systemMetadata : g_userMetadata ) , * * tmp_app_metadata = NULL ;
u32 cached_app_metadata_count = ( is_system ? g_systemMetadataCount : g_userMetadataCount ) ;
u32 realloc_app_count = ( ! free_entries ? ( cached_app_metadata_count + extra_app_count ) : cached_app_metadata_count ) ;
2021-03-10 01:12:01 +00:00
bool success = false ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
if ( free_entries )
{
2021-06-01 02:12:15 +01:00
if ( ! cached_app_metadata )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Free previously allocated application metadata entries. */
for ( u32 i = 0 ; i < = extra_app_count ; i + + )
{
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * cur_app_metadata = cached_app_metadata [ cached_app_metadata_count + i ] ;
2021-03-10 01:12:01 +00:00
if ( cur_app_metadata )
{
if ( cur_app_metadata - > icon ) free ( cur_app_metadata - > icon ) ;
free ( cur_app_metadata ) ;
2024-01-13 20:25:02 +00:00
cached_app_metadata [ cached_app_metadata_count + i ] = NULL ;
2021-03-10 01:12:01 +00:00
}
}
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
if ( realloc_app_count )
{
/* Reallocate application metadata pointer array. */
2021-06-01 02:12:15 +01:00
tmp_app_metadata = realloc ( cached_app_metadata , realloc_app_count * sizeof ( TitleApplicationMetadata * ) ) ;
2021-03-10 01:12:01 +00:00
if ( tmp_app_metadata )
{
/* Update application metadata pointer. */
2021-06-01 02:12:15 +01:00
cached_app_metadata = tmp_app_metadata ;
2021-03-10 01:12:01 +00:00
tmp_app_metadata = NULL ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Clear new application metadata pointer array area (if needed). */
2024-01-13 20:25:02 +00:00
if ( realloc_app_count > cached_app_metadata_count ) memset ( cached_app_metadata + cached_app_metadata_count , 0 , extra_app_count * sizeof ( TitleApplicationMetadata * ) ) ;
2021-03-10 01:12:01 +00:00
} else {
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate application metadata pointer array! (%u element[s]). " , realloc_app_count ) ;
2021-03-10 01:12:01 +00:00
goto end ;
}
2021-06-01 02:12:15 +01:00
} else
if ( cached_app_metadata )
{
2021-03-10 01:12:01 +00:00
/* Free application metadata pointer array. */
2021-06-01 02:12:15 +01:00
free ( cached_app_metadata ) ;
cached_app_metadata = NULL ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update global pointer array. */
if ( is_system )
{
g_systemMetadata = cached_app_metadata ;
} else {
g_userMetadata = cached_app_metadata ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update flag. */
success = true ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
end :
return success ;
}
NX_INLINE bool titleInitializePersistentTitleStorages ( void )
{
for ( u8 i = NcmStorageId_BuiltInSystem ; i < = NcmStorageId_SdCard ; i + + )
{
if ( ! titleInitializeTitleStorage ( i ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to initialize title storage with ID %u! " , i ) ;
2021-06-01 02:12:15 +01:00
return false ;
}
}
2022-07-05 02:04:28 +01:00
2023-11-26 21:17:52 +00:00
# if LOG_LEVEL <= LOG_LEVEL_INFO
# define ORPHAN_INFO_LOG(fmt, ...) utilsAppendFormattedStringToBuffer(&orphan_info_buf, &orphan_info_buf_size, fmt, ##__VA_ARGS__)
if ( g_orphanTitleInfo & & g_orphanTitleInfoCount )
{
char * orphan_info_buf = NULL ;
size_t orphan_info_buf_size = 0 ;
ORPHAN_INFO_LOG ( " Identified %u orphan title(s) across all initialized title storages. \r \n " , g_orphanTitleInfoCount ) ;
for ( u32 i = 0 ; i < g_orphanTitleInfoCount ; i + + )
{
TitleInfo * orphan_info = g_orphanTitleInfo [ i ] ;
ORPHAN_INFO_LOG ( " - %016lX v%u (%s, %s).%s " , orphan_info - > meta_key . id , orphan_info - > version . value , titleGetNcmContentMetaTypeName ( orphan_info - > meta_key . type ) , \
titleGetNcmStorageIdName ( orphan_info - > storage_id ) , ( i + 1 ) < g_orphanTitleInfoCount ? " \r \n " : " " ) ;
}
if ( orphan_info_buf )
{
LOG_MSG_INFO ( " %s " , orphan_info_buf ) ;
free ( orphan_info_buf ) ;
}
}
# undef ORPHAN_INFO_LOG
# endif /* LOG_LEVEL <= LOG_LEVEL_INFO */
2021-06-01 02:12:15 +01:00
return true ;
}
NX_INLINE void titleCloseTitleStorages ( void )
{
for ( u8 i = NcmStorageId_GameCard ; i < = NcmStorageId_SdCard ; i + + ) titleCloseTitleStorage ( i ) ;
}
static bool titleInitializeTitleStorage ( u8 storage_id )
{
if ( storage_id < NcmStorageId_GameCard | | storage_id > NcmStorageId_SdCard )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Close title storage before proceeding. */
titleCloseTitleStorage ( storage_id ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( storage_id ) ] ) ;
NcmContentMetaDatabase * ncm_db = & ( title_storage - > ncm_db ) ;
NcmContentStorage * ncm_storage = & ( title_storage - > ncm_storage ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
Result rc = 0 ;
bool success = false ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Set ncm storage ID. */
title_storage - > storage_id = storage_id ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Open ncm database. */
rc = ncmOpenContentMetaDatabase ( ncm_db , storage_id ) ;
if ( R_FAILED ( rc ) )
{
/* If the SD card is mounted, but it isn't currently being used by HOS, 0x21005 will be returned, so we'll just filter this particular error and continue. */
/* This can occur when using the "Nintendo" directory from a different console, or when the "sdmc:/Nintendo/Contents/private" file is corrupted. */
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " ncmOpenContentMetaDatabase failed for %s! (0x%X). " , titleGetNcmStorageIdName ( storage_id ) , rc ) ;
2021-06-01 02:12:15 +01:00
if ( storage_id = = NcmStorageId_SdCard & & rc = = 0x21005 ) success = true ;
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Open ncm storage. */
rc = ncmOpenContentStorage ( ncm_storage , storage_id ) ;
if ( R_FAILED ( rc ) )
{
/* If the SD card is mounted, but it isn't currently being used by HOS, 0x21005 will be returned, so we'll just filter this particular error and continue. */
/* This can occur when using the "Nintendo" directory from a different console, or when the "sdmc:/Nintendo/Contents/private" file is corrupted. */
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " ncmOpenContentStorage failed for %s! (0x%X). " , titleGetNcmStorageIdName ( storage_id ) , rc ) ;
2021-06-01 02:12:15 +01:00
if ( storage_id = = NcmStorageId_SdCard & & rc = = 0x21005 ) success = true ;
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Generate title info entries for this storage. */
if ( ! titleGenerateTitleInfoEntriesForTitleStorage ( title_storage ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to generate title info entries for %s! " , titleGetNcmStorageIdName ( storage_id ) ) ;
2021-06-01 02:12:15 +01:00
goto end ;
2021-03-10 01:12:01 +00:00
}
2022-07-05 02:04:28 +01:00
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " Loaded %u title info %s from %s. " , title_storage - > title_count , ( title_storage - > title_count = = 1 ? " entry " : " entries " ) , titleGetNcmStorageIdName ( storage_id ) ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Update flag. */
success = true ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
end :
return success ;
}
2021-06-01 02:12:15 +01:00
static void titleCloseTitleStorage ( u8 storage_id )
2020-07-26 05:57:12 +01:00
{
2021-06-01 02:12:15 +01:00
if ( storage_id < NcmStorageId_GameCard | | storage_id > NcmStorageId_SdCard ) return ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( storage_id ) ] ) ;
NcmContentMetaDatabase * ncm_db = & ( title_storage - > ncm_db ) ;
NcmContentStorage * ncm_storage = & ( title_storage - > ncm_storage ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free title infos from this title storage. */
if ( title_storage - > titles )
2020-07-26 05:57:12 +01:00
{
2021-06-01 02:12:15 +01:00
for ( u32 i = 0 ; i < title_storage - > title_count ; i + + )
2020-07-26 05:57:12 +01:00
{
2021-06-01 02:12:15 +01:00
TitleInfo * cur_title_info = title_storage - > titles [ i ] ;
2021-03-10 01:12:01 +00:00
if ( cur_title_info )
{
if ( cur_title_info - > content_infos ) free ( cur_title_info - > content_infos ) ;
free ( cur_title_info ) ;
}
2020-07-26 05:57:12 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
free ( title_storage - > titles ) ;
title_storage - > titles = NULL ;
2020-07-26 05:57:12 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Reset title count. */
title_storage - > title_count = 0 ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Check if the ncm storage handle for this title storage has already been retrieved. If so, close it. */
if ( serviceIsActive ( & ( ncm_storage - > s ) ) ) ncmContentStorageClose ( ncm_storage ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Check if the ncm database handle for this title storage has already been retrieved. If so, close it. */
if ( serviceIsActive ( & ( ncm_db - > s ) ) ) ncmContentMetaDatabaseClose ( ncm_db ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Reset ncm storage ID. */
title_storage - > storage_id = NcmStorageId_None ;
2020-07-26 05:57:12 +01:00
}
2020-07-25 06:56:35 +01:00
2021-06-01 02:12:15 +01:00
static bool titleReallocateTitleInfoFromStorage ( TitleStorage * title_storage , u32 extra_title_count , bool free_entries )
2021-03-10 01:12:01 +00:00
{
2021-06-01 02:12:15 +01:00
if ( ! title_storage )
2021-03-10 01:12:01 +00:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-03-10 01:12:01 +00:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleInfo * * title_info = title_storage - > titles , * * tmp_title_info = NULL ;
u32 title_count = title_storage - > title_count ;
u32 realloc_title_count = ( ! free_entries ? ( title_count + extra_title_count ) : title_count ) ;
2021-03-10 01:12:01 +00:00
bool success = false ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
if ( free_entries )
{
2021-06-01 02:12:15 +01:00
if ( ! title_info )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Free previously allocated title info entries. */
for ( u32 i = 0 ; i < = extra_title_count ; i + + )
{
2021-06-01 02:12:15 +01:00
TitleInfo * cur_title_info = title_info [ title_count + i ] ;
2021-03-10 01:12:01 +00:00
if ( cur_title_info )
{
if ( cur_title_info - > content_infos ) free ( cur_title_info - > content_infos ) ;
free ( cur_title_info ) ;
2024-01-13 20:25:02 +00:00
title_info [ title_count + i ] = NULL ;
2021-03-10 01:12:01 +00:00
}
}
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
if ( realloc_title_count )
{
/* Reallocate title info pointer array. */
2021-06-01 02:12:15 +01:00
tmp_title_info = realloc ( title_info , realloc_title_count * sizeof ( TitleInfo * ) ) ;
2021-03-10 01:12:01 +00:00
if ( tmp_title_info )
{
/* Update title info pointer. */
2021-06-01 02:12:15 +01:00
title_info = tmp_title_info ;
2021-03-10 01:12:01 +00:00
tmp_title_info = NULL ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Clear new title info pointer array area (if needed). */
2024-01-13 20:25:02 +00:00
if ( realloc_title_count > title_count ) memset ( title_info + title_count , 0 , extra_title_count * sizeof ( TitleInfo * ) ) ;
2021-03-10 01:12:01 +00:00
} else {
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate title info pointer array! (%u element[s]). " , realloc_title_count ) ;
2021-03-10 01:12:01 +00:00
goto end ;
}
2021-06-01 02:12:15 +01:00
} else
if ( title_info )
{
2021-03-10 01:12:01 +00:00
/* Free title info pointer array. */
2021-06-01 02:12:15 +01:00
free ( title_info ) ;
title_info = NULL ;
2021-03-10 01:12:01 +00:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update pointer array in global title storage. */
title_storage - > titles = title_info ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Update flag. */
success = true ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
end :
return success ;
}
2021-06-01 02:12:15 +01:00
NX_INLINE void titleFreeOrphanTitleInfoEntries ( void )
2021-03-10 13:10:43 +00:00
{
if ( g_orphanTitleInfo )
{
free ( g_orphanTitleInfo ) ;
g_orphanTitleInfo = NULL ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 13:10:43 +00:00
g_orphanTitleInfoCount = 0 ;
}
static void titleAddOrphanTitleInfoEntry ( TitleInfo * orphan_title )
{
if ( ! orphan_title ) return ;
2022-07-05 02:04:28 +01:00
2021-03-10 13:10:43 +00:00
/* Reallocate orphan title info pointer array. */
TitleInfo * * tmp_orphan_info = realloc ( g_orphanTitleInfo , ( g_orphanTitleInfoCount + 1 ) * sizeof ( TitleInfo * ) ) ;
if ( ! tmp_orphan_info )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate orphan title info pointer array! " ) ;
2021-03-10 13:10:43 +00:00
return ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 13:10:43 +00:00
g_orphanTitleInfo = tmp_orphan_info ;
tmp_orphan_info = NULL ;
2022-07-05 02:04:28 +01:00
2021-03-10 13:10:43 +00:00
/* Set orphan title info entry pointer. */
g_orphanTitleInfo [ g_orphanTitleInfoCount + + ] = orphan_title ;
2022-07-05 02:04:28 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
/* Sort orphan title info entries by title ID, version and storage ID. */
if ( g_orphanTitleInfoCount > 1 ) qsort ( g_orphanTitleInfo , g_orphanTitleInfoCount , sizeof ( TitleInfo * ) , & titleInfoEntrySortFunction ) ;
2021-03-10 13:10:43 +00:00
}
2020-07-28 04:32:08 +01:00
static bool titleGenerateMetadataEntriesFromSystemTitles ( void )
{
2021-03-10 01:12:01 +00:00
u32 extra_app_count = 0 ;
bool success = false ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Reallocate application metadata pointer array. */
2021-06-01 02:12:15 +01:00
if ( ! titleReallocateApplicationMetadata ( g_systemTitlesCount , true , false ) )
2020-07-28 04:32:08 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate application metadata pointer array for system titles! " ) ;
2020-07-28 04:32:08 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-28 04:32:08 +01:00
/* Fill new application metadata entries. */
2021-03-10 01:12:01 +00:00
for ( extra_app_count = 0 ; extra_app_count < g_systemTitlesCount ; extra_app_count + + )
2020-07-28 04:32:08 +01:00
{
2021-03-10 01:12:01 +00:00
/* Allocate memory for the current entry. */
TitleApplicationMetadata * cur_app_metadata = calloc ( 1 , sizeof ( TitleApplicationMetadata ) ) ;
if ( ! cur_app_metadata )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for application metadata entry #%u! " , extra_app_count ) ;
2021-03-10 01:12:01 +00:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Fill information. */
2021-04-20 23:43:37 +01:00
const TitleSystemEntry * system_title = & ( g_systemTitles [ extra_app_count ] ) ;
2021-03-10 01:12:01 +00:00
cur_app_metadata - > title_id = system_title - > title_id ;
2021-07-21 16:04:18 +01:00
sprintf ( cur_app_metadata - > lang_entry . name , " %s " , system_title - > name ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Set application metadata entry pointer. */
2021-06-01 02:12:15 +01:00
g_systemMetadata [ g_systemMetadataCount + extra_app_count ] = cur_app_metadata ;
2020-07-28 04:32:08 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-28 04:32:08 +01:00
/* Update application metadata count. */
2021-06-01 02:12:15 +01:00
g_systemMetadataCount + = g_systemTitlesCount ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Sort metadata entries by title ID. */
if ( g_systemMetadataCount > 1 ) qsort ( g_systemMetadata , g_systemMetadataCount , sizeof ( TitleApplicationMetadata * ) , & titleSystemTitleMetadataEntrySortFunction ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Update flag. */
success = true ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
end :
/* Free previously allocated application metadata pointers. Ignore return value. */
2021-06-01 02:12:15 +01:00
if ( ! success ) titleReallocateApplicationMetadata ( extra_app_count , true , true ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
return success ;
2020-07-28 04:32:08 +01:00
}
static bool titleGenerateMetadataEntriesFromNsRecords ( void )
2020-07-25 06:56:35 +01:00
{
Result rc = 0 ;
2022-07-05 02:04:28 +01:00
2021-07-19 22:09:58 +01:00
NsApplicationRecord * app_records = NULL , * tmp_app_records = NULL ;
u32 app_records_block_count = 0 , app_records_count = 0 , extra_app_count = 0 ;
size_t app_records_size = 0 , app_records_block_size = ( NS_APPLICATION_RECORD_BLOCK_SIZE * sizeof ( NsApplicationRecord ) ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
bool success = false , free_entries = false ;
2022-07-05 02:04:28 +01:00
2021-07-19 22:09:58 +01:00
/* Retrieve NS application records in a loop until we get them all. */
do {
/* Allocate memory for the NS application records. */
tmp_app_records = realloc ( app_records , app_records_size + app_records_block_size ) ;
if ( ! tmp_app_records )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate NS application records buffer! (%u) " , app_records_count ) ;
2021-07-19 22:09:58 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-07-19 22:09:58 +01:00
app_records = tmp_app_records ;
tmp_app_records = NULL ;
app_records_size + = app_records_block_size ;
2022-07-05 02:04:28 +01:00
2021-07-19 22:09:58 +01:00
/* Clear newly allocated block. */
NsApplicationRecord * app_records_block = & ( app_records [ app_records_count ] ) ;
memset ( app_records_block , 0 , app_records_block_size ) ;
2022-07-05 02:04:28 +01:00
2021-07-19 22:09:58 +01:00
/* Retrieve NS application records. */
rc = nsListApplicationRecord ( app_records_block , NS_APPLICATION_RECORD_BLOCK_SIZE , ( s32 ) app_records_count , ( s32 * ) & app_records_block_count ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " nsListApplicationRecord failed! (0x%X) (%u). " , rc , app_records_count ) ;
2021-07-19 22:09:58 +01:00
if ( ! app_records_count ) goto end ;
break ; /* Gotta work with what we have. */
}
2022-07-05 02:04:28 +01:00
2021-07-19 22:09:58 +01:00
app_records_count + = app_records_block_count ;
} while ( app_records_block_count > = NS_APPLICATION_RECORD_BLOCK_SIZE ) ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Return right away if no records were retrieved. */
if ( ! app_records_count )
{
success = true ;
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Reallocate application metadata pointer array. */
2021-06-01 02:12:15 +01:00
if ( ! titleReallocateApplicationMetadata ( app_records_count , false , false ) )
2020-07-25 06:56:35 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate application metadata pointer array for NS records! " ) ;
2020-07-25 06:56:35 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
free_entries = true ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Retrieve application metadata for each NS application record. */
2020-07-25 06:56:35 +01:00
for ( u32 i = 0 ; i < app_records_count ; i + + )
{
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * cur_app_metadata = g_userMetadata [ g_userMetadataCount + extra_app_count ] ;
2021-03-10 01:12:01 +00:00
if ( ! cur_app_metadata )
{
/* Allocate memory for a new application metadata entry. */
cur_app_metadata = calloc ( 1 , sizeof ( TitleApplicationMetadata ) ) ;
if ( ! cur_app_metadata )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for application metadata entry #%u! (%u / %u). " , extra_app_count , i + 1 , app_records_count ) ;
2021-03-10 01:12:01 +00:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Set application metadata entry pointer. */
2021-06-01 02:12:15 +01:00
g_userMetadata [ g_userMetadataCount + extra_app_count ] = cur_app_metadata ;
2021-03-10 01:12:01 +00:00
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Retrieve application metadata. */
2021-06-01 02:12:15 +01:00
if ( ! titleRetrieveUserApplicationMetadataByTitleId ( app_records [ i ] . application_id , cur_app_metadata ) ) continue ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Increase extra application metadata counter. */
extra_app_count + + ;
2020-07-25 06:56:35 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Check retrieved application metadata count. */
2021-03-10 01:12:01 +00:00
if ( ! extra_app_count )
2020-07-25 06:56:35 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Unable to retrieve application metadata from NS application records! (%u element[s]). " , app_records_count ) ;
2020-07-25 06:56:35 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-08-21 01:18:05 +01:00
/* Update application metadata count. */
2021-06-01 02:12:15 +01:00
g_userMetadataCount + = extra_app_count ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Free extra allocated pointers if we didn't use them. */
2021-06-01 02:12:15 +01:00
if ( extra_app_count < app_records_count ) titleReallocateApplicationMetadata ( 0 , false , false ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Sort application metadata entries by name. */
if ( g_userMetadataCount > 1 ) qsort ( g_userMetadata , g_userMetadataCount , sizeof ( TitleApplicationMetadata * ) , & titleUserApplicationMetadataEntrySortFunction ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Update flag. */
2020-08-21 01:18:05 +01:00
success = true ;
2022-07-05 02:04:28 +01:00
2020-08-21 01:18:05 +01:00
end :
2021-03-10 01:12:01 +00:00
if ( app_records ) free ( app_records ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Free previously allocated application metadata pointers. Ignore return value. */
2021-06-01 02:12:15 +01:00
if ( ! success & & free_entries ) titleReallocateApplicationMetadata ( extra_app_count , false , true ) ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
return success ;
}
2021-06-01 02:12:15 +01:00
static TitleApplicationMetadata * titleGenerateDummySystemMetadataEntry ( u64 title_id )
2020-07-25 06:56:35 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! title_id )
2020-07-25 06:56:35 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
return NULL ;
2020-07-25 06:56:35 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * cur_app_metadata = NULL ;
bool free_entry = false ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Reallocate application metadata pointer array. */
if ( ! titleReallocateApplicationMetadata ( 1 , true , false ) )
2020-07-25 06:56:35 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate application metadata pointer array for %016lX! " , title_id ) ;
2021-06-01 02:12:15 +01:00
goto end ;
2020-07-25 06:56:35 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
free_entry = true ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Allocate memory for the current entry. */
cur_app_metadata = calloc ( 1 , sizeof ( TitleApplicationMetadata ) ) ;
if ( ! cur_app_metadata )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for application metadata %016lX! " , title_id ) ;
2021-06-01 02:12:15 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Fill information. */
cur_app_metadata - > title_id = title_id ;
sprintf ( cur_app_metadata - > lang_entry . name , " Unknown " ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Set application metadata entry pointer. */
g_systemMetadata [ g_systemMetadataCount + + ] = cur_app_metadata ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Sort metadata entries by title ID. */
if ( g_systemMetadataCount > 1 ) qsort ( g_systemMetadata , g_systemMetadataCount , sizeof ( TitleApplicationMetadata * ) , & titleSystemTitleMetadataEntrySortFunction ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
end :
/* Free previously allocated application metadata pointer. Ignore return value. */
if ( ! cur_app_metadata & & free_entry ) titleReallocateApplicationMetadata ( 0 , true , false ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
return cur_app_metadata ;
}
static bool titleRetrieveUserApplicationMetadataByTitleId ( u64 title_id , TitleApplicationMetadata * out )
{
if ( ! g_nsAppControlData | | ! title_id | | ! out )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
Result rc = 0 ;
u64 write_size = 0 ;
NacpLanguageEntry * lang_entry = NULL ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
u32 icon_size = 0 ;
u8 * icon = NULL ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Retrieve ns application control data. */
rc = nsGetApplicationControlData ( NsApplicationControlSource_Storage , title_id , g_nsAppControlData , sizeof ( NsApplicationControlData ) , & write_size ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " nsGetApplicationControlData failed for title ID \" %016lX \" ! (0x%X). " , title_id , rc ) ;
2021-06-01 02:12:15 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( write_size < sizeof ( NacpStruct ) )
2020-07-25 06:56:35 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Retrieved application control data buffer is too small! (0x%lX). " , write_size ) ;
2020-07-25 06:56:35 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Get language entry. */
rc = nacpGetLanguageEntry ( & ( g_nsAppControlData - > nacp ) , & lang_entry ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " nacpGetLanguageEntry failed! (0x%X). " , rc ) ;
2020-07-25 06:56:35 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 09:00:54 +01:00
/* Get icon. */
icon_size = ( u32 ) ( write_size - sizeof ( NacpStruct ) ) ;
if ( icon_size )
{
icon = malloc ( icon_size ) ;
if ( ! icon )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Error allocating memory for the icon buffer! (0x%X). " , icon_size ) ;
2020-07-26 09:00:54 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 09:00:54 +01:00
memcpy ( icon , g_nsAppControlData - > icon , icon_size ) ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Copy data. */
out - > title_id = title_id ;
2022-07-05 02:04:28 +01:00
2024-01-13 20:25:02 +00:00
if ( lang_entry )
{
memcpy ( & ( out - > lang_entry ) , lang_entry , sizeof ( NacpLanguageEntry ) ) ;
utilsTrimString ( out - > lang_entry . name ) ;
utilsTrimString ( out - > lang_entry . author ) ;
} else {
/* Yes, this can happen -- NACPs with empty language entries are a thing, somehow. */
sprintf ( out - > lang_entry . name , " Unknown " ) ;
sprintf ( out - > lang_entry . author , " Unknown " ) ;
LOG_DATA_DEBUG ( & ( g_nsAppControlData - > nacp ) , sizeof ( NacpStruct ) , " NACP dump (ID %016lX): " , title_id ) ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 09:00:54 +01:00
out - > icon_size = icon_size ;
out - > icon = icon ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
return true ;
}
2021-06-15 06:44:10 +01:00
NX_INLINE TitleApplicationMetadata * titleFindApplicationMetadataByTitleId ( u64 title_id , bool is_system , u32 extra_app_count )
2020-07-25 06:56:35 +01:00
{
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
if ( ! title_id | | ( is_system & & ( ! g_systemMetadata | | ! g_systemMetadataCount ) ) | | ( ! is_system & & ( ! g_userMetadata | | ! g_userMetadataCount ) ) ) return NULL ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * * cached_app_metadata = ( is_system ? g_systemMetadata : g_userMetadata ) ;
2021-06-15 06:44:10 +01:00
u32 cached_app_metadata_count = ( ( is_system ? g_systemMetadataCount : g_userMetadataCount ) + extra_app_count ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u32 i = 0 ; i < cached_app_metadata_count ; i + + )
2020-07-25 06:56:35 +01:00
{
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * cur_app_metadata = cached_app_metadata [ i ] ;
if ( cur_app_metadata & & cur_app_metadata - > title_id = = title_id ) return cur_app_metadata ;
2020-07-25 06:56:35 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
return NULL ;
2020-07-25 06:56:35 +01:00
}
2023-11-26 21:17:52 +00:00
NX_INLINE u64 titleGetApplicationIdByContentMetaKey ( const NcmContentMetaKey * meta_key )
2023-05-24 20:05:34 +01:00
{
if ( ! meta_key ) return 0 ;
u64 app_id = meta_key - > id ;
switch ( meta_key - > type )
{
case NcmContentMetaType_Patch :
app_id = titleGetApplicationIdByPatchId ( meta_key - > id ) ;
break ;
case NcmContentMetaType_AddOnContent :
app_id = titleGetApplicationIdByAddOnContentId ( meta_key - > id ) ;
break ;
case NcmContentMetaType_Delta :
app_id = titleGetApplicationIdByDeltaId ( meta_key - > id ) ;
break ;
case NcmContentMetaType_DataPatch :
app_id = titleGetApplicationIdByDataPatchId ( meta_key - > id ) ;
break ;
default :
break ;
}
return app_id ;
}
2021-06-01 02:12:15 +01:00
static bool titleGenerateTitleInfoEntriesForTitleStorage ( TitleStorage * title_storage )
2020-07-25 06:56:35 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! title_storage | | title_storage - > storage_id < NcmStorageId_GameCard | | title_storage - > storage_id > NcmStorageId_SdCard | | ! serviceIsActive ( & ( title_storage - > ncm_db . s ) ) )
2020-07-25 06:56:35 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
return false ;
2020-07-25 06:56:35 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
u8 storage_id = title_storage - > storage_id ;
NcmContentMetaDatabase * ncm_db = & ( title_storage - > ncm_db ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
u32 total = 0 , extra_title_count = 0 ;
NcmContentMetaKey * meta_keys = NULL ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
bool success = false , free_entries = false ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Get content meta keys for this storage. */
if ( ! titleGetMetaKeysFromContentDatabase ( ncm_db , & meta_keys , & total ) ) goto end ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Check if we're dealing with an empty storage. */
if ( ! total )
2020-07-25 19:50:42 +01:00
{
2021-06-01 02:12:15 +01:00
success = true ;
2020-07-25 19:50:42 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Reallocate pointer array in title storage. */
if ( ! titleReallocateTitleInfoFromStorage ( title_storage , total , false ) ) goto end ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
free_entries = true ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Fill new title info entries. */
for ( u32 i = 0 ; i < total ; i + + )
2020-07-25 06:56:35 +01:00
{
2021-06-01 02:12:15 +01:00
u64 tmp_size = 0 ;
NcmContentMetaKey * cur_meta_key = & ( meta_keys [ i ] ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleInfo * cur_title_info = title_storage - > titles [ title_storage - > title_count + extra_title_count ] ;
if ( ! cur_title_info )
2020-07-25 06:56:35 +01:00
{
2021-06-01 02:12:15 +01:00
/* Allocate memory for a new entry. */
cur_title_info = calloc ( 1 , sizeof ( TitleInfo ) ) ;
if ( ! cur_title_info )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for title info entry #%u! (%u / %u). " , extra_title_count , i + 1 , total ) ;
2021-06-01 02:12:15 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Set title info entry pointer. */
title_storage - > titles [ title_storage - > title_count + extra_title_count ] = cur_title_info ;
2020-07-25 06:56:35 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Get content infos. */
if ( ! titleGetContentInfosForMetaKey ( ncm_db , cur_meta_key , & ( cur_title_info - > content_infos ) , & ( cur_title_info - > content_count ) ) )
2021-03-10 13:10:43 +00:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to get content infos for title ID %016lX! " , cur_meta_key - > id ) ;
2021-06-01 02:12:15 +01:00
continue ;
2021-03-10 13:10:43 +00:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Calculate title size. */
for ( u32 j = 0 ; j < cur_title_info - > content_count ; j + + )
{
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
ncmContentInfoSizeToU64 ( & ( cur_title_info - > content_infos [ j ] ) , & tmp_size ) ;
2021-06-01 02:12:15 +01:00
cur_title_info - > size + = tmp_size ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Fill information. */
cur_title_info - > storage_id = storage_id ;
memcpy ( & ( cur_title_info - > meta_key ) , cur_meta_key , sizeof ( NcmContentMetaKey ) ) ;
cur_title_info - > version . value = cur_title_info - > meta_key . version ;
2021-07-29 08:50:17 +01:00
utilsGenerateFormattedSizeString ( ( double ) cur_title_info - > size , cur_title_info - > size_str , sizeof ( cur_title_info - > size_str ) ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Retrieve application metadata. */
2023-11-26 21:17:52 +00:00
u64 app_id = titleGetApplicationIdByContentMetaKey ( & ( cur_title_info - > meta_key ) ) ;
2021-06-15 06:44:10 +01:00
cur_title_info - > app_metadata = titleFindApplicationMetadataByTitleId ( app_id , storage_id = = NcmStorageId_BuiltInSystem , 0 ) ;
2021-06-01 02:12:15 +01:00
if ( ! cur_title_info - > app_metadata & & storage_id = = NcmStorageId_BuiltInSystem )
{
/* Generate dummy system metadata entry if we have no hardcoded information for this system title. */
cur_title_info - > app_metadata = titleGenerateDummySystemMetadataEntry ( cur_title_info - > meta_key . id ) ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Increase extra title info counter. */
extra_title_count + + ;
2020-07-25 06:56:35 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Check retrieved title info count. */
if ( ! extra_title_count )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Unable to generate title info entries! (%u element[s]). " , total ) ;
2021-06-01 02:12:15 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update title info count. */
title_storage - > title_count + = extra_title_count ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free extra allocated pointers if we didn't use them. */
if ( extra_title_count < total ) titleReallocateTitleInfoFromStorage ( title_storage , 0 , false ) ;
2022-07-05 02:04:28 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
/* Sort title info entries by title ID, version and storage ID. */
qsort ( title_storage - > titles , title_storage - > title_count , sizeof ( TitleInfo * ) , & titleInfoEntrySortFunction ) ;
2021-06-01 02:12:15 +01:00
/* Update linked lists for user applications, patches and add-on contents. */
/* This will also keep track of orphan titles - titles with no available application metadata. */
titleUpdateTitleInfoLinkedLists ( ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update flag. */
success = true ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
end :
if ( meta_keys ) free ( meta_keys ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Free previously allocated title info pointers. Ignore return value. */
if ( ! success & & free_entries ) titleReallocateTitleInfoFromStorage ( title_storage , extra_title_count , true ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
return success ;
2020-07-25 06:56:35 +01:00
}
2021-06-01 02:12:15 +01:00
static bool titleGetMetaKeysFromContentDatabase ( NcmContentMetaDatabase * ncm_db , NcmContentMetaKey * * out_meta_keys , u32 * out_meta_key_count )
2020-07-25 06:56:35 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! ncm_db | | ! serviceIsActive ( & ( ncm_db - > s ) ) | | ! out_meta_keys | | ! out_meta_key_count )
2020-07-25 06:56:35 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-07-25 06:56:35 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
Result rc = 0 ;
2021-06-01 02:12:15 +01:00
u32 written = 0 , total = 0 ;
2020-07-25 06:56:35 +01:00
NcmContentMetaKey * meta_keys = NULL , * meta_keys_tmp = NULL ;
size_t meta_keys_size = sizeof ( NcmContentMetaKey ) ;
2021-06-01 02:12:15 +01:00
bool success = false ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Allocate memory for the ncm application content meta keys. */
meta_keys = calloc ( 1 , meta_keys_size ) ;
if ( ! meta_keys )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Unable to allocate memory for the ncm application meta keys! " ) ;
2020-07-25 06:56:35 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Get a full list of all titles available in this storage. */
/* Meta type '0' means all title types will be retrieved. */
2020-07-29 22:02:21 +01:00
rc = ncmContentMetaDatabaseList ( ncm_db , ( s32 * ) & total , ( s32 * ) & written , meta_keys , 1 , 0 , 0 , 0 , UINT64_MAX , NcmContentInstallType_Full ) ;
2020-07-25 06:56:35 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " ncmContentMetaDatabaseList failed! (0x%X) (first entry). " , rc ) ;
2020-07-25 06:56:35 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Check if our application meta keys buffer was actually filled. */
/* If it wasn't, odds are there are no titles in this storage. */
if ( ! written | | ! total )
{
2021-06-01 02:12:15 +01:00
* out_meta_key_count = 0 ;
2020-07-25 06:56:35 +01:00
success = true ;
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Check if we need to resize our application meta keys buffer. */
if ( total > written )
{
/* Update application meta keys buffer size. */
meta_keys_size * = total ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Reallocate application meta keys buffer. */
meta_keys_tmp = realloc ( meta_keys , meta_keys_size ) ;
if ( ! meta_keys_tmp )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Unable to reallocate application meta keys buffer! (%u entries). " , total ) ;
2020-07-25 06:56:35 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
meta_keys = meta_keys_tmp ;
meta_keys_tmp = NULL ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Issue call again. */
2020-07-29 22:02:21 +01:00
rc = ncmContentMetaDatabaseList ( ncm_db , ( s32 * ) & total , ( s32 * ) & written , meta_keys , ( s32 ) total , 0 , 0 , 0 , UINT64_MAX , NcmContentInstallType_Full ) ;
2020-07-25 06:56:35 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " ncmContentMetaDatabaseList failed! (0x%X) (%u %s). " , rc , total , total > 1 ? " entries " : " entry " ) ;
2020-07-25 06:56:35 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
/* Safety check. */
if ( written ! = total )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Application meta key count mismatch! (%u != %u). " , written , total ) ;
2020-07-25 06:56:35 +01:00
goto end ;
}
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update output. */
* out_meta_keys = meta_keys ;
* out_meta_key_count = total ;
2022-07-05 02:04:28 +01:00
2021-02-22 21:30:47 +00:00
success = true ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
end :
2021-06-01 02:12:15 +01:00
if ( ! success & & meta_keys ) free ( meta_keys ) ;
2022-07-05 02:04:28 +01:00
2020-07-25 06:56:35 +01:00
return success ;
}
2021-06-01 02:12:15 +01:00
static bool titleGetContentInfosForMetaKey ( NcmContentMetaDatabase * ncm_db , const NcmContentMetaKey * meta_key , NcmContentInfo * * out_content_infos , u32 * out_content_count )
2020-07-26 05:57:12 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! ncm_db | | ! serviceIsActive ( & ( ncm_db - > s ) ) | | ! meta_key | | ! out_content_infos | | ! out_content_count )
2020-07-26 05:57:12 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-07-26 05:57:12 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
Result rc = 0 ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
NcmContentMetaHeader content_meta_header = { 0 } ;
u64 content_meta_header_read_size = 0 ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
NcmContentInfo * content_infos = NULL ;
u32 content_count = 0 , written = 0 ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
bool success = false ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
/* Retrieve content meta header. */
rc = ncmContentMetaDatabaseGet ( ncm_db , meta_key , & content_meta_header_read_size , & content_meta_header , sizeof ( NcmContentMetaHeader ) ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " ncmContentMetaDatabaseGet failed! (0x%X). " , rc ) ;
2020-07-26 05:57:12 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
if ( content_meta_header_read_size ! = sizeof ( NcmContentMetaHeader ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Content meta header size mismatch! (0x%lX != 0x%lX). " , content_meta_header_read_size , sizeof ( NcmContentMetaHeader ) ) ;
2020-07-26 05:57:12 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
/* Get content count. */
content_count = ( u32 ) content_meta_header . content_count ;
if ( ! content_count )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Content count is zero! " ) ;
2020-07-26 05:57:12 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
/* Allocate memory for the content infos. */
content_infos = calloc ( content_count , sizeof ( NcmContentInfo ) ) ;
if ( ! content_infos )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Unable to allocate memory for the content infos buffer! (%u content[s]). " , content_count ) ;
2020-07-26 05:57:12 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
/* Retrieve content infos. */
rc = ncmContentMetaDatabaseListContentInfo ( ncm_db , ( s32 * ) & written , content_infos , ( s32 ) content_count , meta_key , 0 ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " ncmContentMetaDatabaseListContentInfo failed! (0x%X). " , rc ) ;
2020-07-26 05:57:12 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
if ( written ! = content_count )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Content count mismatch! (%u != %u). " , written , content_count ) ;
2020-07-26 05:57:12 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
/* Update output. */
* out_content_infos = content_infos ;
* out_content_count = content_count ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
success = true ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
end :
if ( ! success & & content_infos ) free ( content_infos ) ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
return success ;
}
2021-02-22 21:30:47 +00:00
static void titleUpdateTitleInfoLinkedLists ( void )
{
2021-06-01 02:12:15 +01:00
/* Free orphan title info entries. */
titleFreeOrphanTitleInfoEntries ( ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Loop through all available title storages. */
for ( u8 i = NcmStorageId_GameCard ; i < = NcmStorageId_SdCard ; i + + )
2021-02-22 21:30:47 +00:00
{
2021-06-01 02:12:15 +01:00
/* Don't process system titles. */
if ( i = = NcmStorageId_BuiltInSystem ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( i ) ] ) ;
TitleInfo * * titles = title_storage - > titles ;
u32 title_count = title_storage - > title_count ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Don't proceed if the current storage holds no titles. */
if ( ! titles | | ! title_count ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Process titles from the current storage. */
for ( u32 j = 0 ; j < title_count ; j + + )
2021-02-22 21:30:47 +00:00
{
2021-06-01 02:12:15 +01:00
/* Get pointer to the current title info and reset its linked list pointers. */
TitleInfo * child_info = titles [ j ] ;
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
if ( ! child_info ) continue ;
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
child_info - > previous = child_info - > next = NULL ;
2022-07-05 02:04:28 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
/* If we're dealing with a title that's not an user application, patch, add-on content or add-on content patch, flag it as orphan and proceed onto the next one. */
if ( child_info - > meta_key . type < NcmContentMetaType_Application | | ( child_info - > meta_key . type > NcmContentMetaType_AddOnContent & & \
child_info - > meta_key . type ! = NcmContentMetaType_DataPatch ) )
{
titleAddOrphanTitleInfoEntry ( child_info ) ;
continue ;
}
2022-12-04 10:29:47 +00:00
if ( child_info - > meta_key . type ! = NcmContentMetaType_Application & & ! child_info - > app_metadata )
2021-02-22 21:30:47 +00:00
{
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
/* We're dealing with a patch, an add-on content or an add-on content patch. */
2022-12-04 10:29:47 +00:00
/* We'll just retrieve a pointer to the first matching user application entry and use it to set a pointer to an application metadata entry. */
2023-11-26 21:17:52 +00:00
u64 app_id = titleGetApplicationIdByContentMetaKey ( & ( child_info - > meta_key ) ) ;
2022-12-04 10:29:47 +00:00
TitleInfo * parent = _titleGetInfoFromStorageByTitleId ( NcmStorageId_Any , app_id ) ;
if ( parent )
2021-02-22 21:30:47 +00:00
{
2022-12-04 10:29:47 +00:00
/* Set pointer to application metadata. */
child_info - > app_metadata = parent - > app_metadata ;
} else {
/* Add orphan title info entry since we have no application metadata. */
2021-06-01 02:12:15 +01:00
titleAddOrphanTitleInfoEntry ( child_info ) ;
continue ;
2021-02-22 21:30:47 +00:00
}
}
2022-07-05 02:04:28 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
/* Locate previous user application, patch, add-on content or add-on content patch entry. */
2021-06-01 02:12:15 +01:00
/* If it's found, we will update both its next pointer and the previous pointer from the current entry. */
for ( u8 k = i ; k > = NcmStorageId_GameCard ; k - - )
2021-02-22 21:30:47 +00:00
{
2021-06-01 02:12:15 +01:00
/* Don't process system titles. And don't proceed if we're currently dealing with the first entry from a storage. */
if ( k = = NcmStorageId_BuiltInSystem | | ( k = = i & & j = = 0 ) ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleStorage * prev_title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( k ) ] ) ;
TitleInfo * * prev_titles = prev_title_storage - > titles ;
u32 prev_title_count = prev_title_storage - > title_count ;
u32 start_idx = ( k = = i ? j : prev_title_count ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Don't proceed if the current storage holds no titles. */
if ( ! prev_titles | | ! prev_title_count ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u32 l = start_idx ; l > 0 ; l - - )
{
TitleInfo * prev_info = prev_titles [ l - 1 ] ;
if ( ! prev_info ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( prev_info - > meta_key . type = = child_info - > meta_key . type & & \
2022-12-04 10:29:47 +00:00
( ( ( child_info - > meta_key . type = = NcmContentMetaType_Application | | child_info - > meta_key . type = = NcmContentMetaType_Patch ) & & prev_info - > meta_key . id = = child_info - > meta_key . id ) | | \
( child_info - > meta_key . type = = NcmContentMetaType_AddOnContent & & titleCheckIfAddOnContentIdsAreSiblings ( prev_info - > meta_key . id , child_info - > meta_key . id ) ) | | \
( child_info - > meta_key . type = = NcmContentMetaType_DataPatch & & titleCheckIfDataPatchIdsAreSiblings ( prev_info - > meta_key . id , child_info - > meta_key . id ) ) ) )
2021-06-01 02:12:15 +01:00
{
prev_info - > next = child_info ;
child_info - > previous = prev_info ;
break ;
}
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( child_info - > previous ) break ;
2021-02-22 21:30:47 +00:00
}
}
}
}
2020-07-30 22:43:50 +01:00
static bool titleCreateGameCardInfoThread ( void )
2020-07-25 19:50:42 +01:00
{
2020-08-18 06:04:13 +01:00
if ( ! utilsCreateThread ( & g_titleGameCardInfoThread , titleGameCardInfoThreadFunc , NULL , 1 ) )
2020-07-30 22:43:50 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to create gamecard title info thread! " ) ;
2020-07-30 22:43:50 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
return true ;
}
static void titleDestroyGameCardInfoThread ( void )
{
/* Signal the exit event to terminate the gamecard title info thread. */
ueventSignal ( & g_titleGameCardInfoThreadExitEvent ) ;
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
/* Wait for the gamecard title info thread to exit. */
2020-08-18 06:04:13 +01:00
utilsJoinThread ( & g_titleGameCardInfoThread ) ;
2020-07-30 22:43:50 +01:00
}
2020-08-18 06:04:13 +01:00
static void titleGameCardInfoThreadFunc ( void * arg )
2020-07-30 22:43:50 +01:00
{
2023-12-20 19:32:48 +00:00
NX_IGNORE_ARG ( arg ) ;
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
Result rc = 0 ;
int idx = 0 ;
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
Waiter gamecard_status_event_waiter = waiterForUEvent ( g_titleGameCardStatusChangeUserEvent ) ;
Waiter exit_event_waiter = waiterForUEvent ( & g_titleGameCardInfoThreadExitEvent ) ;
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
while ( true )
{
/* Wait until an event is triggered. */
rc = waitMulti ( & idx , - 1 , gamecard_status_event_waiter , exit_event_waiter ) ;
if ( R_FAILED ( rc ) ) continue ;
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
/* Exit event triggered. */
if ( idx = = 1 ) break ;
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
/* Update gamecard title info. */
2021-06-01 02:12:15 +01:00
SCOPED_LOCK ( & g_titleMutex ) g_titleGameCardInfoUpdated = titleRefreshGameCardTitleInfo ( ) ;
2020-07-30 22:43:50 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-30 22:43:50 +01:00
/* Update gamecard flags. */
g_titleGameCardAvailable = g_titleGameCardInfoUpdated = false ;
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
threadExit ( ) ;
2020-07-30 22:43:50 +01:00
}
static bool titleRefreshGameCardTitleInfo ( void )
{
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = NULL ;
TitleInfo * * titles = NULL ;
u32 title_count = 0 , gamecard_app_count = 0 , extra_app_count = 0 ;
2021-03-10 01:12:01 +00:00
bool status = false , success = false , cleanup = true , free_entries = false ;
2022-07-05 02:04:28 +01:00
2020-07-25 19:50:42 +01:00
/* Retrieve current gamecard status. */
status = ( gamecardGetStatus ( ) = = GameCardStatus_InsertedAndInfoLoaded ) ;
if ( status = = g_titleGameCardAvailable | | ! status )
{
2020-07-28 04:32:08 +01:00
success = cleanup = ( status ! = g_titleGameCardAvailable ) ;
2020-07-25 19:50:42 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Initialize gamecard title storage. */
if ( ! titleInitializeTitleStorage ( NcmStorageId_GameCard ) )
2020-07-25 19:50:42 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to initialize gamecard title storage! " ) ;
2020-07-25 19:50:42 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Get gamecard title storage info. */
title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( NcmStorageId_GameCard ) ] ) ;
titles = title_storage - > titles ;
title_count = title_storage - > title_count ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Verify title count. */
if ( ! title_count )
2020-07-25 19:50:42 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Gamecard title count is zero! " ) ;
2020-07-25 19:50:42 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Get gamecard user application count. */
2021-06-01 02:12:15 +01:00
for ( u32 i = 0 ; i < title_count ; i + + )
2020-07-25 19:50:42 +01:00
{
2021-06-01 02:12:15 +01:00
TitleInfo * cur_title_info = titles [ i ] ;
2021-03-10 01:12:01 +00:00
if ( cur_title_info & & cur_title_info - > meta_key . type = = NcmContentMetaType_Application ) gamecard_app_count + + ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Return immediately if, for some reason, there are no user applications. */
if ( ! gamecard_app_count )
{
success = true ;
cleanup = false ;
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Reallocate application metadata pointer array. */
2021-06-01 02:12:15 +01:00
if ( ! titleReallocateApplicationMetadata ( gamecard_app_count , false , false ) )
2021-03-10 01:12:01 +00:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to reallocate application metadata pointer array for gamecard user applications! " ) ;
2021-03-10 01:12:01 +00:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
free_entries = true ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Retrieve application metadata for gamecard user applications. */
2021-06-01 02:12:15 +01:00
for ( u32 i = 0 ; i < title_count ; i + + )
2021-03-10 01:12:01 +00:00
{
2021-06-01 02:12:15 +01:00
TitleInfo * cur_title_info = titles [ i ] ;
2021-03-10 01:12:01 +00:00
if ( ! cur_title_info ) continue ;
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
/* Do not proceed if application metadata has already been retrieved, or if we can successfully retrieve it. */
2023-11-26 21:17:52 +00:00
u64 app_id = titleGetApplicationIdByContentMetaKey ( & ( cur_title_info - > meta_key ) ) ;
2023-05-24 20:05:34 +01:00
if ( cur_title_info - > app_metadata ! = NULL | | ( cur_title_info - > app_metadata = titleFindApplicationMetadataByTitleId ( app_id , false , extra_app_count ) ) ! = NULL ) continue ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Retrieve application metadata pointer. */
2021-06-01 02:12:15 +01:00
TitleApplicationMetadata * cur_app_metadata = g_userMetadata [ g_userMetadataCount + extra_app_count ] ;
2021-03-10 01:12:01 +00:00
if ( ! cur_app_metadata )
2020-07-25 19:50:42 +01:00
{
2021-03-10 01:12:01 +00:00
/* Allocate memory for a new application metadata entry. */
cur_app_metadata = calloc ( 1 , sizeof ( TitleApplicationMetadata ) ) ;
if ( ! cur_app_metadata )
2020-07-25 19:50:42 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for application metadata entry #%u! " , extra_app_count ) ;
2020-07-25 19:50:42 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Set application metadata entry pointer. */
2021-06-01 02:12:15 +01:00
g_userMetadata [ g_userMetadataCount + extra_app_count ] = cur_app_metadata ;
2020-07-25 19:50:42 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-25 19:50:42 +01:00
/* Retrieve application metadata. */
2021-06-01 02:12:15 +01:00
if ( ! titleRetrieveUserApplicationMetadataByTitleId ( app_id , cur_app_metadata ) ) continue ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Update application metadata pointer in title info. */
cur_title_info - > app_metadata = cur_app_metadata ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Increase extra application metadata counter. */
extra_app_count + + ;
2020-07-25 19:50:42 +01:00
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
if ( extra_app_count )
2020-07-25 19:50:42 +01:00
{
2021-03-10 01:12:01 +00:00
/* Update application metadata count. */
2021-06-01 02:12:15 +01:00
g_userMetadataCount + = extra_app_count ;
2022-07-05 02:04:28 +01:00
2021-02-22 21:30:47 +00:00
/* Sort application metadata entries by name. */
2021-06-01 02:12:15 +01:00
if ( g_userMetadataCount > 1 ) qsort ( g_userMetadata , g_userMetadataCount , sizeof ( TitleApplicationMetadata * ) , & titleUserApplicationMetadataEntrySortFunction ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 13:10:43 +00:00
/* Update linked lists for user applications, patches and add-on contents. */
/* This will take care of orphan titles we might now have application metadata for. */
titleUpdateTitleInfoLinkedLists ( ) ;
2021-06-01 02:12:15 +01:00
} else
if ( g_userMetadata [ g_userMetadataCount ] )
{
2021-03-10 01:12:01 +00:00
/* Free leftover application metadata entry (if needed). */
2021-06-01 02:12:15 +01:00
free ( g_userMetadata [ g_userMetadataCount ] ) ;
g_userMetadata [ g_userMetadataCount ] = NULL ;
2020-07-25 19:50:42 +01:00
}
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Free extra allocated pointers if we didn't use them. */
2021-06-01 02:12:15 +01:00
if ( extra_app_count < gamecard_app_count ) titleReallocateApplicationMetadata ( 0 , false , false ) ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Update flags. */
2020-07-25 19:50:42 +01:00
success = true ;
cleanup = false ;
2022-07-05 02:04:28 +01:00
2020-07-25 19:50:42 +01:00
end :
/* Update gamecard status. */
g_titleGameCardAvailable = status ;
2022-07-05 02:04:28 +01:00
2021-03-10 01:12:01 +00:00
/* Free previously allocated application metadata pointers. Ignore return value. */
2021-06-01 02:12:15 +01:00
if ( ! success & & free_entries ) titleReallocateApplicationMetadata ( extra_app_count , false , true ) ;
2022-07-05 02:04:28 +01:00
2020-07-25 19:50:42 +01:00
if ( cleanup )
{
2021-06-01 02:12:15 +01:00
/* Close gamecard title storage. */
titleCloseTitleStorage ( NcmStorageId_GameCard ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update linked lists for user applications, patches and add-on contents. */
titleUpdateTitleInfoLinkedLists ( ) ;
2020-07-25 19:50:42 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-25 19:50:42 +01:00
return success ;
}
2021-06-01 02:12:15 +01:00
static bool titleIsUserApplicationContentAvailable ( u64 app_id )
2020-07-25 19:50:42 +01:00
{
2021-06-01 02:12:15 +01:00
if ( ! app_id ) return false ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u8 i = NcmStorageId_GameCard ; i < = NcmStorageId_SdCard ; i + + )
{
if ( i = = NcmStorageId_BuiltInSystem ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = & ( g_titleStorage [ TITLE_STORAGE_INDEX ( i ) ] ) ;
if ( ! title_storage - > titles | | ! * ( title_storage - > titles ) | | ! title_storage - > title_count ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u32 j = 0 ; j < title_storage - > title_count ; j + + )
{
TitleInfo * title_info = title_storage - > titles [ j ] ;
if ( ! title_info ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( ( title_info - > meta_key . type = = NcmContentMetaType_Application & & title_info - > meta_key . id = = app_id ) | | \
( title_info - > meta_key . type = = NcmContentMetaType_Patch & & titleCheckIfPatchIdBelongsToApplicationId ( app_id , title_info - > meta_key . id ) ) | | \
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
( title_info - > meta_key . type = = NcmContentMetaType_AddOnContent & & titleCheckIfAddOnContentIdBelongsToApplicationId ( app_id , title_info - > meta_key . id ) ) | | \
( title_info - > meta_key . type = = NcmContentMetaType_DataPatch & & titleCheckIfDataPatchIdBelongsToApplicationId ( app_id , title_info - > meta_key . id ) ) ) return true ;
2021-06-01 02:12:15 +01:00
}
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
return false ;
2020-07-25 06:56:35 +01:00
}
2020-07-28 04:32:08 +01:00
2021-06-01 02:12:15 +01:00
static TitleInfo * _titleGetInfoFromStorageByTitleId ( u8 storage_id , u64 title_id )
2020-07-28 04:32:08 +01:00
{
2021-06-01 02:12:15 +01:00
if ( storage_id < NcmStorageId_GameCard | | storage_id > NcmStorageId_Any | | ! title_id )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
return NULL ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleInfo * out = NULL ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
u8 start_idx = ( storage_id = = NcmStorageId_Any ? TITLE_STORAGE_INDEX ( NcmStorageId_GameCard ) : TITLE_STORAGE_INDEX ( storage_id ) ) ;
u8 max_val = ( storage_id = = NcmStorageId_Any ? TITLE_STORAGE_INDEX ( NcmStorageId_SdCard ) : start_idx ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u8 i = start_idx ; i < = max_val ; i + + )
2020-07-28 04:32:08 +01:00
{
2021-06-01 02:12:15 +01:00
TitleStorage * title_storage = & ( g_titleStorage [ i ] ) ;
if ( ! title_storage - > titles | | ! * ( title_storage - > titles ) | | ! title_storage - > title_count ) continue ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
for ( u32 j = 0 ; j < title_storage - > title_count ; j + + )
{
TitleInfo * title_info = title_storage - > titles [ j ] ;
if ( title_info & & title_info - > meta_key . id = = title_id )
{
out = title_info ;
break ;
}
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( out ) break ;
2020-07-28 04:32:08 +01:00
}
2022-07-05 02:04:28 +01:00
2022-07-12 18:11:23 +01:00
if ( ! out ) LOG_MSG_DEBUG ( " Unable to find title info entry with ID \" %016lX \" in %s. " , title_id , titleGetNcmStorageIdName ( storage_id ) ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
return out ;
2020-07-28 04:32:08 +01:00
}
2023-05-27 19:10:35 +01:00
static TitleInfo * titleDuplicateTitleInfoFull ( TitleInfo * title_info , TitleInfo * previous , TitleInfo * next )
2020-07-28 04:32:08 +01:00
{
2023-05-27 19:10:35 +01:00
if ( ! titleIsValidInfoBlock ( title_info ) )
2021-06-01 02:12:15 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-01 02:12:15 +01:00
return NULL ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
TitleInfo * title_info_dup = NULL , * tmp1 = NULL , * tmp2 = NULL ;
2022-12-04 10:29:47 +00:00
bool dup_previous = false , dup_next = false , success = false ;
2022-07-05 02:04:28 +01:00
2023-05-27 19:10:35 +01:00
/* Duplicate TitleInfo object. */
title_info_dup = titleDuplicateTitleInfo ( title_info ) ;
2021-06-01 02:12:15 +01:00
if ( ! title_info_dup )
{
2023-05-27 19:10:35 +01:00
LOG_MSG_ERROR ( " Failed to duplicate TitleInfo object! " ) ;
2021-06-01 02:12:15 +01:00
return NULL ;
}
2022-07-05 02:04:28 +01:00
2022-12-04 10:29:47 +00:00
# define TITLE_DUPLICATE_LINKED_LIST(elem, prv, nxt) \
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
if ( title_info - > elem ) { \
if ( elem ) { \
title_info_dup - > elem = elem ; \
} else { \
2023-05-27 19:10:35 +01:00
title_info_dup - > elem = titleDuplicateTitleInfoFull ( title_info - > elem , prv , nxt ) ; \
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
if ( ! title_info_dup - > elem ) goto end ; \
dup_ # # elem = true ; \
} \
2020-07-28 04:32:08 +01:00
}
2022-07-05 02:04:28 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
# define TITLE_FREE_DUPLICATED_LINKED_LIST(elem) \
if ( dup_ # # elem ) { \
tmp1 = title_info_dup - > elem ; \
while ( tmp1 ) { \
tmp2 = tmp1 - > elem ; \
2022-12-04 10:29:47 +00:00
tmp1 - > previous = tmp1 - > next = NULL ; \
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
titleFreeTitleInfo ( & tmp1 ) ; \
tmp1 = tmp2 ; \
} \
2021-06-01 02:12:15 +01:00
}
2022-07-05 02:04:28 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
/* Duplicate linked lists based on two different principles: */
/* 1) Linked list pointers will only be populated if their corresponding pointer is also populated in the TitleInfo element to duplicate. */
/* 2) Pointers passed into this function take precedence before actual data duplication. */
2022-12-04 10:29:47 +00:00
TITLE_DUPLICATE_LINKED_LIST ( previous , NULL , title_info_dup ) ;
TITLE_DUPLICATE_LINKED_LIST ( next , title_info_dup , NULL ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update flag. */
success = true ;
2022-07-05 02:04:28 +01:00
2020-07-28 04:32:08 +01:00
end :
2021-06-01 02:12:15 +01:00
/* We can't directly use titleFreeTitleInfo() on title_info_dup because some or all of the linked list data may have been provided as function arguments. */
/* So we'll take care of freeing data the old fashioned way. */
2023-05-27 19:10:35 +01:00
if ( ! success & & title_info_dup )
{
/* Free content infos pointer. */
if ( title_info_dup - > content_infos ) free ( title_info_dup - > content_infos ) ;
/* Free previous and next linked lists (if duplicated). */
/* We need to take care of not freeing the linked lists right away, either because we may have already freed them, or because they may have been passed as arguments. */
/* Furthermore, both the next pointer from the previous sibling and the previous pointer from the next sibling reference our current duplicated entry. */
/* To avoid issues, we'll just clear all linked list pointers. */
TITLE_FREE_DUPLICATED_LINKED_LIST ( previous ) ;
TITLE_FREE_DUPLICATED_LINKED_LIST ( next ) ;
/* Free allocated buffer and update return pointer. */
free ( title_info_dup ) ;
title_info_dup = NULL ;
}
# undef TITLE_DUPLICATE_LINKED_LIST
# undef TITLE_FREE_DUPLICATED_LINKED_LIST
return title_info_dup ;
}
static TitleInfo * titleDuplicateTitleInfo ( TitleInfo * title_info )
{
if ( ! titleIsValidInfoBlock ( title_info ) )
{
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
return NULL ;
}
TitleInfo * title_info_dup = NULL ;
NcmContentInfo * content_infos_dup = NULL ;
bool success = false ;
/* Allocate memory for the new TitleInfo element. */
title_info_dup = calloc ( 1 , sizeof ( TitleInfo ) ) ;
if ( ! title_info_dup )
{
LOG_MSG_ERROR ( " Failed to allocate memory for TitleInfo duplicate! " ) ;
return NULL ;
}
/* Copy TitleInfo data. */
memcpy ( title_info_dup , title_info , sizeof ( TitleInfo ) ) ;
title_info_dup - > previous = title_info_dup - > next = NULL ;
/* Allocate memory for NcmContentInfo elements. */
content_infos_dup = calloc ( title_info - > content_count , sizeof ( NcmContentInfo ) ) ;
if ( ! content_infos_dup )
{
LOG_MSG_ERROR ( " Failed to allocate memory for NcmContentInfo duplicates! " ) ;
goto end ;
}
/* Copy NcmContentInfo data. */
memcpy ( content_infos_dup , title_info - > content_infos , title_info - > content_count * sizeof ( NcmContentInfo ) ) ;
/* Update content infos pointer. */
title_info_dup - > content_infos = content_infos_dup ;
/* Update flag. */
success = true ;
end :
2021-06-01 02:12:15 +01:00
if ( ! success )
{
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
if ( content_infos_dup ) free ( content_infos_dup ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( title_info_dup )
{
free ( title_info_dup ) ;
title_info_dup = NULL ;
}
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
return title_info_dup ;
2020-07-28 04:32:08 +01:00
}
2020-08-21 01:18:05 +01:00
static int titleSystemTitleMetadataEntrySortFunction ( const void * a , const void * b )
2020-07-28 04:32:08 +01:00
{
2021-03-12 16:47:40 +00:00
const TitleApplicationMetadata * app_metadata_1 = * ( ( const TitleApplicationMetadata * * ) a ) ;
2021-03-10 01:12:01 +00:00
const TitleApplicationMetadata * app_metadata_2 = * ( ( const TitleApplicationMetadata * * ) b ) ;
2022-07-05 02:04:28 +01:00
2020-07-28 04:32:08 +01:00
if ( app_metadata_1 - > title_id < app_metadata_2 - > title_id )
{
return - 1 ;
} else
if ( app_metadata_1 - > title_id > app_metadata_2 - > title_id )
{
return 1 ;
}
2022-07-05 02:04:28 +01:00
2020-07-28 04:32:08 +01:00
return 0 ;
}
2020-07-28 04:38:45 +01:00
2020-08-21 01:18:05 +01:00
static int titleUserApplicationMetadataEntrySortFunction ( const void * a , const void * b )
2020-07-28 04:38:45 +01:00
{
2021-03-12 16:47:40 +00:00
const TitleApplicationMetadata * app_metadata_1 = * ( ( const TitleApplicationMetadata * * ) a ) ;
2021-03-10 01:12:01 +00:00
const TitleApplicationMetadata * app_metadata_2 = * ( ( const TitleApplicationMetadata * * ) b ) ;
2022-07-05 02:04:28 +01:00
2020-07-28 04:38:45 +01:00
return strcasecmp ( app_metadata_1 - > lang_entry . name , app_metadata_2 - > lang_entry . name ) ;
}
2020-08-21 01:18:05 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
static int titleInfoEntrySortFunction ( const void * a , const void * b )
2020-08-21 01:18:05 +01:00
{
2021-03-12 16:47:40 +00:00
const TitleInfo * title_info_1 = * ( ( const TitleInfo * * ) a ) ;
2020-08-21 01:18:05 +01:00
const TitleInfo * title_info_2 = * ( ( const TitleInfo * * ) b ) ;
2022-07-05 02:04:28 +01:00
2020-08-21 01:18:05 +01:00
if ( title_info_1 - > meta_key . id < title_info_2 - > meta_key . id )
{
return - 1 ;
} else
if ( title_info_1 - > meta_key . id > title_info_2 - > meta_key . id )
{
return 1 ;
}
2022-07-05 02:04:28 +01:00
Preliminar 15.x support.
This commit uses my yet unmerged libnx PR to update ncm_types.h.
PoC code hasn't been updated yet, so proper support for DLC updates will arrive at a later time.
Note to self: implement a way to provide access to loaded DataPatch TitleInfo entries (linked list hell).
* bktr: renamed bktrBucketInitializeSubStorageReadParams to bktrInitializeSubStorageReadParams to avoid redundancy, added debug code to dump BucketInfo and BucketTree tables if BucketTree storage initialization fails.
* cnmt: updated ContentMetaAddOnContentMetaExtendedHeader struct to its 15.x equivalent, added ContentMetaLegacyAddOnContentMetaExtendedHeader struct, added ContentMetaDataPatchMetaExtendedHeader struct, updated the cnmtGetRequiredTitleId and cnmtGetRequiredTitleVersion functions to support DataPatch titles, updated cnmtInitializeContext to support both the new AddOnContent extended header and DataPatch titles, added debug code to dump the whole CNMT if context initialization fails, updated cnmtGenerateAuthoringToolXml to support DataPatch titles.
* keys: updated block hashes to match 15.x keyset, use case-insensitive comparison while looking for entry names in keysReadKeysFromFile, make sure the eticket_rsa_kek is non-zero before proceeding in keysGetDecryptedEticketRsaDeviceKey.
* nca: updated NcaKeyGeneration enum, added reminder about updating NcaSignatureKeyGeneration if necessary, replaced ncaFsSectionCheckHashRegionAccess with ncaFsSectionCheckPlaintextHashRegionAccess, removed all extents checks on Patch RomFS and sparse sections, updated ncaGetFsSectionTypeName to reflect if a FS section holds a sparse layer or not.
* nca_storage: updated ncaStorageInitializeContext to avoid initializing a compressed storage if a sparse layer is also used (fixes issues with Them's Fightin' Herds), updated ncaStorageSetPatchOriginalSubStorage to enforce the presence of a compressed storage in a patch if the base FS holds a compressed storage.
* npdm: added reminder about updating NpdmSignatureKeyGeneration if necessary, updated NpdmFsAccessControlFlags enum, updated NpdmAccessibility enum, updated NpdmSystemCallId enum, fixed typos.
* title: updated all relevant functions that deal with NcmContentMetaType values to also handle DataPatch titles, added functions to handle DataPatchId values, removed titleConvertNcmContentSizeToU64 and titleConvertU64ToNcmContentSize functions in favor of ncmContentInfoSizeToU64 and ncmU64ToContentInfoSize from my unmerged libnx PR, updated internal arrays to match 15.x changes, renamed titleOrphanTitleInfoSortFunction to titleInfoEntrySortFunction and updated it to also sort entries by version and storage ID, updated titleGenerateTitleInfoEntriesForTitleStorage to sort TitleInfo entries, simplified titleDuplicateTitleInfo a bit by using macros.
2022-10-23 15:44:47 +01:00
if ( title_info_1 - > version . value < title_info_2 - > version . value )
{
return - 1 ;
} else
if ( title_info_1 - > version . value > title_info_2 - > version . value )
{
return 1 ;
}
if ( title_info_1 - > storage_id < title_info_2 - > storage_id )
{
return - 1 ;
} else
if ( title_info_1 - > storage_id > title_info_2 - > storage_id )
{
return 1 ;
}
2020-08-21 01:18:05 +01:00
return 0 ;
}
2021-06-05 22:06:29 +01:00
static char * titleGetPatchVersionString ( TitleInfo * title_info )
{
NcmContentInfo * nacp_content = NULL ;
2023-05-24 20:05:34 +01:00
u8 storage_id = NcmStorageId_None , hfs_partition_type = HashFileSystemPartitionType_None ;
2021-06-05 22:06:29 +01:00
NcaContext * nca_ctx = NULL ;
NacpContext nacp_ctx = { 0 } ;
2021-06-08 17:10:41 +01:00
char display_version [ 0x11 ] = { 0 } , * str = NULL ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
if ( ! title_info | | title_info - > meta_key . type ! = NcmContentMetaType_Patch | | ! ( nacp_content = titleGetContentInfoByTypeAndIdOffset ( title_info , NcmContentType_Control , 0 ) ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-06-05 22:06:29 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
/* Update parameters. */
storage_id = title_info - > storage_id ;
2023-05-24 20:05:34 +01:00
if ( storage_id = = NcmStorageId_GameCard ) hfs_partition_type = HashFileSystemPartitionType_Secure ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
/* Allocate memory for the NCA context. */
nca_ctx = calloc ( 1 , sizeof ( NcaContext ) ) ;
if ( ! nca_ctx )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for NCA context! " ) ;
2021-06-05 22:06:29 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
/* Initialize NCA context. */
2023-05-30 00:22:12 +01:00
if ( ! ncaInitializeContext ( nca_ctx , storage_id , hfs_partition_type , & ( title_info - > meta_key ) , nacp_content , NULL ) )
2021-06-05 22:06:29 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to initialize NCA context for Control NCA from %016lX! " , title_info - > meta_key . id ) ;
2021-06-05 22:06:29 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
/* Initialize NACP context. */
if ( ! nacpInitializeContext ( & nacp_ctx , nca_ctx ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to initialize NACP context for %016lX! " , title_info - > meta_key . id ) ;
2021-06-05 22:06:29 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-08 17:10:41 +01:00
/* Get trimmed version string. */
snprintf ( display_version , sizeof ( display_version ) , " %s " , nacp_ctx . data - > display_version ) ;
utilsTrimString ( display_version ) ;
2022-07-05 02:04:28 +01:00
2021-06-08 17:10:41 +01:00
/* Check version string length. */
if ( ! * display_version )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Display version string from %016lX is empty! " , title_info - > meta_key . id ) ;
2021-06-08 17:10:41 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-08 17:10:41 +01:00
/* Duplicate version string. */
str = strdup ( display_version ) ;
2022-07-12 17:34:49 +01:00
if ( ! str ) LOG_MSG_ERROR ( " Failed to duplicate version string from %016lX! " , title_info - > meta_key . id ) ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
end :
nacpFreeContext ( & nacp_ctx ) ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
if ( nca_ctx ) free ( nca_ctx ) ;
2022-07-05 02:04:28 +01:00
2021-06-05 22:06:29 +01:00
return str ;
}