|
|
|
@ -2581,50 +2581,48 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
FileSys::VirtualFile base_romfs; |
|
|
|
if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) { |
|
|
|
failed(); |
|
|
|
return; |
|
|
|
} |
|
|
|
FileSys::VirtualFile packed_update_raw{}; |
|
|
|
loader->ReadUpdateRaw(packed_update_raw); |
|
|
|
|
|
|
|
const auto& installed = system->GetContentProvider(); |
|
|
|
const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); |
|
|
|
|
|
|
|
if (!romfs_title_id) { |
|
|
|
u64 title_id{}; |
|
|
|
u8 raw_type{}; |
|
|
|
if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) { |
|
|
|
failed(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program |
|
|
|
: FileSys::ContentRecordType::Data; |
|
|
|
const auto base_nca = installed.GetEntry(*romfs_title_id, type); |
|
|
|
const auto type = static_cast<FileSys::ContentRecordType>(raw_type); |
|
|
|
const auto base_nca = installed.GetEntry(title_id, type); |
|
|
|
if (!base_nca) { |
|
|
|
failed(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const FileSys::NCA update_nca{packed_update_raw, nullptr}; |
|
|
|
if (type != FileSys::ContentRecordType::Program || |
|
|
|
update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS || |
|
|
|
update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) { |
|
|
|
packed_update_raw = {}; |
|
|
|
} |
|
|
|
|
|
|
|
const auto base_romfs = base_nca->GetRomFS(); |
|
|
|
if (!base_romfs) { |
|
|
|
failed(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const auto dump_dir = |
|
|
|
target == DumpRomFSTarget::Normal |
|
|
|
? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) |
|
|
|
: Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; |
|
|
|
const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); |
|
|
|
const auto romfs_dir = fmt::format("{:016X}/romfs", title_id); |
|
|
|
|
|
|
|
const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); |
|
|
|
|
|
|
|
FileSys::VirtualFile romfs; |
|
|
|
|
|
|
|
if (*romfs_title_id == program_id) { |
|
|
|
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), installed}; |
|
|
|
romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, nullptr, false); |
|
|
|
} else { |
|
|
|
romfs = installed.GetEntry(*romfs_title_id, type)->GetRomFS(); |
|
|
|
} |
|
|
|
|
|
|
|
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); |
|
|
|
if (extracted == nullptr) { |
|
|
|
failed(); |
|
|
|
return; |
|
|
|
} |
|
|
|
const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; |
|
|
|
auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); |
|
|
|
|
|
|
|
const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); |
|
|
|
|
|
|
|
@ -2648,6 +2646,12 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); |
|
|
|
if (extracted == nullptr) { |
|
|
|
failed(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const auto full = res == selections.constFirst(); |
|
|
|
const auto entry_size = CalculateRomFSEntrySize(extracted, full); |
|
|
|
|
|
|
|
@ -4358,28 +4362,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() { |
|
|
|
return mii_nca->GetRomFS().get() != nullptr; |
|
|
|
} |
|
|
|
|
|
|
|
std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, |
|
|
|
u64 program_id) { |
|
|
|
const auto dlc_entries = |
|
|
|
installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); |
|
|
|
std::vector<FileSys::ContentProviderEntry> dlc_match; |
|
|
|
dlc_match.reserve(dlc_entries.size()); |
|
|
|
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), |
|
|
|
[&program_id, &installed](const FileSys::ContentProviderEntry& entry) { |
|
|
|
return FileSys::GetBaseTitleID(entry.title_id) == program_id && |
|
|
|
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; |
|
|
|
}); |
|
|
|
|
|
|
|
std::vector<u64> romfs_tids; |
|
|
|
romfs_tids.push_back(program_id); |
|
|
|
for (const auto& entry : dlc_match) { |
|
|
|
romfs_tids.push_back(entry.title_id); |
|
|
|
} |
|
|
|
|
|
|
|
if (romfs_tids.size() > 1) { |
|
|
|
QStringList list{QStringLiteral("Base")}; |
|
|
|
for (std::size_t i = 1; i < romfs_tids.size(); ++i) { |
|
|
|
list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); |
|
|
|
bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, |
|
|
|
u64* selected_title_id, u8* selected_content_record_type) { |
|
|
|
using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>; |
|
|
|
boost::container::flat_map<u64, ContentInfo> available_title_ids; |
|
|
|
|
|
|
|
const auto RetrieveEntries = [&](FileSys::TitleType title_type, |
|
|
|
FileSys::ContentRecordType record_type) { |
|
|
|
const auto entries = installed.ListEntriesFilter(title_type, record_type); |
|
|
|
for (const auto& entry : entries) { |
|
|
|
if (FileSys::GetBaseTitleID(entry.title_id) == program_id && |
|
|
|
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) { |
|
|
|
available_title_ids[entry.title_id] = {title_type, record_type}; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); |
|
|
|
RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); |
|
|
|
|
|
|
|
if (available_title_ids.empty()) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
size_t title_index = 0; |
|
|
|
|
|
|
|
if (available_title_ids.size() > 1) { |
|
|
|
QStringList list; |
|
|
|
for (auto& [title_id, content_info] : available_title_ids) { |
|
|
|
const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id)); |
|
|
|
if (content_info.first == FileSys::TitleType::Application) { |
|
|
|
list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id)); |
|
|
|
} else { |
|
|
|
list.push_back( |
|
|
|
QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool ok; |
|
|
|
@ -4387,13 +4404,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv |
|
|
|
this, tr("Select RomFS Dump Target"), |
|
|
|
tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); |
|
|
|
if (!ok) { |
|
|
|
return {}; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return romfs_tids[list.indexOf(res)]; |
|
|
|
title_index = list.indexOf(res); |
|
|
|
} |
|
|
|
|
|
|
|
return program_id; |
|
|
|
const auto selected_info = available_title_ids.nth(title_index); |
|
|
|
*selected_title_id = selected_info->first; |
|
|
|
*selected_content_record_type = static_cast<u8>(selected_info->second.second); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool GMainWindow::ConfirmClose() { |
|
|
|
|