summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/AndroidManifest.xml3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt219
-rw-r--r--src/android/app/src/main/jni/native.cpp16
-rw-r--r--src/android/app/src/main/res/values/strings.xml10
-rw-r--r--src/android/app/src/main/res/xml/game_mode_config.xml7
-rw-r--r--src/core/file_sys/savedata_factory.cpp9
-rw-r--r--src/core/file_sys/savedata_factory.h1
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp60
-rw-r--r--src/core/hle/service/hid/hid_system_server.h6
-rw-r--r--src/hid_core/resource_manager.cpp1
-rw-r--r--src/hid_core/resources/applet_resource.cpp34
-rw-r--r--src/hid_core/resources/npad/npad.cpp5
-rw-r--r--src/hid_core/resources/npad/npad_resource.cpp4
14 files changed, 357 insertions, 27 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index f10131b24..f011bd696 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -31,6 +31,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
31 android:dataExtractionRules="@xml/data_extraction_rules_api_31" 31 android:dataExtractionRules="@xml/data_extraction_rules_api_31"
32 android:enableOnBackInvokedCallback="true"> 32 android:enableOnBackInvokedCallback="true">
33 33
34 <meta-data android:name="android.game_mode_config"
35 android:resource="@xml/game_mode_config" />
36
34 <activity 37 <activity
35 android:name="org.yuzu.yuzu_emu.ui.main.MainActivity" 38 android:name="org.yuzu.yuzu_emu.ui.main.MainActivity"
36 android:exported="true" 39 android:exported="true"
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 010c44951..b7556e353 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
@@ -548,6 +548,15 @@ object NativeLibrary {
548 external fun getSavePath(programId: String): String 548 external fun getSavePath(programId: String): String
549 549
550 /** 550 /**
551 * Gets the root save directory for the default profile as either
552 * /user/save/account/<user id raw string> or /user/save/000...000/<user id>
553 *
554 * @param future If true, returns the /user/save/account/... directory
555 * @return Save data path that may not exist yet
556 */
557 external fun getDefaultProfileSaveDataRoot(future: Boolean): String
558
559 /**
551 * Adds a file to the manual filesystem provider in our EmulationSession instance 560 * Adds a file to the manual filesystem provider in our EmulationSession instance
552 * @param path Path to the file we're adding. Can be a string representation of a [Uri] or 561 * @param path Path to the file we're adding. Can be a string representation of a [Uri] or
553 * a normal path 562 * a normal path
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 569727b90..5b4bf2c9f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -7,20 +7,39 @@ import android.os.Bundle
7import android.view.LayoutInflater 7import android.view.LayoutInflater
8import android.view.View 8import android.view.View
9import android.view.ViewGroup 9import android.view.ViewGroup
10import android.widget.Toast
11import androidx.activity.result.contract.ActivityResultContracts
10import androidx.core.view.ViewCompat 12import androidx.core.view.ViewCompat
11import androidx.core.view.WindowInsetsCompat 13import androidx.core.view.WindowInsetsCompat
12import androidx.core.view.updatePadding 14import androidx.core.view.updatePadding
13import androidx.fragment.app.Fragment 15import androidx.fragment.app.Fragment
14import androidx.fragment.app.activityViewModels 16import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
15import androidx.navigation.findNavController 20import androidx.navigation.findNavController
16import androidx.recyclerview.widget.GridLayoutManager 21import androidx.recyclerview.widget.GridLayoutManager
17import com.google.android.material.transition.MaterialSharedAxis 22import com.google.android.material.transition.MaterialSharedAxis
23import kotlinx.coroutines.Dispatchers
24import kotlinx.coroutines.launch
25import kotlinx.coroutines.withContext
26import org.yuzu.yuzu_emu.NativeLibrary
18import org.yuzu.yuzu_emu.R 27import org.yuzu.yuzu_emu.R
28import org.yuzu.yuzu_emu.YuzuApplication
19import org.yuzu.yuzu_emu.adapters.InstallableAdapter 29import org.yuzu.yuzu_emu.adapters.InstallableAdapter
20import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding 30import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
21import org.yuzu.yuzu_emu.model.HomeViewModel 31import org.yuzu.yuzu_emu.model.HomeViewModel
22import org.yuzu.yuzu_emu.model.Installable 32import org.yuzu.yuzu_emu.model.Installable
33import org.yuzu.yuzu_emu.model.TaskState
23import org.yuzu.yuzu_emu.ui.main.MainActivity 34import org.yuzu.yuzu_emu.ui.main.MainActivity
35import org.yuzu.yuzu_emu.utils.DirectoryInitialization
36import org.yuzu.yuzu_emu.utils.FileUtil
37import java.io.BufferedInputStream
38import java.io.BufferedOutputStream
39import java.io.File
40import java.math.BigInteger
41import java.time.LocalDateTime
42import java.time.format.DateTimeFormatter
24 43
25class InstallableFragment : Fragment() { 44class InstallableFragment : Fragment() {
26 private var _binding: FragmentInstallablesBinding? = null 45 private var _binding: FragmentInstallablesBinding? = null
@@ -56,6 +75,17 @@ class InstallableFragment : Fragment() {
56 binding.root.findNavController().popBackStack() 75 binding.root.findNavController().popBackStack()
57 } 76 }
58 77
78 viewLifecycleOwner.lifecycleScope.launch {
79 repeatOnLifecycle(Lifecycle.State.CREATED) {
80 homeViewModel.openImportSaves.collect {
81 if (it) {
82 importSaves.launch(arrayOf("application/zip"))
83 homeViewModel.setOpenImportSaves(false)
84 }
85 }
86 }
87 }
88
59 val installables = listOf( 89 val installables = listOf(
60 Installable( 90 Installable(
61 R.string.user_data, 91 R.string.user_data,
@@ -64,6 +94,43 @@ class InstallableFragment : Fragment() {
64 export = { mainActivity.exportUserData.launch("export.zip") } 94 export = { mainActivity.exportUserData.launch("export.zip") }
65 ), 95 ),
66 Installable( 96 Installable(
97 R.string.manage_save_data,
98 R.string.manage_save_data_description,
99 install = {
100 MessageDialogFragment.newInstance(
101 requireActivity(),
102 titleId = R.string.import_save_warning,
103 descriptionId = R.string.import_save_warning_description,
104 positiveAction = { homeViewModel.setOpenImportSaves(true) }
105 ).show(parentFragmentManager, MessageDialogFragment.TAG)
106 },
107 export = {
108 val oldSaveDataFolder = File(
109 "${DirectoryInitialization.userDirectory}/nand" +
110 NativeLibrary.getDefaultProfileSaveDataRoot(false)
111 )
112 val futureSaveDataFolder = File(
113 "${DirectoryInitialization.userDirectory}/nand" +
114 NativeLibrary.getDefaultProfileSaveDataRoot(true)
115 )
116 if (!oldSaveDataFolder.exists() && !futureSaveDataFolder.exists()) {
117 Toast.makeText(
118 YuzuApplication.appContext,
119 R.string.no_save_data_found,
120 Toast.LENGTH_SHORT
121 ).show()
122 return@Installable
123 } else {
124 exportSaves.launch(
125 "${getString(R.string.save_data)} " +
126 LocalDateTime.now().format(
127 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
128 )
129 )
130 }
131 }
132 ),
133 Installable(
67 R.string.install_game_content, 134 R.string.install_game_content,
68 R.string.install_game_content_description, 135 R.string.install_game_content_description,
69 install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) } 136 install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
@@ -121,4 +188,156 @@ class InstallableFragment : Fragment() {
121 188
122 windowInsets 189 windowInsets
123 } 190 }
191
192 private val importSaves =
193 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
194 if (result == null) {
195 return@registerForActivityResult
196 }
197
198 val inputZip = requireContext().contentResolver.openInputStream(result)
199 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
200 cacheSaveDir.mkdir()
201
202 if (inputZip == null) {
203 Toast.makeText(
204 YuzuApplication.appContext,
205 getString(R.string.fatal_error),
206 Toast.LENGTH_LONG
207 ).show()
208 return@registerForActivityResult
209 }
210
211 IndeterminateProgressDialogFragment.newInstance(
212 requireActivity(),
213 R.string.save_files_importing,
214 false
215 ) {
216 try {
217 FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
218 val files = cacheSaveDir.listFiles()
219 var successfulImports = 0
220 var failedImports = 0
221 if (files != null) {
222 for (file in files) {
223 if (file.isDirectory) {
224 val baseSaveDir =
225 NativeLibrary.getSavePath(BigInteger(file.name, 16).toString())
226 if (baseSaveDir.isEmpty()) {
227 failedImports++
228 continue
229 }
230
231 val internalSaveFolder = File(
232 "${DirectoryInitialization.userDirectory}/nand$baseSaveDir"
233 )
234 internalSaveFolder.deleteRecursively()
235 internalSaveFolder.mkdir()
236 file.copyRecursively(target = internalSaveFolder, overwrite = true)
237 successfulImports++
238 }
239 }
240 }
241
242 withContext(Dispatchers.Main) {
243 if (successfulImports == 0) {
244 MessageDialogFragment.newInstance(
245 requireActivity(),
246 titleId = R.string.save_file_invalid_zip_structure,
247 descriptionId = R.string.save_file_invalid_zip_structure_description
248 ).show(parentFragmentManager, MessageDialogFragment.TAG)
249 return@withContext
250 }
251 val successString = if (failedImports > 0) {
252 """
253 ${
254 requireContext().resources.getQuantityString(
255 R.plurals.saves_import_success,
256 successfulImports,
257 successfulImports
258 )
259 }
260 ${
261 requireContext().resources.getQuantityString(
262 R.plurals.saves_import_failed,
263 failedImports,
264 failedImports
265 )
266 }
267 """
268 } else {
269 requireContext().resources.getQuantityString(
270 R.plurals.saves_import_success,
271 successfulImports,
272 successfulImports
273 )
274 }
275 MessageDialogFragment.newInstance(
276 requireActivity(),
277 titleId = R.string.import_complete,
278 descriptionString = successString
279 ).show(parentFragmentManager, MessageDialogFragment.TAG)
280 }
281
282 cacheSaveDir.deleteRecursively()
283 } catch (e: Exception) {
284 Toast.makeText(
285 YuzuApplication.appContext,
286 getString(R.string.fatal_error),
287 Toast.LENGTH_LONG
288 ).show()
289 }
290 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
291 }
292
293 private val exportSaves = registerForActivityResult(
294 ActivityResultContracts.CreateDocument("application/zip")
295 ) { result ->
296 if (result == null) {
297 return@registerForActivityResult
298 }
299
300 IndeterminateProgressDialogFragment.newInstance(
301 requireActivity(),
302 R.string.save_files_exporting,
303 false
304 ) {
305 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
306 cacheSaveDir.mkdir()
307
308 val oldSaveDataFolder = File(
309 "${DirectoryInitialization.userDirectory}/nand" +
310 NativeLibrary.getDefaultProfileSaveDataRoot(false)
311 )
312 if (oldSaveDataFolder.exists()) {
313 oldSaveDataFolder.copyRecursively(cacheSaveDir)
314 }
315
316 val futureSaveDataFolder = File(
317 "${DirectoryInitialization.userDirectory}/nand" +
318 NativeLibrary.getDefaultProfileSaveDataRoot(true)
319 )
320 if (futureSaveDataFolder.exists()) {
321 futureSaveDataFolder.copyRecursively(cacheSaveDir)
322 }
323
324 val saveFilesTotal = cacheSaveDir.listFiles()?.size ?: 0
325 if (saveFilesTotal == 0) {
326 cacheSaveDir.deleteRecursively()
327 return@newInstance getString(R.string.no_save_data_found)
328 }
329
330 val zipResult = FileUtil.zipFromInternalStorage(
331 cacheSaveDir,
332 cacheSaveDir.path,
333 BufferedOutputStream(requireContext().contentResolver.openOutputStream(result))
334 )
335 cacheSaveDir.deleteRecursively()
336
337 return@newInstance when (zipResult) {
338 TaskState.Completed -> getString(R.string.export_success)
339 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
340 }
341 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
342 }
124} 343}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 056920a4a..136c8dee6 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -862,6 +862,9 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
862jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, 862jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
863 jstring jprogramId) { 863 jstring jprogramId) {
864 auto program_id = EmulationSession::GetProgramId(env, jprogramId); 864 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
865 if (program_id == 0) {
866 return ToJString(env, "");
867 }
865 868
866 auto& system = EmulationSession::GetInstance().System(); 869 auto& system = EmulationSession::GetInstance().System();
867 870
@@ -880,6 +883,19 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
880 return ToJString(env, user_save_data_path); 883 return ToJString(env, user_save_data_path);
881} 884}
882 885
886jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env,
887 jobject jobj,
888 jboolean jfuture) {
889 Service::Account::ProfileManager manager;
890 // TODO: Pass in a selected user once we get the relevant UI working
891 const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
892 ASSERT(user_id);
893
894 const auto user_save_data_root =
895 FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture);
896 return ToJString(env, user_save_data_root);
897}
898
883void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj, 899void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
884 jstring jpath) { 900 jstring jpath) {
885 EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath)); 901 EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath));
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 83aa1b781..3bb92ad67 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -133,6 +133,15 @@
133 <string name="add_game_folder">Add game folder</string> 133 <string name="add_game_folder">Add game folder</string>
134 <string name="folder_already_added">This folder was already added!</string> 134 <string name="folder_already_added">This folder was already added!</string>
135 <string name="game_folder_properties">Game folder properties</string> 135 <string name="game_folder_properties">Game folder properties</string>
136 <plurals name="saves_import_failed">
137 <item quantity="one">Failed to import %d save</item>
138 <item quantity="other">Failed to import %d saves</item>
139 </plurals>
140 <plurals name="saves_import_success">
141 <item quantity="one">Successfully imported %d save</item>
142 <item quantity="other">Successfully imported %d saves</item>
143 </plurals>
144 <string name="no_save_data_found">No save data found</string>
136 145
137 <!-- Applet launcher strings --> 146 <!-- Applet launcher strings -->
138 <string name="applets">Applet launcher</string> 147 <string name="applets">Applet launcher</string>
@@ -276,6 +285,7 @@
276 <string name="global">Global</string> 285 <string name="global">Global</string>
277 <string name="custom">Custom</string> 286 <string name="custom">Custom</string>
278 <string name="notice">Notice</string> 287 <string name="notice">Notice</string>
288 <string name="import_complete">Import complete</string>
279 289
280 <!-- GPU driver installation --> 290 <!-- GPU driver installation -->
281 <string name="select_gpu_driver">Select GPU driver</string> 291 <string name="select_gpu_driver">Select GPU driver</string>
diff --git a/src/android/app/src/main/res/xml/game_mode_config.xml b/src/android/app/src/main/res/xml/game_mode_config.xml
new file mode 100644
index 000000000..b28dd3a11
--- /dev/null
+++ b/src/android/app/src/main/res/xml/game_mode_config.xml
@@ -0,0 +1,7 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<game-mode-config
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 android:supportsBatteryGameMode="true"
5 android:supportsPerformanceGameMode="true"
6 android:allowGameDownscaling="false"
7 android:allowGameFpsOverride="false"/>
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 8d5d593e8..12b3bd797 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -189,6 +189,15 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
189 } 189 }
190} 190}
191 191
192std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future) {
193 if (future) {
194 Common::UUID uuid;
195 std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
196 return fmt::format("/user/save/account/{}", uuid.RawString());
197 }
198 return fmt::format("/user/save/{:016X}/{:016X}{:016X}", 0, user_id[1], user_id[0]);
199}
200
192SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, 201SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
193 u128 user_id) const { 202 u128 user_id) const {
194 const auto path = 203 const auto path =
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index e3a0f8cef..fd4887e99 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -101,6 +101,7 @@ public:
101 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); 101 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
102 static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, 102 static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
103 SaveDataType type, u64 title_id, u128 user_id, u64 save_id); 103 SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
104 static std::string GetUserGameSaveDataRoot(u128 user_id, bool future);
104 105
105 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; 106 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
106 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, 107 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 027c56025..2a65615e8 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -81,7 +81,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
81 {522, nullptr, "SetJoyConRailEnabled"}, 81 {522, nullptr, "SetJoyConRailEnabled"},
82 {523, nullptr, "IsJoyConRailEnabled"}, 82 {523, nullptr, "IsJoyConRailEnabled"},
83 {524, nullptr, "IsHandheldHidsEnabled"}, 83 {524, nullptr, "IsHandheldHidsEnabled"},
84 {525, nullptr, "IsJoyConAttachedOnAllRail"}, 84 {525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"},
85 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, 85 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
86 {541, nullptr, "GetPlayReportControllerUsages"}, 86 {541, nullptr, "GetPlayReportControllerUsages"},
87 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, 87 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
@@ -131,7 +131,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
131 {1001, nullptr, "GetFirmwareVersion"}, 131 {1001, nullptr, "GetFirmwareVersion"},
132 {1002, nullptr, "GetAvailableFirmwareVersion"}, 132 {1002, nullptr, "GetAvailableFirmwareVersion"},
133 {1003, nullptr, "IsFirmwareUpdateAvailable"}, 133 {1003, nullptr, "IsFirmwareUpdateAvailable"},
134 {1004, nullptr, "CheckFirmwareUpdateRequired"}, 134 {1004, &IHidSystemServer::CheckFirmwareUpdateRequired, "CheckFirmwareUpdateRequired"},
135 {1005, nullptr, "StartFirmwareUpdate"}, 135 {1005, nullptr, "StartFirmwareUpdate"},
136 {1006, nullptr, "AbortFirmwareUpdate"}, 136 {1006, nullptr, "AbortFirmwareUpdate"},
137 {1007, nullptr, "GetFirmwareUpdateState"}, 137 {1007, nullptr, "GetFirmwareUpdateState"},
@@ -144,9 +144,9 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
144 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, 144 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
145 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, 145 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
146 {1100, nullptr, "GetHidbusSystemServiceObject"}, 146 {1100, nullptr, "GetHidbusSystemServiceObject"},
147 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, 147 {1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"},
148 {1130, nullptr, "InitializeUsbFirmwareUpdate"}, 148 {1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"},
149 {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, 149 {1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"},
150 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, 150 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
151 {1133, nullptr, "StartUsbFirmwareUpdate"}, 151 {1133, nullptr, "StartUsbFirmwareUpdate"},
152 {1134, nullptr, "GetUsbFirmwareUpdateState"}, 152 {1134, nullptr, "GetUsbFirmwareUpdateState"},
@@ -196,7 +196,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
196 {1268, nullptr, "DeleteButtonConfigStorageFull"}, 196 {1268, nullptr, "DeleteButtonConfigStorageFull"},
197 {1269, nullptr, "DeleteButtonConfigStorageLeft"}, 197 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
198 {1270, nullptr, "DeleteButtonConfigStorageRight"}, 198 {1270, nullptr, "DeleteButtonConfigStorageRight"},
199 {1271, nullptr, "IsUsingCustomButtonConfig"}, 199 {1271, &IHidSystemServer::IsUsingCustomButtonConfig, "IsUsingCustomButtonConfig"},
200 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, 200 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
201 {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, 201 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
202 {1274, nullptr, "SetDefaultButtonConfig"}, 202 {1274, nullptr, "SetDefaultButtonConfig"},
@@ -555,6 +555,16 @@ void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) {
555 rb.Push(ResultSuccess); 555 rb.Push(ResultSuccess);
556} 556}
557 557
558void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
559 const bool is_attached = true;
560
561 LOG_DEBUG(Service_HID, "(STUBBED) called, is_attached={}", is_attached);
562
563 IPC::ResponseBuilder rb{ctx, 3};
564 rb.Push(ResultSuccess);
565 rb.Push(is_attached);
566}
567
558void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) { 568void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
559 LOG_INFO(Service_AM, "(STUBBED) called"); 569 LOG_INFO(Service_AM, "(STUBBED) called");
560 570
@@ -641,6 +651,34 @@ void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) {
641 rb.Push(ResultSuccess); 651 rb.Push(ResultSuccess);
642} 652}
643 653
654void IHidSystemServer::CheckFirmwareUpdateRequired(HLERequestContext& ctx) {
655 LOG_WARNING(Service_HID, "(STUBBED) called");
656
657 IPC::ResponseBuilder rb{ctx, 2};
658 rb.Push(ResultSuccess);
659}
660
661void IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx) {
662 LOG_WARNING(Service_HID, "(STUBBED) called");
663
664 IPC::ResponseBuilder rb{ctx, 2};
665 rb.Push(ResultSuccess);
666}
667
668void IHidSystemServer::InitializeUsbFirmwareUpdate(HLERequestContext& ctx) {
669 LOG_WARNING(Service_HID, "(STUBBED) called");
670
671 IPC::ResponseBuilder rb{ctx, 2};
672 rb.Push(ResultSuccess);
673}
674
675void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) {
676 LOG_WARNING(Service_HID, "(STUBBED) called");
677
678 IPC::ResponseBuilder rb{ctx, 2};
679 rb.Push(ResultSuccess);
680}
681
644void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { 682void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
645 LOG_WARNING(Service_HID, "(STUBBED) called"); 683 LOG_WARNING(Service_HID, "(STUBBED) called");
646 684
@@ -665,6 +703,16 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx
665 rb.PushRaw(touchscreen_config); 703 rb.PushRaw(touchscreen_config);
666} 704}
667 705
706void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) {
707 const bool is_enabled = false;
708
709 LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
710
711 IPC::ResponseBuilder rb{ctx, 3};
712 rb.Push(ResultSuccess);
713 rb.Push(is_enabled);
714}
715
668std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() { 716std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
669 resource_manager->Initialize(); 717 resource_manager->Initialize();
670 return resource_manager; 718 return resource_manager;
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
index 1e623dfc2..f467e2aa8 100644
--- a/src/core/hle/service/hid/hid_system_server.h
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -44,6 +44,7 @@ private:
44 void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx); 44 void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx);
45 void EnableAppletToGetPadInput(HLERequestContext& ctx); 45 void EnableAppletToGetPadInput(HLERequestContext& ctx);
46 void EnableAppletToGetTouchScreen(HLERequestContext& ctx); 46 void EnableAppletToGetTouchScreen(HLERequestContext& ctx);
47 void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
47 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); 48 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
48 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); 49 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
49 void GetRegisteredDevices(HLERequestContext& ctx); 50 void GetRegisteredDevices(HLERequestContext& ctx);
@@ -53,8 +54,13 @@ private:
53 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); 54 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
54 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); 55 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
55 void InitializeFirmwareUpdate(HLERequestContext& ctx); 56 void InitializeFirmwareUpdate(HLERequestContext& ctx);
57 void CheckFirmwareUpdateRequired(HLERequestContext& ctx);
58 void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx);
59 void InitializeUsbFirmwareUpdate(HLERequestContext& ctx);
60 void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
56 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); 61 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
57 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); 62 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
63 void IsUsingCustomButtonConfig(HLERequestContext& ctx);
58 64
59 std::shared_ptr<ResourceManager> GetResourceManager(); 65 std::shared_ptr<ResourceManager> GetResourceManager();
60 66
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp
index 17dacef6e..2c5fe6d51 100644
--- a/src/hid_core/resource_manager.cpp
+++ b/src/hid_core/resource_manager.cpp
@@ -224,6 +224,7 @@ Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value)
224void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) { 224void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) {
225 std::scoped_lock lock{shared_mutex}; 225 std::scoped_lock lock{shared_mutex};
226 applet_resource->UnregisterAppletResourceUserId(aruid); 226 applet_resource->UnregisterAppletResourceUserId(aruid);
227 npad->UnregisterAppletResourceUserId(aruid);
227} 228}
228 229
229Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { 230Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
diff --git a/src/hid_core/resources/applet_resource.cpp b/src/hid_core/resources/applet_resource.cpp
index d16cff1a4..d09a525c6 100644
--- a/src/hid_core/resources/applet_resource.cpp
+++ b/src/hid_core/resources/applet_resource.cpp
@@ -87,7 +87,9 @@ Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input
87 data_index = i; 87 data_index = i;
88 break; 88 break;
89 } 89 }
90 if (registration_list.flag[i] == RegistrationStatus::None) { 90 // TODO: Don't Handle pending delete here
91 if (registration_list.flag[i] == RegistrationStatus::None ||
92 registration_list.flag[i] == RegistrationStatus::PendingDelete) {
91 data_index = i; 93 data_index = i;
92 break; 94 break;
93 } 95 }
@@ -104,30 +106,22 @@ Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input
104} 106}
105 107
106void AppletResource::UnregisterAppletResourceUserId(u64 aruid) { 108void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
107 u64 index = GetIndexFromAruid(aruid); 109 const u64 index = GetIndexFromAruid(aruid);
108 110
109 if (index < AruidIndexMax) { 111 if (index >= AruidIndexMax) {
110 if (data[index].flag.is_assigned) { 112 return;
111 data[index].shared_memory_format = nullptr;
112 data[index].flag.is_assigned.Assign(false);
113 }
114 } 113 }
115 114
116 index = GetIndexFromAruid(aruid); 115 FreeAppletResourceId(aruid);
117 if (index < AruidIndexMax) { 116 DestroySevenSixAxisTransferMemory();
118 DestroySevenSixAxisTransferMemory(); 117 data[index].flag.raw = 0;
119 data[index].flag.raw = 0; 118 data[index].aruid = 0;
120 data[index].aruid = 0;
121 119
122 index = GetIndexFromAruid(aruid); 120 registration_list.flag[index] = RegistrationStatus::PendingDelete;
123 if (index < AruidIndexMax) {
124 registration_list.flag[index] = RegistrationStatus::PendingDelete;
125 }
126 }
127} 121}
128 122
129void AppletResource::FreeAppletResourceId(u64 aruid) { 123void AppletResource::FreeAppletResourceId(u64 aruid) {
130 u64 index = GetIndexFromAruid(aruid); 124 const u64 index = GetIndexFromAruid(aruid);
131 if (index >= AruidIndexMax) { 125 if (index >= AruidIndexMax) {
132 return; 126 return;
133 } 127 }
@@ -144,7 +138,7 @@ u64 AppletResource::GetActiveAruid() {
144} 138}
145 139
146Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { 140Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
147 u64 index = GetIndexFromAruid(aruid); 141 const u64 index = GetIndexFromAruid(aruid);
148 if (index >= AruidIndexMax) { 142 if (index >= AruidIndexMax) {
149 return ResultAruidNotRegistered; 143 return ResultAruidNotRegistered;
150 } 144 }
@@ -155,7 +149,7 @@ Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle,
155 149
156Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, 150Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format,
157 u64 aruid) { 151 u64 aruid) {
158 u64 index = GetIndexFromAruid(aruid); 152 const u64 index = GetIndexFromAruid(aruid);
159 if (index >= AruidIndexMax) { 153 if (index >= AruidIndexMax) {
160 return ResultAruidNotRegistered; 154 return ResultAruidNotRegistered;
161 } 155 }
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index e6c035628..97f31d26e 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -870,6 +870,11 @@ void NPad::InitializeVibrationDevice(
870 const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid(); 870 const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
871 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id); 871 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
872 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 872 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
873
874 if (aruid == 0) {
875 return;
876 }
877
873 InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index); 878 InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index);
874} 879}
875 880
diff --git a/src/hid_core/resources/npad/npad_resource.cpp b/src/hid_core/resources/npad/npad_resource.cpp
index b0255a05c..ea9fc14ed 100644
--- a/src/hid_core/resources/npad/npad_resource.cpp
+++ b/src/hid_core/resources/npad/npad_resource.cpp
@@ -46,7 +46,9 @@ Result NPadResource::RegisterAppletResourceUserId(u64 aruid) {
46 data_index = i; 46 data_index = i;
47 break; 47 break;
48 } 48 }
49 if (registration_list.flag[i] == RegistrationStatus::None) { 49 // TODO: Don't Handle pending delete here
50 if (registration_list.flag[i] == RegistrationStatus::None ||
51 registration_list.flag[i] == RegistrationStatus::PendingDelete) {
50 data_index = i; 52 data_index = i;
51 break; 53 break;
52 } 54 }