summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys/registered_cache.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/file_sys/registered_cache.cpp217
1 files changed, 197 insertions, 20 deletions
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 3946ff871..ac3fbd849 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <random>
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
@@ -48,18 +49,22 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
static bool FollowsNcaIdFormat(std::string_view name) {
static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
std::regex_constants::icase);
- return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex);
+ static const std::regex nca_id_cnmt_regex(
+ "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
+ return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
+ (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
}
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
- bool within_two_digit) {
+ bool within_two_digit, bool cnmt_suffix) {
if (!within_two_digit)
- return fmt::format("/{}.nca", Common::HexArrayToString(nca_id, second_hex_upper));
+ return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
+ Common::HexToString(nca_id, second_hex_upper));
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
- return fmt::format("/000000{:02X}/{}.nca", hash[0],
- Common::HexArrayToString(nca_id, second_hex_upper));
+ return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
+ Common::HexToString(nca_id, second_hex_upper));
}
static std::string GetCNMTName(TitleType type, u64 title_id) {
@@ -98,7 +103,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
return ContentRecordType::Data;
case NCAContentType::Manual:
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
- return ContentRecordType::Manual;
+ return ContentRecordType::HtmlDocument;
default:
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
}
@@ -126,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
+PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
+
+bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+ if (dir->GetFileRelative(path) != nullptr) {
+ return false;
+ }
+
+ Core::Crypto::SHA256Hash hash{};
+ mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
+ const auto dirname = fmt::format("000000{:02X}", hash[0]);
+
+ const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
+
+ if (dir2 == nullptr)
+ return false;
+
+ const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
+
+ if (file == nullptr)
+ return false;
+
+ return file->Resize(size);
+}
+
+bool PlaceholderCache::Delete(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+ if (dir->GetFileRelative(path) == nullptr) {
+ return false;
+ }
+
+ Core::Crypto::SHA256Hash hash{};
+ mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
+ const auto dirname = fmt::format("000000{:02X}", hash[0]);
+
+ const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
+
+ const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
+
+ return res;
+}
+
+bool PlaceholderCache::Exists(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+ return dir->GetFileRelative(path) != nullptr;
+}
+
+bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return false;
+
+ return file->WriteBytes(data, offset) == data.size();
+}
+
+bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
+ const NcaID& install) const {
+ const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return false;
+
+ const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
+
+ if (res != InstallResult::Success)
+ return false;
+
+ return Delete(placeholder);
+}
+
+bool PlaceholderCache::CleanAll() const {
+ return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
+}
+
+std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return std::nullopt;
+
+ NCA nca{file};
+
+ if (nca.GetStatus() != Loader::ResultStatus::Success &&
+ nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
+ return std::nullopt;
+ }
+
+ const auto rights_id = nca.GetRightsId();
+ if (rights_id == NcaID{})
+ return std::nullopt;
+
+ return rights_id;
+}
+
+u64 PlaceholderCache::Size(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return 0;
+
+ return file->GetSize();
+}
+
+bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return false;
+
+ return file->Resize(new_size);
+}
+
+std::vector<NcaID> PlaceholderCache::List() const {
+ std::vector<NcaID> out;
+ for (const auto& sdir : dir->GetSubdirectories()) {
+ for (const auto& file : sdir->GetFiles()) {
+ const auto name = file->GetName();
+ if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
+ name[35] == 'a') {
+ out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
+ }
+ }
+ }
+ return out;
+}
+
+NcaID PlaceholderCache::Generate() {
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
+
+ NcaID out{};
+
+ const auto v1 = distribution(gen);
+ const auto v2 = distribution(gen);
+ std::memcpy(out.data(), &v1, sizeof(u64));
+ std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
+
+ return out;
+}
+
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
@@ -168,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
VirtualFile file;
- // Try all four modes of file storage:
- // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
- // 00: /000000**/{:032X}.nca
- // 01: /{:032X}.nca
- // 10: /000000**/{:032x}.nca
- // 11: /{:032x}.nca
- for (u8 i = 0; i < 4; ++i) {
- const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
+ // Try all five relevant modes of file storage:
+ // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
+ // 000: /000000**/{:032X}.nca
+ // 010: /{:032X}.nca
+ // 100: /000000**/{:032x}.nca
+ // 110: /{:032x}.nca
+ // 111: /{:032x}.cnmt.nca
+ for (u8 i = 0; i < 8; ++i) {
+ if ((i % 2) == 1 && i != 7)
+ continue;
+ const auto path =
+ GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
file = OpenFileOrDirectoryConcat(dir, path);
if (file != nullptr)
return file;
@@ -376,10 +535,11 @@ std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
}
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
- const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
- if (file == nullptr)
+ auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false)));
+ if (file == nullptr) {
return nullptr;
- return std::make_shared<NCA>(file);
+ }
+ return std::make_shared<NCA>(std::move(file));
}
InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
@@ -395,8 +555,8 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
});
if (meta_iter == ncas.end()) {
- 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.");
+ LOG_ERROR(Loader, "The file you are attempting to install does not have a metadata NCA and "
+ "is therefore malformed. Check your encryption keys.");
return InstallResult::ErrorMetaFailed;
}
@@ -413,6 +573,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
for (const auto& record : cnmt.GetContentRecords()) {
+ // Ignore DeltaFragments, they are not useful to us
+ if (record.type == ContentRecordType::DeltaFragment)
+ continue;
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
@@ -467,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
memcpy(id.data(), hash.data(), 16);
}
- std::string path = GetRelativePathFromNcaID(id, false, true);
+ std::string path = GetRelativePathFromNcaID(id, false, true, false);
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
@@ -643,6 +806,20 @@ ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnion
return out;
}
+std::optional<ContentProviderUnionSlot> ContentProviderUnion::GetSlotForEntry(
+ u64 title_id, ContentRecordType type) const {
+ const auto iter =
+ std::find_if(providers.begin(), providers.end(), [title_id, type](const auto& provider) {
+ return provider.second != nullptr && provider.second->HasEntry(title_id, type);
+ });
+
+ if (iter == providers.end()) {
+ return std::nullopt;
+ }
+
+ return iter->first;
+}
+
ManualContentProvider::~ManualContentProvider() = default;
void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,