qt: Add support for dumping a DLC Data RomFS

This commit is contained in:
Zach Hilman 2018-10-17 18:27:23 -04:00
parent 59044862a9
commit 9d0fb0f815
4 changed files with 73 additions and 11 deletions

View File

@ -35,6 +35,10 @@ bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
} }
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return !operator==(lhs, rhs);
}
static bool FollowsTwoDigitDirFormat(std::string_view name) { static bool FollowsTwoDigitDirFormat(std::string_view name) {
static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
std::regex_constants::icase); std::regex_constants::icase);

View File

@ -52,6 +52,7 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
// std unique requires operator== to identify duplicates. // std unique requires operator== to identify duplicates.
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
/* /*
* A class that catalogues NCAs in the registered directory structure. * A class that catalogues NCAs in the registered directory structure.

View File

@ -100,6 +100,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
} }
#endif #endif
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
/** /**
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
* is a bitfield "callout_flags" options, used to track if a message has already been shown to the * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
@ -823,14 +825,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
} }
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
const auto path = fmt::format("{}{:016X}/romfs", const auto failed = [this] {
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
const auto failed = [this, &path] {
QMessageBox::warning(this, tr("RomFS Extraction Failed!"), QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
tr("There was an error copying the RomFS files or the user " tr("There was an error copying the RomFS files or the user "
"cancelled the operation.")); "cancelled the operation."));
vfs->DeleteDirectory(path);
}; };
const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
@ -845,10 +843,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
return; return;
} }
const auto romfs = const auto installed = Service::FileSystem::GetUnionContents();
loader->IsRomFSUpdatable() auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset())
: file; if (!romfs_title_id) {
failed();
return;
}
const auto path = fmt::format(
"{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
FileSys::VirtualFile romfs;
if (*romfs_title_id == program_id) {
romfs = file;
} else {
romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
}
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
if (extracted == nullptr) { if (extracted == nullptr) {
@ -860,6 +872,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
if (out == nullptr) { if (out == nullptr) {
failed(); failed();
vfs->DeleteDirectory(path);
return; return;
} }
@ -870,8 +883,11 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
"files into the new directory while <br>skeleton will only create the directory " "files into the new directory while <br>skeleton will only create the directory "
"structure."), "structure."),
{"Full", "Skeleton"}, 0, false, &ok); {"Full", "Skeleton"}, 0, false, &ok);
if (!ok) if (!ok) {
failed(); failed();
vfs->DeleteDirectory(path);
return;
}
const auto full = res == "Full"; const auto full = res == "Full";
const auto entry_size = CalculateRomFSEntrySize(extracted, full); const auto entry_size = CalculateRomFSEntrySize(extracted, full);
@ -888,6 +904,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
} else { } else {
progress.close(); progress.close();
failed(); failed();
vfs->DeleteDirectory(path);
} }
} }
@ -1459,6 +1476,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
} }
} }
boost::optional<u64> GMainWindow::SelectRomFSDumpTarget(
const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
const auto dlc_entries =
installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
std::vector<FileSys::RegisteredCacheEntry> 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::RegisteredCacheEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == 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{"Base"};
for (std::size_t i = 1; i < romfs_tids.size(); ++i)
list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
bool ok;
const auto res = QInputDialog::getItem(
this, tr("Select RomFS Dump Target"),
tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
if (!ok) {
return boost::none;
}
return romfs_tids[list.indexOf(res)];
}
return program_id;
}
bool GMainWindow::ConfirmClose() { bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true; return true;

View File

@ -10,6 +10,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QTimer> #include <QTimer>
#include <boost/optional.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core.h" #include "core/core.h"
#include "ui_main.h" #include "ui_main.h"
@ -29,8 +30,9 @@ class WaitTreeWidget;
enum class GameListOpenTarget; enum class GameListOpenTarget;
namespace FileSys { namespace FileSys {
class RegisteredCacheUnion;
class VfsFilesystem; class VfsFilesystem;
} } // namespace FileSys
namespace Tegra { namespace Tegra {
class DebugContext; class DebugContext;
@ -175,6 +177,8 @@ private slots:
void OnReinitializeKeys(ReinitializeKeyBehavior behavior); void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
private: private:
boost::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&,
u64 program_id);
void UpdateStatusBar(); void UpdateStatusBar();
Ui::MainWindow ui; Ui::MainWindow ui;