summaryrefslogtreecommitdiff
path: root/src/frontend_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend_common')
-rw-r--r--src/frontend_common/CMakeLists.txt1
-rw-r--r--src/frontend_common/content_manager.h238
2 files changed, 239 insertions, 0 deletions
diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt
index 22e9337c4..94d8cc4c3 100644
--- a/src/frontend_common/CMakeLists.txt
+++ b/src/frontend_common/CMakeLists.txt
@@ -4,6 +4,7 @@
4add_library(frontend_common STATIC 4add_library(frontend_common STATIC
5 config.cpp 5 config.cpp
6 config.h 6 config.h
7 content_manager.h
7) 8)
8 9
9create_target_directory_groups(frontend_common) 10create_target_directory_groups(frontend_common)
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
new file mode 100644
index 000000000..23f2979db
--- /dev/null
+++ b/src/frontend_common/content_manager.h
@@ -0,0 +1,238 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <boost/algorithm/string.hpp>
7#include "common/common_types.h"
8#include "common/literals.h"
9#include "core/core.h"
10#include "core/file_sys/common_funcs.h"
11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/mode.h"
13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/submission_package.h"
16#include "core/hle/service/filesystem/filesystem.h"
17#include "core/loader/loader.h"
18
19namespace ContentManager {
20
21enum class InstallResult {
22 Success,
23 Overwrite,
24 Failure,
25 BaseInstallAttempted,
26};
27
28/**
29 * \brief Removes a single installed DLC
30 * \param fs_controller [FileSystemController] reference from the Core::System instance
31 * \param title_id Unique title ID representing the DLC which will be removed
32 * \return 'true' if successful
33 */
34inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_controller,
35 const u64 title_id) {
36 return fs_controller.GetUserNANDContents()->RemoveExistingEntry(title_id) ||
37 fs_controller.GetSDMCContents()->RemoveExistingEntry(title_id);
38}
39
40/**
41 * \brief Removes all DLC for a game
42 * \param system Raw pointer to the system instance
43 * \param program_id Program ID for the game that will have all of its DLC removed
44 * \return Number of DLC removed
45 */
46inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) {
47 size_t count{};
48 const auto& fs_controller = system->GetFileSystemController();
49 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
50 FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
51 std::vector<u64> program_dlc_entries;
52
53 for (const auto& entry : dlc_entries) {
54 if (FileSys::GetBaseTitleID(entry.title_id) == program_id) {
55 program_dlc_entries.push_back(entry.title_id);
56 }
57 }
58
59 for (const auto& entry : program_dlc_entries) {
60 if (RemoveDLC(fs_controller, entry)) {
61 ++count;
62 }
63 }
64 return count;
65}
66
67/**
68 * \brief Removes the installed update for a game
69 * \param fs_controller [FileSystemController] reference from the Core::System instance
70 * \param program_id Program ID for the game that will have its installed update removed
71 * \return 'true' if successful
72 */
73inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller,
74 const u64 program_id) {
75 const auto update_id = program_id | 0x800;
76 return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
77 fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
78}
79
80/**
81 * \brief Removes the base content for a game
82 * \param fs_controller [FileSystemController] reference from the Core::System instance
83 * \param program_id Program ID for the game that will have its base content removed
84 * \return 'true' if successful
85 */
86inline bool RemoveBaseContent(const Service::FileSystem::FileSystemController& fs_controller,
87 const u64 program_id) {
88 return fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
89 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
90}
91
92/**
93 * \brief Removes a mod for a game
94 * \param fs_controller [FileSystemController] reference from the Core::System instance
95 * \param program_id Program ID for the game where [mod_name] will be removed
96 * \param mod_name The name of a mod as given by FileSys::PatchManager::GetPatches. This corresponds
97 * with the name of the mod's directory in a game's load folder.
98 * \return 'true' if successful
99 */
100inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_controller,
101 const u64 program_id, const std::string& mod_name) {
102 // Check general Mods (LayeredFS and IPS)
103 const auto mod_dir = fs_controller.GetModificationLoadRoot(program_id);
104 if (mod_dir != nullptr) {
105 return mod_dir->DeleteSubdirectoryRecursive(mod_name);
106 }
107
108 // Check SDMC mod directory (RomFS LayeredFS)
109 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(program_id);
110 if (sdmc_mod_dir != nullptr) {
111 return sdmc_mod_dir->DeleteSubdirectoryRecursive(mod_name);
112 }
113
114 return false;
115}
116
117/**
118 * \brief Installs an NSP
119 * \param system Raw pointer to the system instance
120 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System
121 * \param filename Path to the NSP file
122 * \param callback Optional callback to report the progress of the installation. The first size_t
123 * parameter is the total size of the virtual file and the second is the current progress. If you
124 * return false to the callback, it will cancel the installation as soon as possible.
125 * \return [InstallResult] representing how the installation finished
126 */
127inline InstallResult InstallNSP(
128 Core::System* system, FileSys::VfsFilesystem* vfs, const std::string& filename,
129 const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
130 const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
131 std::size_t block_size) {
132 if (src == nullptr || dest == nullptr) {
133 return false;
134 }
135 if (!dest->Resize(src->GetSize())) {
136 return false;
137 }
138
139 using namespace Common::Literals;
140 std::vector<u8> buffer(1_MiB);
141
142 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
143 if (callback(src->GetSize(), i)) {
144 dest->Resize(0);
145 return false;
146 }
147 const auto read = src->Read(buffer.data(), buffer.size(), i);
148 dest->Write(buffer.data(), read, i);
149 }
150 return true;
151 };
152
153 std::shared_ptr<FileSys::NSP> nsp;
154 FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read);
155 if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
156 nsp = std::make_shared<FileSys::NSP>(file);
157 if (nsp->IsExtractedType()) {
158 return InstallResult::Failure;
159 }
160 } else {
161 return InstallResult::Failure;
162 }
163
164 if (nsp->GetStatus() != Loader::ResultStatus::Success) {
165 return InstallResult::Failure;
166 }
167 const auto res =
168 system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
169 switch (res) {
170 case FileSys::InstallResult::Success:
171 return InstallResult::Success;
172 case FileSys::InstallResult::OverwriteExisting:
173 return InstallResult::Overwrite;
174 case FileSys::InstallResult::ErrorBaseInstall:
175 return InstallResult::BaseInstallAttempted;
176 default:
177 return InstallResult::Failure;
178 }
179}
180
181/**
182 * \brief Installs an NCA
183 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System
184 * \param filename Path to the NCA file
185 * \param registered_cache Raw pointer to the registered cache that the NCA will be installed to
186 * \param title_type Type of NCA package to install
187 * \param callback Optional callback to report the progress of the installation. The first size_t
188 * parameter is the total size of the virtual file and the second is the current progress. If you
189 * return false to the callback, it will cancel the installation as soon as possible.
190 * \return [InstallResult] representing how the installation finished
191 */
192inline InstallResult InstallNCA(
193 FileSys::VfsFilesystem* vfs, const std::string& filename,
194 FileSys::RegisteredCache* registered_cache, const FileSys::TitleType title_type,
195 const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
196 const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
197 std::size_t block_size) {
198 if (src == nullptr || dest == nullptr) {
199 return false;
200 }
201 if (!dest->Resize(src->GetSize())) {
202 return false;
203 }
204
205 using namespace Common::Literals;
206 std::vector<u8> buffer(1_MiB);
207
208 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
209 if (callback(src->GetSize(), i)) {
210 dest->Resize(0);
211 return false;
212 }
213 const auto read = src->Read(buffer.data(), buffer.size(), i);
214 dest->Write(buffer.data(), read, i);
215 }
216 return true;
217 };
218
219 const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read));
220 const auto id = nca->GetStatus();
221
222 // Game updates necessary are missing base RomFS
223 if (id != Loader::ResultStatus::Success &&
224 id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
225 return InstallResult::Failure;
226 }
227
228 const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy);
229 if (res == FileSys::InstallResult::Success) {
230 return InstallResult::Success;
231 } else if (res == FileSys::InstallResult::OverwriteExisting) {
232 return InstallResult::Overwrite;
233 } else {
234 return InstallResult::Failure;
235 }
236}
237
238} // namespace ContentManager