Browse Source
Merge pull request #12715 from t895/remove-addons
Merge pull request #12715 from t895/remove-addons
android: Add uninstall addon buttonpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 912 additions and 424 deletions
-
44src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
-
21src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
-
25src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
-
6src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
-
25src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
-
29src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
-
44src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
-
10src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt
-
42src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt
-
15src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt
-
16src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt
-
14src/android/app/src/main/java/org/yuzu/yuzu_emu/model/PatchType.kt
-
29src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
-
101src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
-
80src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
-
5src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
-
16src/android/app/src/main/jni/android_common/android_common.cpp
-
7src/android/app/src/main/jni/android_common/android_common.h
-
101src/android/app/src/main/jni/id_cache.cpp
-
17src/android/app/src/main/jni/id_cache.h
-
128src/android/app/src/main/jni/native.cpp
-
2src/android/app/src/main/jni/native.h
-
30src/android/app/src/main/res/layout/dialog_progress_bar.xml
-
32src/android/app/src/main/res/layout/list_item_addon.xml
-
3src/android/app/src/main/res/values/strings.xml
-
43src/core/file_sys/patch_manager.cpp
-
17src/core/file_sys/patch_manager.h
-
1src/frontend_common/CMakeLists.txt
-
238src/frontend_common/content_manager.h
-
7src/yuzu/configuration/configure_per_game_addons.cpp
-
11src/yuzu/game_list_worker.cpp
-
166src/yuzu/main.cpp
-
11src/yuzu/main.h
@ -1,10 +0,0 @@ |
|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.model |
|||
|
|||
data class Addon( |
|||
var enabled: Boolean, |
|||
val title: String, |
|||
val version: String |
|||
) |
|||
@ -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,16 @@ |
|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.model |
|||
|
|||
import androidx.annotation.Keep |
|||
|
|||
@Keep |
|||
data class Patch( |
|||
var enabled: Boolean, |
|||
val name: String, |
|||
val version: String, |
|||
val type: Int, |
|||
val programId: String, |
|||
val titleId: String |
|||
) |
|||
@ -0,0 +1,14 @@ |
|||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.model |
|||
|
|||
enum class PatchType(val int: Int) { |
|||
Update(0), |
|||
DLC(1), |
|||
Mod(2); |
|||
|
|||
companion object { |
|||
fun from(int: Int): PatchType = entries.firstOrNull { it.int == int } ?: Update |
|||
} |
|||
} |
|||
@ -1,8 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<com.google.android.material.progressindicator.LinearProgressIndicator xmlns:android="http://schemas.android.com/apk/res/android" |
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
android:id="@+id/progress_bar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:padding="24dp" |
|||
app:trackCornerRadius="4dp" /> |
|||
android:orientation="vertical"> |
|||
|
|||
<com.google.android.material.textview.MaterialTextView |
|||
android:id="@+id/message" |
|||
style="@style/TextAppearance.Material3.BodyMedium" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginHorizontal="24dp" |
|||
android:layout_marginTop="12dp" |
|||
android:layout_marginBottom="6dp" |
|||
android:ellipsize="marquee" |
|||
android:marqueeRepeatLimit="marquee_forever" |
|||
android:requiresFadingEdge="horizontal" |
|||
android:singleLine="true" |
|||
android:textAlignment="viewStart" |
|||
android:visibility="gone" /> |
|||
|
|||
<com.google.android.material.progressindicator.LinearProgressIndicator |
|||
android:id="@+id/progress_bar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:padding="24dp" |
|||
app:trackCornerRadius="4dp" /> |
|||
|
|||
</LinearLayout> |
|||
@ -0,0 +1,238 @@ |
|||
// 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, |
|||
}; |
|||
|
|||
/** |
|||
* \brief Removes a single installed DLC |
|||
* \param fs_controller [FileSystemController] reference from the Core::System instance |
|||
* \param title_id Unique title ID representing the DLC which will be removed |
|||
* \return 'true' if successful |
|||
*/ |
|||
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); |
|||
} |
|||
|
|||
/** |
|||
* \brief Removes all DLC for a game |
|||
* \param system Raw pointer to the system instance |
|||
* \param program_id Program ID for the game that will have all of its DLC removed |
|||
* \return Number of DLC removed |
|||
*/ |
|||
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; |
|||
} |
|||
|
|||
/** |
|||
* \brief Removes the installed update for a game |
|||
* \param fs_controller [FileSystemController] reference from the Core::System instance |
|||
* \param program_id Program ID for the game that will have its installed update removed |
|||
* \return 'true' if successful |
|||
*/ |
|||
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); |
|||
} |
|||
|
|||
/** |
|||
* \brief Removes the base content for a game |
|||
* \param fs_controller [FileSystemController] reference from the Core::System instance |
|||
* \param program_id Program ID for the game that will have its base content removed |
|||
* \return 'true' if successful |
|||
*/ |
|||
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); |
|||
} |
|||
|
|||
/** |
|||
* \brief Removes a mod for a game |
|||
* \param fs_controller [FileSystemController] reference from the Core::System instance |
|||
* \param program_id Program ID for the game where [mod_name] will be removed |
|||
* \param mod_name The name of a mod as given by FileSys::PatchManager::GetPatches. This corresponds |
|||
* with the name of the mod's directory in a game's load folder. |
|||
* \return 'true' if successful |
|||
*/ |
|||
inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_controller, |
|||
const u64 program_id, const std::string& mod_name) { |
|||
// Check general Mods (LayeredFS and IPS) |
|||
const auto mod_dir = fs_controller.GetModificationLoadRoot(program_id); |
|||
if (mod_dir != nullptr) { |
|||
return mod_dir->DeleteSubdirectoryRecursive(mod_name); |
|||
} |
|||
|
|||
// Check SDMC mod directory (RomFS LayeredFS) |
|||
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(program_id); |
|||
if (sdmc_mod_dir != nullptr) { |
|||
return sdmc_mod_dir->DeleteSubdirectoryRecursive(mod_name); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* \brief Installs an NSP |
|||
* \param system Raw pointer to the system instance |
|||
* \param vfs Raw pointer to the VfsFilesystem instance in Core::System |
|||
* \param filename Path to the NSP file |
|||
* \param callback Optional callback to report the progress of the installation. The first size_t |
|||
* parameter is the total size of the virtual file and the second is the current progress. If you |
|||
* return false to the callback, it will cancel the installation as soon as possible. |
|||
* \return [InstallResult] representing how the installation finished |
|||
*/ |
|||
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; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* \brief Installs an NCA |
|||
* \param vfs Raw pointer to the VfsFilesystem instance in Core::System |
|||
* \param filename Path to the NCA file |
|||
* \param registered_cache Raw pointer to the registered cache that the NCA will be installed to |
|||
* \param title_type Type of NCA package to install |
|||
* \param callback Optional callback to report the progress of the installation. The first size_t |
|||
* parameter is the total size of the virtual file and the second is the current progress. If you |
|||
* return false to the callback, it will cancel the installation as soon as possible. |
|||
* \return [InstallResult] representing how the installation finished |
|||
*/ |
|||
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