@ -123,6 +123,39 @@ bool IsVersionedExternalUpdateDisabled(const std::vector<std::string>& disabled,
return std : : find ( disabled . cbegin ( ) , disabled . cend ( ) , disabled_key ) ! = disabled . cend ( ) | |
std : : find ( disabled . cbegin ( ) , disabled . cend ( ) , " Update " ) ! = disabled . cend ( ) ;
}
std : : string GetUpdateVersionStringFromSlot ( const ContentProvider * provider , u64 update_tid ) {
if ( provider = = nullptr ) {
return { } ;
}
auto control_nca = provider - > GetEntry ( update_tid , ContentRecordType : : Control ) ;
if ( control_nca = = nullptr | |
control_nca - > GetStatus ( ) ! = Loader : : ResultStatus : : Success ) {
return { } ;
}
const auto romfs = control_nca - > GetRomFS ( ) ;
if ( romfs = = nullptr ) {
return { } ;
}
const auto extracted = ExtractRomFS ( romfs ) ;
if ( extracted = = nullptr ) {
return { } ;
}
auto nacp_file = extracted - > GetFile ( " control.nacp " ) ;
if ( nacp_file = = nullptr ) {
nacp_file = extracted - > GetFile ( " Control.nacp " ) ;
}
if ( nacp_file = = nullptr ) {
return { } ;
}
NACP nacp { nacp_file } ;
return nacp . GetVersionString ( ) ;
}
} // Anonymous namespace
PatchManager : : PatchManager ( u64 title_id_ ,
@ -771,6 +804,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std : : nullopt , std : : nullopt , ContentRecordType : : Program , update_tid ) ;
for ( const auto & [ slot , entry ] : all_updates ) {
( void ) entry ;
if ( slot = = ContentProviderUnionSlot : : External | |
slot = = ContentProviderUnionSlot : : FrontendManual ) {
continue ;
@ -786,7 +820,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
source_suffix = " (NAND) " ;
break ;
case ContentProviderUnionSlot : : SDMC :
source_type = PatchSource : : NAND ;
source_type = PatchSource : : SDMC ;
source_suffix = " (SDMC) " ;
break ;
default :
@ -795,21 +829,18 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std : : string version_str ;
u32 numeric_ver = 0 ;
PatchManager update { update_tid , fs_controller , content_provider } ;
const auto metadata = update . GetControlMetadata ( ) ;
const auto & nacp = metadata . first ;
if ( nacp ! = nullptr ) {
version_str = nacp - > GetVersionString ( ) ;
}
const auto * slot_provider = content_union - > GetSlotProvider ( slot ) ;
version_str = GetUpdateVersionStringFromSlot ( slot_provider , update_tid ) ;
const auto meta_ver = content_provider . GetEntryVersion ( update_tid ) ;
if ( meta_ver . has_value ( ) ) {
numeric_ver = * meta_ver ;
if ( slot_provider ! = nullptr ) {
const auto slot_ver = slot_provider - > GetEntryVersion ( update_tid ) ;
if ( slot_ver . has_value ( ) ) {
numeric_ver = * slot_ver ;
if ( version_str . empty ( ) & & numeric_ver ! = 0 ) {
version_str = FormatTitleVersion ( numeric_ver ) ;
}
}
}
std : : string patch_name = " Update " + source_suffix ;
@ -956,37 +987,60 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
}
// DLC
const auto dlc_entries =
content_provider . ListEntriesFilter ( TitleType : : AOC , ContentRecordType : : Data ) ;
std : : vector < ContentProviderEntry > dlc_match ;
dlc_match . reserve ( dlc_entries . size ( ) ) ;
std : : copy_if ( dlc_entries . begin ( ) , dlc_entries . end ( ) , std : : back_inserter ( dlc_match ) ,
[ this ] ( const ContentProviderEntry & entry ) {
bool has_external_dlc = false ;
bool has_nand_dlc = false ;
bool has_sdmc_dlc = false ;
bool has_other_dlc = false ;
const auto dlc_entries_with_origin =
content_union - > ListEntriesFilterOrigin ( std : : nullopt , TitleType : : AOC , ContentRecordType : : Data ) ;
dlc_match . reserve ( dlc_entries_with_origin . size ( ) ) ;
for ( const auto & [ slot , entry ] : dlc_entries_with_origin ) {
const auto base_tid = GetBaseTitleID ( entry . title_id ) ;
const bool matches_base = base_tid = = title_id ;
if ( ! matches_base ) {
LOG_DEBUG ( Loader , " DLC {:016X} base {:016X} doesn't match title {:016X} " ,
entry . title_id , base_tid , title_id ) ;
return fals e ;
continu e;
}
auto nca = content_provider . GetEntry ( entry ) ;
const auto * slot_provider = content_union - > GetSlotProvider ( slot ) ;
if ( slot_provider = = nullptr ) {
continue ;
}
auto nca = slot_provider - > GetEntry ( entry ) ;
if ( ! nca ) {
LOG_DEBUG ( Loader , " Failed to get NCA for DLC {:016X} " , entry . title_id ) ;
return false ;
continu e;
}
const auto status = nca - > GetStatus ( ) ;
if ( status ! = Loader : : ResultStatus : : Success ) {
LOG_DEBUG ( Loader , " DLC {:016X} NCA has status {} " ,
entry . title_id , static_cast < int > ( status ) ) ;
return fals e ;
LOG_DEBUG ( Loader , " DLC {:016X} NCA has status {} " , entry . title_id ,
static_cast < int > ( status ) ) ;
continu e;
}
return true ;
} ) ;
switch ( slot ) {
case ContentProviderUnionSlot : : External :
case ContentProviderUnionSlot : : FrontendManual :
has_external_dlc = true ;
break ;
case ContentProviderUnionSlot : : UserNAND :
case ContentProviderUnionSlot : : SysNAND :
has_nand_dlc = true ;
break ;
case ContentProviderUnionSlot : : SDMC :
has_sdmc_dlc = true ;
break ;
default :
has_other_dlc = true ;
break ;
}
dlc_match . push_back ( entry ) ;
}
if ( ! dlc_match . empty ( ) ) {
// Ensure sorted so DLC IDs show in order.
@ -1000,13 +1054,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
const auto dlc_disabled =
std : : find ( disabled . begin ( ) , disabled . end ( ) , " DLC " ) ! = disabled . end ( ) ;
PatchSource dlc_source = PatchSource : : Unknown ;
if ( has_external_dlc & & ! has_nand_dlc & & ! has_sdmc_dlc & & ! has_other_dlc ) {
dlc_source = PatchSource : : External ;
} else if ( has_nand_dlc & & ! has_external_dlc & & ! has_sdmc_dlc & & ! has_other_dlc ) {
dlc_source = PatchSource : : NAND ;
} else if ( has_sdmc_dlc & & ! has_external_dlc & & ! has_nand_dlc & & ! has_other_dlc ) {
dlc_source = PatchSource : : SDMC ;
}
out . push_back ( { . enabled = ! dlc_disabled ,
. name = " DLC " ,
. version = std : : move ( list ) ,
. type = PatchType : : DLC ,
. program_id = title_id ,
. title_id = dlc_match . back ( ) . title_id ,
. source = PatchSource : : Unknown } ) ;
. source = dlc_source } ) ;
}
return out ;