registration: Add support for force overwrite of installed
This commit is contained in:
parent
fdf27bf390
commit
6b76b77400
|
@ -60,7 +60,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
|
||||||
auto index = static_cast<size_t>(type);
|
auto index = static_cast<size_t>(type);
|
||||||
// If the index is after the jump in TitleType, subtract it out.
|
// If the index is after the jump in TitleType, subtract it out.
|
||||||
if (index >= static_cast<size_t>(TitleType::Application))
|
if (index >= static_cast<size_t>(TitleType::Application))
|
||||||
index -= static_cast<size_t>(TitleType::Application);
|
index -= 0x7B;
|
||||||
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
|
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +343,8 @@ static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const N
|
||||||
return iter == xci->GetNCAs().end() ? nullptr : *iter;
|
return iter == xci->GetNCAs().end() ? nullptr : *iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFunction& copy) {
|
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
|
||||||
|
const VfsCopyFunction& copy) {
|
||||||
const auto& ncas = xci->GetNCAs();
|
const auto& ncas = xci->GetNCAs();
|
||||||
const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
|
const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
|
||||||
return nca->GetType() == NCAContentType::Meta;
|
return nca->GetType() == NCAContentType::Meta;
|
||||||
|
@ -352,14 +353,16 @@ bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFuncti
|
||||||
if (meta_iter == ncas.end()) {
|
if (meta_iter == ncas.end()) {
|
||||||
LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
|
LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
|
||||||
"is therefore malformed. Double check your encryption keys.");
|
"is therefore malformed. Double check your encryption keys.");
|
||||||
return false;
|
return InstallResult::ErrorMetaFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install Metadata File
|
// Install Metadata File
|
||||||
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
|
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
|
||||||
const auto meta_id = HexStringToArray<16>(meta_id_raw);
|
const auto meta_id = HexStringToArray<16>(meta_id_raw);
|
||||||
if (!RawInstallNCA(*meta_iter, copy, meta_id))
|
|
||||||
return false;
|
const auto res = RawInstallNCA(*meta_iter, copy, overwrite_if_exists, meta_id);
|
||||||
|
if (res != InstallResult::Success)
|
||||||
|
return res;
|
||||||
|
|
||||||
// Install all the other NCAs
|
// Install all the other NCAs
|
||||||
const auto section0 = (*meta_iter)->GetSubdirectories()[0];
|
const auto section0 = (*meta_iter)->GetSubdirectories()[0];
|
||||||
|
@ -367,16 +370,19 @@ bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFuncti
|
||||||
const CNMT cnmt(cnmt_file);
|
const CNMT cnmt(cnmt_file);
|
||||||
for (const auto& record : cnmt.GetContentRecords()) {
|
for (const auto& record : cnmt.GetContentRecords()) {
|
||||||
const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
|
const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
|
||||||
if (nca == nullptr || !RawInstallNCA(nca, copy, record.nca_id))
|
if (nca == nullptr)
|
||||||
return false;
|
return InstallResult::ErrorCopyFailed;
|
||||||
|
const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
|
||||||
|
if (res2 != InstallResult::Success)
|
||||||
|
return res2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Refresh();
|
Refresh();
|
||||||
return true;
|
return InstallResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
|
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
|
||||||
const VfsCopyFunction& copy) {
|
bool overwrite_if_exists, const VfsCopyFunction& copy) {
|
||||||
CNMTHeader header{
|
CNMTHeader header{
|
||||||
nca->GetTitleId(), ///< Title ID
|
nca->GetTitleId(), ///< Title ID
|
||||||
0, ///< Ignore/Default title version
|
0, ///< Ignore/Default title version
|
||||||
|
@ -393,10 +399,13 @@ bool RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
|
||||||
mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0);
|
mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0);
|
||||||
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
||||||
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
||||||
return RawInstallYuzuMeta(new_cnmt) && RawInstallNCA(nca, copy, c_rec.nca_id);
|
if (!RawInstallYuzuMeta(new_cnmt))
|
||||||
|
return InstallResult::ErrorMetaFailed;
|
||||||
|
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
||||||
|
bool overwrite_if_exists,
|
||||||
boost::optional<NcaID> override_id) {
|
boost::optional<NcaID> override_id) {
|
||||||
const auto in = nca->GetBaseFile();
|
const auto in = nca->GetBaseFile();
|
||||||
Core::Crypto::SHA256Hash hash{};
|
Core::Crypto::SHA256Hash hash{};
|
||||||
|
@ -416,15 +425,22 @@ bool RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunct
|
||||||
|
|
||||||
std::string path = GetRelativePathFromNcaID(id, false, true);
|
std::string path = GetRelativePathFromNcaID(id, false, true);
|
||||||
|
|
||||||
if (GetFileAtID(id) != nullptr) {
|
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
|
||||||
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
|
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
|
||||||
return false;
|
return InstallResult::ErrorAlreadyExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetFileAtID(id) != nullptr) {
|
||||||
|
LOG_WARNING(Loader, "Overwriting existing NCA...");
|
||||||
|
VirtualDir c_dir;
|
||||||
|
{ c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
|
||||||
|
c_dir->DeleteFile(FileUtil::GetFilename(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = dir->CreateFileRelative(path);
|
auto out = dir->CreateFileRelative(path);
|
||||||
if (out == nullptr)
|
if (out == nullptr)
|
||||||
return false;
|
return InstallResult::ErrorCopyFailed;
|
||||||
return copy(in, out);
|
return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
|
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
|
||||||
|
|
|
@ -25,6 +25,13 @@ using NcaID = std::array<u8, 0x10>;
|
||||||
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
|
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
|
||||||
using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
|
using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
|
||||||
|
|
||||||
|
enum class InstallResult {
|
||||||
|
Success,
|
||||||
|
ErrorAlreadyExists,
|
||||||
|
ErrorCopyFailed,
|
||||||
|
ErrorMetaFailed,
|
||||||
|
};
|
||||||
|
|
||||||
struct RegisteredCacheEntry {
|
struct RegisteredCacheEntry {
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
ContentRecordType type;
|
ContentRecordType type;
|
||||||
|
@ -77,13 +84,15 @@ public:
|
||||||
|
|
||||||
// Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
|
// Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
|
||||||
// is a meta NCA and all of them are accessible.
|
// is a meta NCA and all of them are accessible.
|
||||||
bool InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFunction& copy = &VfsRawCopy);
|
InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
|
||||||
|
const VfsCopyFunction& copy = &VfsRawCopy);
|
||||||
|
|
||||||
// Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
|
// Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
|
||||||
// poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
|
// poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
|
||||||
// dir inside the NAND called 'yuzu_meta' and store the raw CNMT there.
|
// dir inside the NAND called 'yuzu_meta' and store the raw CNMT there.
|
||||||
// TODO(DarkLordZach): Author real meta-type NCAs and install those.
|
// TODO(DarkLordZach): Author real meta-type NCAs and install those.
|
||||||
bool InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
|
InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
|
||||||
|
bool overwrite_if_exists = false,
|
||||||
const VfsCopyFunction& copy = &VfsRawCopy);
|
const VfsCopyFunction& copy = &VfsRawCopy);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -97,7 +106,8 @@ private:
|
||||||
boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
|
boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
|
||||||
VirtualFile GetFileAtID(NcaID id) const;
|
VirtualFile GetFileAtID(NcaID id) const;
|
||||||
VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
|
VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
|
||||||
bool RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
||||||
|
bool overwrite_if_exists,
|
||||||
boost::optional<NcaID> override_id = boost::none);
|
boost::optional<NcaID> override_id = boost::none);
|
||||||
bool RawInstallYuzuMeta(const CNMT& cnmt);
|
bool RawInstallYuzuMeta(const CNMT& cnmt);
|
||||||
|
|
||||||
|
|
|
@ -405,7 +405,6 @@ void GameList::RefreshGameDirectory() {
|
||||||
|
|
||||||
static void GetMetadataFromControlNCA(const std::shared_ptr<FileSys::NCA>& nca,
|
static void GetMetadataFromControlNCA(const std::shared_ptr<FileSys::NCA>& nca,
|
||||||
std::vector<u8>& icon, std::string& name) {
|
std::vector<u8>& icon, std::string& name) {
|
||||||
|
|
||||||
const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
|
const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
|
||||||
if (control_dir == nullptr)
|
if (control_dir == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -650,37 +650,59 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auto success = [this]() {
|
||||||
|
QMessageBox::information(this, tr("Successfully Installed"),
|
||||||
|
tr("The file was successfully installed."));
|
||||||
|
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto failed = [this]() {
|
||||||
|
QMessageBox::warning(
|
||||||
|
this, tr("Failed to Install"),
|
||||||
|
tr("There was an error while attempting to install the provided file. It "
|
||||||
|
"could have an incorrect format or be missing metadata. Please "
|
||||||
|
"double-check your file and try again."));
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto overwrite = [this]() {
|
||||||
|
return QMessageBox::question(this, "Failed to Install",
|
||||||
|
"The file you are attempting to install already exists "
|
||||||
|
"in the cache. Would you like to overwrite it?") ==
|
||||||
|
QMessageBox::Yes;
|
||||||
|
};
|
||||||
|
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
if (filename.endsWith("xci", Qt::CaseInsensitive)) {
|
if (filename.endsWith("xci", Qt::CaseInsensitive)) {
|
||||||
const auto xci = std::make_shared<FileSys::XCI>(
|
const auto xci = std::make_shared<FileSys::XCI>(
|
||||||
vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
|
vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
|
||||||
if (xci->GetStatus() != Loader::ResultStatus::Success) {
|
if (xci->GetStatus() != Loader::ResultStatus::Success) {
|
||||||
QMessageBox::warning(
|
failed();
|
||||||
this, tr("Failed to Install XCI"),
|
|
||||||
tr("The XCI file you provided is invalid. Please double-check your encryption "
|
|
||||||
"keys and the file and try again."));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, qt_raw_copy)) {
|
const auto res =
|
||||||
QMessageBox::information(this, tr("Successfully Installed XCI"),
|
Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy);
|
||||||
tr("The file was successfully installed."));
|
if (res == FileSys::InstallResult::Success) {
|
||||||
game_list->PopulateAsync(UISettings::values.gamedir,
|
success();
|
||||||
UISettings::values.gamedir_deepscan);
|
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(
|
if (res == FileSys::InstallResult::ErrorAlreadyExists) {
|
||||||
this, tr("Failed to Install XCI"),
|
if (overwrite()) {
|
||||||
tr("There was an error while attempting to install the provided XCI file. It "
|
const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
|
||||||
"could have an incorrect format or be missing a metadata entry. Please "
|
xci, true, qt_raw_copy);
|
||||||
"double-check your file and try again."));
|
if (res2 == FileSys::InstallResult::Success) {
|
||||||
|
success();
|
||||||
|
} else {
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const auto nca = std::make_shared<FileSys::NCA>(
|
const auto nca = std::make_shared<FileSys::NCA>(
|
||||||
vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
|
vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
|
||||||
if (nca->GetStatus() != Loader::ResultStatus::Success) {
|
if (nca->GetStatus() != Loader::ResultStatus::Success) {
|
||||||
QMessageBox::warning(
|
failed();
|
||||||
this, tr("Failed to Install NCA"),
|
|
||||||
tr("The NCA file you provided is invalid. Please double-check your encryption "
|
|
||||||
"keys and the file and try again."));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -702,7 +724,7 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||||
|
|
||||||
auto index = tt_options.indexOf(item);
|
auto index = tt_options.indexOf(item);
|
||||||
if (!ok || index == -1) {
|
if (!ok || index == -1) {
|
||||||
QMessageBox::warning(this, tr("Failed to Install NCA"),
|
QMessageBox::warning(this, tr("Failed to Install"),
|
||||||
tr("The title type you selected for the NCA is invalid."));
|
tr("The title type you selected for the NCA is invalid."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -710,18 +732,24 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||||
if (index >= 5)
|
if (index >= 5)
|
||||||
index += 0x7B;
|
index += 0x7B;
|
||||||
|
|
||||||
if (Service::FileSystem::GetUserNANDContents()->InstallEntry(
|
const auto res = Service::FileSystem::GetUserNANDContents()->InstallEntry(
|
||||||
nca, static_cast<FileSys::TitleType>(index), qt_raw_copy)) {
|
nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
|
||||||
QMessageBox::information(this, tr("Successfully Installed NCA"),
|
if (res == FileSys::InstallResult::Success) {
|
||||||
tr("The file was successfully installed."));
|
success();
|
||||||
game_list->PopulateAsync(UISettings::values.gamedir,
|
|
||||||
UISettings::values.gamedir_deepscan);
|
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(this, tr("Failed to Install NCA"),
|
if (res == FileSys::InstallResult::ErrorAlreadyExists) {
|
||||||
tr("There was an error while attempting to install the "
|
if (overwrite()) {
|
||||||
"provided NCA file. An error might have occured creating "
|
const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
|
||||||
"the metadata file or parsing the NCA. Please "
|
nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
|
||||||
"double-check your file and try again."));
|
if (res2 == FileSys::InstallResult::Success) {
|
||||||
|
success();
|
||||||
|
} else {
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue