registered_cache: Implement PlaceholderCache to manage placeholder and installing content
This commit is contained in:
parent
256a50ad15
commit
8500ca797f
@ -127,6 +127,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
|
|||||||
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
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::HexArrayToString(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::HexArrayToString(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,
|
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||||
std::string_view path) const {
|
std::string_view path) const {
|
||||||
const auto file = dir->GetFileRelative(path);
|
const auto file = dir->GetFileRelative(path);
|
||||||
|
@ -25,6 +25,8 @@ enum class NCAContentType : u8;
|
|||||||
enum class TitleType : u8;
|
enum class TitleType : u8;
|
||||||
|
|
||||||
struct ContentRecord;
|
struct ContentRecord;
|
||||||
|
struct MetaRecord;
|
||||||
|
class RegisteredCache;
|
||||||
|
|
||||||
using NcaID = std::array<u8, 0x10>;
|
using NcaID = std::array<u8, 0x10>;
|
||||||
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
|
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
|
||||||
@ -89,6 +91,27 @@ protected:
|
|||||||
Core::Crypto::KeyManager keys;
|
Core::Crypto::KeyManager keys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PlaceholderCache {
|
||||||
|
public:
|
||||||
|
explicit PlaceholderCache(VirtualDir dir);
|
||||||
|
|
||||||
|
bool Create(const NcaID& id, u64 size) const;
|
||||||
|
bool Delete(const NcaID& id) const;
|
||||||
|
bool Exists(const NcaID& id) const;
|
||||||
|
bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
|
||||||
|
bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
|
||||||
|
bool CleanAll() const;
|
||||||
|
std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
|
||||||
|
u64 Size(const NcaID& id) const;
|
||||||
|
bool SetSize(const NcaID& id, u64 new_size) const;
|
||||||
|
std::vector<NcaID> List() const;
|
||||||
|
|
||||||
|
static NcaID Generate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
VirtualDir dir;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A class that catalogues NCAs in the registered directory structure.
|
* A class that catalogues NCAs in the registered directory structure.
|
||||||
* Nintendo's registered format follows this structure:
|
* Nintendo's registered format follows this structure:
|
||||||
@ -103,6 +126,8 @@ protected:
|
|||||||
* when 4GB splitting can be ignored.)
|
* when 4GB splitting can be ignored.)
|
||||||
*/
|
*/
|
||||||
class RegisteredCache : public ContentProvider {
|
class RegisteredCache : public ContentProvider {
|
||||||
|
friend class PlaceholderCache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Parsing function defines the conversion from raw file to NCA. If there are other steps
|
// Parsing function defines the conversion from raw file to NCA. If there are other steps
|
||||||
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
|
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
|
||||||
|
Loading…
Reference in New Issue
Block a user