summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar t8952024-01-21 19:07:17 -0500
committerGravatar t8952024-01-21 19:15:11 -0500
commitdd36d43ea12a6f35672dc5b8efadbe9c407fc3c4 (patch)
tree68545b2dcc41a06669554f0cb41f9d411fc1c732
parentfrontend_common: Move integrity verification to content_manager (diff)
downloadyuzu-dd36d43ea12a6f35672dc5b8efadbe9c407fc3c4.tar.gz
yuzu-dd36d43ea12a6f35672dc5b8efadbe9c407fc3c4.tar.xz
yuzu-dd36d43ea12a6f35672dc5b8efadbe9c407fc3c4.zip
android: Add options to verify installed content
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt34
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt33
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameVerificationResult.kt15
-rw-r--r--src/android/app/src/main/jni/native.cpp37
-rw-r--r--src/android/app/src/main/res/layout/fragment_game_info.xml8
-rw-r--r--src/android/app/src/main/res/values/strings.xml11
8 files changed, 165 insertions, 4 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 1c9fb0675..c408485c6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.utils.Log
23import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable 23import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
24import org.yuzu.yuzu_emu.model.InstallResult 24import org.yuzu.yuzu_emu.model.InstallResult
25import org.yuzu.yuzu_emu.model.Patch 25import org.yuzu.yuzu_emu.model.Patch
26import org.yuzu.yuzu_emu.model.GameVerificationResult
26 27
27/** 28/**
28 * Class which contains methods that interact 29 * Class which contains methods that interact
@@ -565,6 +566,26 @@ object NativeLibrary {
565 external fun removeMod(programId: String, name: String) 566 external fun removeMod(programId: String, name: String)
566 567
567 /** 568 /**
569 * Verifies all installed content
570 * @param callback UI callback for verification progress. Return true in the callback to cancel.
571 * @return Array of content that failed verification. Successful if empty.
572 */
573 external fun verifyInstalledContents(
574 callback: (max: Long, progress: Long) -> Boolean
575 ): Array<String>
576
577 /**
578 * Verifies the contents of a game
579 * @param path String path to a game
580 * @param callback UI callback for verification progress. Return true in the callback to cancel.
581 * @return Int that is meant to be converted to a [GameVerificationResult]
582 */
583 external fun verifyGameContents(
584 path: String,
585 callback: (max: Long, progress: Long) -> Boolean
586 ): Int
587
588 /**
568 * Gets the save location for a specific game 589 * Gets the save location for a specific game
569 * 590 *
570 * @param programId String representation of a game's program ID 591 * @param programId String representation of a game's program ID
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
index fa2a4c9f9..5aa3f453f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
@@ -21,8 +21,10 @@ import androidx.fragment.app.activityViewModels
21import androidx.navigation.findNavController 21import androidx.navigation.findNavController
22import androidx.navigation.fragment.navArgs 22import androidx.navigation.fragment.navArgs
23import com.google.android.material.transition.MaterialSharedAxis 23import com.google.android.material.transition.MaterialSharedAxis
24import org.yuzu.yuzu_emu.NativeLibrary
24import org.yuzu.yuzu_emu.R 25import org.yuzu.yuzu_emu.R
25import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding 26import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
27import org.yuzu.yuzu_emu.model.GameVerificationResult
26import org.yuzu.yuzu_emu.model.HomeViewModel 28import org.yuzu.yuzu_emu.model.HomeViewModel
27import org.yuzu.yuzu_emu.utils.GameMetadata 29import org.yuzu.yuzu_emu.utils.GameMetadata
28 30
@@ -101,6 +103,38 @@ class GameInfoFragment : Fragment() {
101 """.trimIndent() 103 """.trimIndent()
102 copyToClipboard(args.game.title, details) 104 copyToClipboard(args.game.title, details)
103 } 105 }
106
107 buttonVerifyIntegrity.setOnClickListener {
108 ProgressDialogFragment.newInstance(
109 requireActivity(),
110 R.string.verifying,
111 true
112 ) { progressCallback, _ ->
113 val result = GameVerificationResult.from(
114 NativeLibrary.verifyGameContents(
115 args.game.path,
116 progressCallback
117 )
118 )
119 return@newInstance when (result) {
120 GameVerificationResult.Success ->
121 MessageDialogFragment.newInstance(
122 titleId = R.string.verify_success,
123 descriptionId = R.string.operation_completed_successfully
124 )
125 GameVerificationResult.Failed ->
126 MessageDialogFragment.newInstance(
127 titleId = R.string.verify_failure,
128 descriptionId = R.string.verify_failure_description
129 )
130 GameVerificationResult.NotImplemented ->
131 MessageDialogFragment.newInstance(
132 titleId = R.string.verify_no_result,
133 descriptionId = R.string.verify_no_result_description
134 )
135 }
136 }.show(parentFragmentManager, ProgressDialogFragment.TAG)
137 }
104 } 138 }
105 139
106 setInsets() 140 setInsets()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 6ddd758e6..aefae2938 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -32,6 +32,7 @@ import org.yuzu.yuzu_emu.BuildConfig
32import org.yuzu.yuzu_emu.HomeNavigationDirections 32import org.yuzu.yuzu_emu.HomeNavigationDirections
33import org.yuzu.yuzu_emu.NativeLibrary 33import org.yuzu.yuzu_emu.NativeLibrary
34import org.yuzu.yuzu_emu.R 34import org.yuzu.yuzu_emu.R
35import org.yuzu.yuzu_emu.YuzuApplication
35import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter 36import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
36import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding 37import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
37import org.yuzu.yuzu_emu.features.DocumentProvider 38import org.yuzu.yuzu_emu.features.DocumentProvider
@@ -142,6 +143,38 @@ class HomeSettingsFragment : Fragment() {
142 ) 143 )
143 add( 144 add(
144 HomeSetting( 145 HomeSetting(
146 R.string.verify_installed_content,
147 R.string.verify_installed_content_description,
148 R.drawable.ic_check_circle,
149 {
150 ProgressDialogFragment.newInstance(
151 requireActivity(),
152 titleId = R.string.verifying,
153 cancellable = true
154 ) { progressCallback, _ ->
155 val result = NativeLibrary.verifyInstalledContents(progressCallback)
156 return@newInstance if (result.isEmpty()) {
157 MessageDialogFragment.newInstance(
158 titleId = R.string.verify_success,
159 descriptionId = R.string.operation_completed_successfully
160 )
161 } else {
162 val failedNames = result.joinToString("\n")
163 val errorMessage = YuzuApplication.appContext.getString(
164 R.string.verification_failed_for,
165 failedNames
166 )
167 MessageDialogFragment.newInstance(
168 titleId = R.string.verify_failure,
169 descriptionString = errorMessage
170 )
171 }
172 }.show(parentFragmentManager, ProgressDialogFragment.TAG)
173 }
174 )
175 )
176 add(
177 HomeSetting(
145 R.string.share_log, 178 R.string.share_log,
146 R.string.share_log_description, 179 R.string.share_log_description,
147 R.drawable.ic_log, 180 R.drawable.ic_log,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
index 32062b6fe..620d8db7c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
@@ -69,7 +69,7 @@ class MessageDialogFragment : DialogFragment() {
69 private const val HELP_LINK = "Link" 69 private const val HELP_LINK = "Link"
70 70
71 fun newInstance( 71 fun newInstance(
72 activity: FragmentActivity, 72 activity: FragmentActivity? = null,
73 titleId: Int = 0, 73 titleId: Int = 0,
74 titleString: String = "", 74 titleString: String = "",
75 descriptionId: Int = 0, 75 descriptionId: Int = 0,
@@ -86,9 +86,11 @@ class MessageDialogFragment : DialogFragment() {
86 putString(DESCRIPTION_STRING, descriptionString) 86 putString(DESCRIPTION_STRING, descriptionString)
87 putInt(HELP_LINK, helpLinkId) 87 putInt(HELP_LINK, helpLinkId)
88 } 88 }
89 ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { 89 if (activity != null) {
90 clear() 90 ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
91 this.positiveAction = positiveAction 91 clear()
92 this.positiveAction = positiveAction
93 }
92 } 94 }
93 dialog.arguments = bundle 95 dialog.arguments = bundle
94 return dialog 96 return dialog
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameVerificationResult.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameVerificationResult.kt
new file mode 100644
index 000000000..804637fb8
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameVerificationResult.kt
@@ -0,0 +1,15 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6enum class GameVerificationResult(val int: Int) {
7 Success(0),
8 Failed(1),
9 NotImplemented(2);
10
11 companion object {
12 fun from(int: Int): GameVerificationResult =
13 entries.firstOrNull { it.int == int } ?: Success
14 }
15}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index be0a723b1..963f57380 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -829,6 +829,43 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
829 program_id, GetJString(env, jname)); 829 program_id, GetJString(env, jname));
830} 830}
831 831
832jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj,
833 jobject jcallback) {
834 auto jlambdaClass = env->GetObjectClass(jcallback);
835 auto jlambdaInvokeMethod = env->GetMethodID(
836 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
837 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
838 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
839 ToJDouble(env, max), ToJDouble(env, progress));
840 return GetJBoolean(env, jwasCancelled);
841 };
842
843 auto& session = EmulationSession::GetInstance();
844 std::vector<std::string> result = ContentManager::VerifyInstalledContents(
845 &session.System(), session.GetContentProvider(), callback);
846 jobjectArray jresult =
847 env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
848 for (size_t i = 0; i < result.size(); ++i) {
849 env->SetObjectArrayElement(jresult, i, ToJString(env, result[i]));
850 }
851 return jresult;
852}
853
854jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobject jobj,
855 jstring jpath, jobject jcallback) {
856 auto jlambdaClass = env->GetObjectClass(jcallback);
857 auto jlambdaInvokeMethod = env->GetMethodID(
858 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
859 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
860 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
861 ToJDouble(env, max), ToJDouble(env, progress));
862 return GetJBoolean(env, jwasCancelled);
863 };
864 auto& session = EmulationSession::GetInstance();
865 return static_cast<jint>(
866 ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback));
867}
868
832jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, 869jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
833 jstring jprogramId) { 870 jstring jprogramId) {
834 auto program_id = EmulationSession::GetProgramId(env, jprogramId); 871 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
diff --git a/src/android/app/src/main/res/layout/fragment_game_info.xml b/src/android/app/src/main/res/layout/fragment_game_info.xml
index 80ede8a8c..53af15787 100644
--- a/src/android/app/src/main/res/layout/fragment_game_info.xml
+++ b/src/android/app/src/main/res/layout/fragment_game_info.xml
@@ -118,6 +118,14 @@
118 android:layout_marginTop="16dp" 118 android:layout_marginTop="16dp"
119 android:text="@string/copy_details" /> 119 android:text="@string/copy_details" />
120 120
121 <com.google.android.material.button.MaterialButton
122 android:id="@+id/button_verify_integrity"
123 style="@style/Widget.Material3.Button"
124 android:layout_width="wrap_content"
125 android:layout_height="wrap_content"
126 android:layout_marginTop="10dp"
127 android:text="@string/verify_integrity" />
128
121 </LinearLayout> 129 </LinearLayout>
122 130
123 </androidx.core.widget.NestedScrollView> 131 </androidx.core.widget.NestedScrollView>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index bfcbb5812..eefcc3ff4 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -142,6 +142,8 @@
142 <item quantity="other">Successfully imported %d saves</item> 142 <item quantity="other">Successfully imported %d saves</item>
143 </plurals> 143 </plurals>
144 <string name="no_save_data_found">No save data found</string> 144 <string name="no_save_data_found">No save data found</string>
145 <string name="verify_installed_content">Verify installed content</string>
146 <string name="verify_installed_content_description">Checks all installed content for corruption</string>
145 147
146 <!-- Applet launcher strings --> 148 <!-- Applet launcher strings -->
147 <string name="applets">Applet launcher</string> 149 <string name="applets">Applet launcher</string>
@@ -288,6 +290,7 @@
288 <string name="import_complete">Import complete</string> 290 <string name="import_complete">Import complete</string>
289 <string name="more_options">More options</string> 291 <string name="more_options">More options</string>
290 <string name="use_global_setting">Use global setting</string> 292 <string name="use_global_setting">Use global setting</string>
293 <string name="operation_completed_successfully">The operation completed successfully</string>
291 294
292 <!-- GPU driver installation --> 295 <!-- GPU driver installation -->
293 <string name="select_gpu_driver">Select GPU driver</string> 296 <string name="select_gpu_driver">Select GPU driver</string>
@@ -352,6 +355,14 @@
352 <string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string> 355 <string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string>
353 <string name="confirm_uninstall">Confirm uninstall</string> 356 <string name="confirm_uninstall">Confirm uninstall</string>
354 <string name="confirm_uninstall_description">Are you sure you want to uninstall this addon?</string> 357 <string name="confirm_uninstall_description">Are you sure you want to uninstall this addon?</string>
358 <string name="verify_integrity">Verify integrity</string>
359 <string name="verifying">Verifying…</string>
360 <string name="verify_success">Integrity verification succeeded!</string>
361 <string name="verify_failure">Integrity verification failed!</string>
362 <string name="verify_failure_description">File contents may be corrupt</string>
363 <string name="verify_no_result">Integrity verification couldn\'t be performed</string>
364 <string name="verify_no_result_description">File contents were not checked for validity</string>
365 <string name="verification_failed_for">Verification failed for the following files:\n%1$s</string>
355 366
356 <!-- ROM loading errors --> 367 <!-- ROM loading errors -->
357 <string name="loader_error_encrypted">Your ROM is encrypted</string> 368 <string name="loader_error_encrypted">Your ROM is encrypted</string>