Browse Source
frontend_common: Add content manager utility functions
frontend_common: Add content manager utility functions
Creates utility functions to remove/install DLC, updates, and base game contentpull/15/merge
12 changed files with 318 additions and 221 deletions
-
19src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
-
15src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt
-
16src/android/app/src/main/jni/android_common/android_common.cpp
-
7src/android/app/src/main/jni/android_common/android_common.h
-
46src/android/app/src/main/jni/id_cache.cpp
-
8src/android/app/src/main/jni/id_cache.h
-
80src/android/app/src/main/jni/native.cpp
-
2src/android/app/src/main/jni/native.h
-
1src/frontend_common/CMakeLists.txt
-
168src/frontend_common/content_manager.h
-
166src/yuzu/main.cpp
-
11src/yuzu/main.h
@ -0,0 +1,15 @@ |
|||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.model |
|||
|
|||
enum class InstallResult(val int: Int) { |
|||
Success(0), |
|||
Overwrite(1), |
|||
Failure(2), |
|||
BaseInstallAttempted(3); |
|||
|
|||
companion object { |
|||
fun from(int: Int): InstallResult = entries.firstOrNull { it.int == int } ?: Success |
|||
} |
|||
} |
|||
@ -0,0 +1,168 @@ |
|||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <boost/algorithm/string.hpp> |
|||
#include "common/common_types.h" |
|||
#include "common/literals.h" |
|||
#include "core/core.h" |
|||
#include "core/file_sys/common_funcs.h" |
|||
#include "core/file_sys/content_archive.h" |
|||
#include "core/file_sys/mode.h" |
|||
#include "core/file_sys/nca_metadata.h" |
|||
#include "core/file_sys/registered_cache.h" |
|||
#include "core/file_sys/submission_package.h" |
|||
#include "core/hle/service/filesystem/filesystem.h" |
|||
#include "core/loader/loader.h" |
|||
|
|||
namespace ContentManager { |
|||
|
|||
enum class InstallResult { |
|||
Success, |
|||
Overwrite, |
|||
Failure, |
|||
BaseInstallAttempted, |
|||
}; |
|||
|
|||
inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_controller, |
|||
const u64 title_id) { |
|||
return fs_controller.GetUserNANDContents()->RemoveExistingEntry(title_id) || |
|||
fs_controller.GetSDMCContents()->RemoveExistingEntry(title_id); |
|||
} |
|||
|
|||
inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) { |
|||
size_t count{}; |
|||
const auto& fs_controller = system->GetFileSystemController(); |
|||
const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( |
|||
FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); |
|||
std::vector<u64> program_dlc_entries; |
|||
|
|||
for (const auto& entry : dlc_entries) { |
|||
if (FileSys::GetBaseTitleID(entry.title_id) == program_id) { |
|||
program_dlc_entries.push_back(entry.title_id); |
|||
} |
|||
} |
|||
|
|||
for (const auto& entry : program_dlc_entries) { |
|||
if (RemoveDLC(fs_controller, entry)) { |
|||
++count; |
|||
} |
|||
} |
|||
return count; |
|||
} |
|||
|
|||
inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller, |
|||
const u64 program_id) { |
|||
const auto update_id = program_id | 0x800; |
|||
return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || |
|||
fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); |
|||
} |
|||
|
|||
inline bool RemoveBaseContent(const Service::FileSystem::FileSystemController& fs_controller, |
|||
const u64 program_id) { |
|||
return fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || |
|||
fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); |
|||
} |
|||
|
|||
inline InstallResult InstallNSP( |
|||
Core::System* system, FileSys::VfsFilesystem* vfs, const std::string& filename, |
|||
const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) { |
|||
const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, |
|||
std::size_t block_size) { |
|||
if (src == nullptr || dest == nullptr) { |
|||
return false; |
|||
} |
|||
if (!dest->Resize(src->GetSize())) { |
|||
return false; |
|||
} |
|||
|
|||
using namespace Common::Literals; |
|||
std::vector<u8> buffer(1_MiB); |
|||
|
|||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
|||
if (callback(src->GetSize(), i)) { |
|||
dest->Resize(0); |
|||
return false; |
|||
} |
|||
const auto read = src->Read(buffer.data(), buffer.size(), i); |
|||
dest->Write(buffer.data(), read, i); |
|||
} |
|||
return true; |
|||
}; |
|||
|
|||
std::shared_ptr<FileSys::NSP> nsp; |
|||
FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read); |
|||
if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) { |
|||
nsp = std::make_shared<FileSys::NSP>(file); |
|||
if (nsp->IsExtractedType()) { |
|||
return InstallResult::Failure; |
|||
} |
|||
} else { |
|||
return InstallResult::Failure; |
|||
} |
|||
|
|||
if (nsp->GetStatus() != Loader::ResultStatus::Success) { |
|||
return InstallResult::Failure; |
|||
} |
|||
const auto res = |
|||
system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); |
|||
switch (res) { |
|||
case FileSys::InstallResult::Success: |
|||
return InstallResult::Success; |
|||
case FileSys::InstallResult::OverwriteExisting: |
|||
return InstallResult::Overwrite; |
|||
case FileSys::InstallResult::ErrorBaseInstall: |
|||
return InstallResult::BaseInstallAttempted; |
|||
default: |
|||
return InstallResult::Failure; |
|||
} |
|||
} |
|||
|
|||
inline InstallResult InstallNCA( |
|||
FileSys::VfsFilesystem* vfs, const std::string& filename, |
|||
FileSys::RegisteredCache* registered_cache, const FileSys::TitleType title_type, |
|||
const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) { |
|||
const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, |
|||
std::size_t block_size) { |
|||
if (src == nullptr || dest == nullptr) { |
|||
return false; |
|||
} |
|||
if (!dest->Resize(src->GetSize())) { |
|||
return false; |
|||
} |
|||
|
|||
using namespace Common::Literals; |
|||
std::vector<u8> buffer(1_MiB); |
|||
|
|||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
|||
if (callback(src->GetSize(), i)) { |
|||
dest->Resize(0); |
|||
return false; |
|||
} |
|||
const auto read = src->Read(buffer.data(), buffer.size(), i); |
|||
dest->Write(buffer.data(), read, i); |
|||
} |
|||
return true; |
|||
}; |
|||
|
|||
const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read)); |
|||
const auto id = nca->GetStatus(); |
|||
|
|||
// Game updates necessary are missing base RomFS |
|||
if (id != Loader::ResultStatus::Success && |
|||
id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { |
|||
return InstallResult::Failure; |
|||
} |
|||
|
|||
const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy); |
|||
if (res == FileSys::InstallResult::Success) { |
|||
return InstallResult::Success; |
|||
} else if (res == FileSys::InstallResult::OverwriteExisting) { |
|||
return InstallResult::Overwrite; |
|||
} else { |
|||
return InstallResult::Failure; |
|||
} |
|||
} |
|||
|
|||
} // namespace ContentManager |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue