diff options
-rw-r--r-- | src/android/app/src/main/AndroidManifest.xml | 3 | ||||
-rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 9 | ||||
-rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt | 219 | ||||
-rw-r--r-- | src/android/app/src/main/jni/native.cpp | 16 | ||||
-rw-r--r-- | src/android/app/src/main/res/values/strings.xml | 10 | ||||
-rw-r--r-- | src/android/app/src/main/res/xml/game_mode_config.xml | 7 | ||||
-rw-r--r-- | src/core/file_sys/savedata_factory.cpp | 9 | ||||
-rw-r--r-- | src/core/file_sys/savedata_factory.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid_system_server.cpp | 60 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid_system_server.h | 6 | ||||
-rw-r--r-- | src/hid_core/resource_manager.cpp | 1 | ||||
-rw-r--r-- | src/hid_core/resources/applet_resource.cpp | 34 | ||||
-rw-r--r-- | src/hid_core/resources/npad/npad.cpp | 5 | ||||
-rw-r--r-- | src/hid_core/resources/npad/npad_resource.cpp | 4 |
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 android:dataExtractionRules="@xml/data_extraction_rules_api_31" android:enableOnBackInvokedCallback="true"> + <meta-data android:name="android.game_mode_config" + android:resource="@xml/game_mode_config" /> + <activity android:name="org.yuzu.yuzu_emu.ui.main.MainActivity" 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 { external fun getSavePath(programId: String): String /** + * Gets the root save directory for the default profile as either + * /user/save/account/<user id raw string> or /user/save/000...000/<user id> + * + * @param future If true, returns the /user/save/account/... directory + * @return Save data path that may not exist yet + */ + external fun getDefaultProfileSaveDataRoot(future: Boolean): String + + /** * Adds a file to the manual filesystem provider in our EmulationSession instance * @param path Path to the file we're adding. Can be a string representation of a [Uri] or * 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 import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.adapters.InstallableAdapter import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.Installable +import org.yuzu.yuzu_emu.model.TaskState import org.yuzu.yuzu_emu.ui.main.MainActivity +import org.yuzu.yuzu_emu.utils.DirectoryInitialization +import org.yuzu.yuzu_emu.utils.FileUtil +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import java.io.File +import java.math.BigInteger +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter class InstallableFragment : Fragment() { private var _binding: FragmentInstallablesBinding? = null @@ -56,6 +75,17 @@ class InstallableFragment : Fragment() { binding.root.findNavController().popBackStack() } + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + homeViewModel.openImportSaves.collect { + if (it) { + importSaves.launch(arrayOf("application/zip")) + homeViewModel.setOpenImportSaves(false) + } + } + } + } + val installables = listOf( Installable( R.string.user_data, @@ -64,6 +94,43 @@ class InstallableFragment : Fragment() { export = { mainActivity.exportUserData.launch("export.zip") } ), Installable( + R.string.manage_save_data, + R.string.manage_save_data_description, + install = { + MessageDialogFragment.newInstance( + requireActivity(), + titleId = R.string.import_save_warning, + descriptionId = R.string.import_save_warning_description, + positiveAction = { homeViewModel.setOpenImportSaves(true) } + ).show(parentFragmentManager, MessageDialogFragment.TAG) + }, + export = { + val oldSaveDataFolder = File( + "${DirectoryInitialization.userDirectory}/nand" + + NativeLibrary.getDefaultProfileSaveDataRoot(false) + ) + val futureSaveDataFolder = File( + "${DirectoryInitialization.userDirectory}/nand" + + NativeLibrary.getDefaultProfileSaveDataRoot(true) + ) + if (!oldSaveDataFolder.exists() && !futureSaveDataFolder.exists()) { + Toast.makeText( + YuzuApplication.appContext, + R.string.no_save_data_found, + Toast.LENGTH_SHORT + ).show() + return@Installable + } else { + exportSaves.launch( + "${getString(R.string.save_data)} " + + LocalDateTime.now().format( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + ) + ) + } + } + ), + Installable( R.string.install_game_content, R.string.install_game_content_description, install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) } @@ -121,4 +188,156 @@ class InstallableFragment : Fragment() { windowInsets } + + private val importSaves = + registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result == null) { + return@registerForActivityResult + } + + val inputZip = requireContext().contentResolver.openInputStream(result) + val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/") + cacheSaveDir.mkdir() + + if (inputZip == null) { + Toast.makeText( + YuzuApplication.appContext, + getString(R.string.fatal_error), + Toast.LENGTH_LONG + ).show() + return@registerForActivityResult + } + + IndeterminateProgressDialogFragment.newInstance( + requireActivity(), + R.string.save_files_importing, + false + ) { + try { + FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir) + val files = cacheSaveDir.listFiles() + var successfulImports = 0 + var failedImports = 0 + if (files != null) { + for (file in files) { + if (file.isDirectory) { + val baseSaveDir = + NativeLibrary.getSavePath(BigInteger(file.name, 16).toString()) + if (baseSaveDir.isEmpty()) { + failedImports++ + continue + } + + val internalSaveFolder = File( + "${DirectoryInitialization.userDirectory}/nand$baseSaveDir" + ) + internalSaveFolder.deleteRecursively() + internalSaveFolder.mkdir() + file.copyRecursively(target = internalSaveFolder, overwrite = true) + successfulImports++ + } + } + } + + withContext(Dispatchers.Main) { + if (successfulImports == 0) { + MessageDialogFragment.newInstance( + requireActivity(), + titleId = R.string.save_file_invalid_zip_structure, + descriptionId = R.string.save_file_invalid_zip_structure_description + ).show(parentFragmentManager, MessageDialogFragment.TAG) + return@withContext + } + val successString = if (failedImports > 0) { + """ + ${ + requireContext().resources.getQuantityString( + R.plurals.saves_import_success, + successfulImports, + successfulImports + ) + } + ${ + requireContext().resources.getQuantityString( + R.plurals.saves_import_failed, + failedImports, + failedImports + ) + } + """ + } else { + requireContext().resources.getQuantityString( + R.plurals.saves_import_success, + successfulImports, + successfulImports + ) + } + MessageDialogFragment.newInstance( + requireActivity(), + titleId = R.string.import_complete, + descriptionString = successString + ).show(parentFragmentManager, MessageDialogFragment.TAG) + } + + cacheSaveDir.deleteRecursively() + } catch (e: Exception) { + Toast.makeText( + YuzuApplication.appContext, + getString(R.string.fatal_error), + Toast.LENGTH_LONG + ).show() + } + }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG) + } + + private val exportSaves = registerForActivityResult( + ActivityResultContracts.CreateDocument("application/zip") + ) { result -> + if (result == null) { + return@registerForActivityResult + } + + IndeterminateProgressDialogFragment.newInstance( + requireActivity(), + R.string.save_files_exporting, + false + ) { + val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/") + cacheSaveDir.mkdir() + + val oldSaveDataFolder = File( + "${DirectoryInitialization.userDirectory}/nand" + + NativeLibrary.getDefaultProfileSaveDataRoot(false) + ) + if (oldSaveDataFolder.exists()) { + oldSaveDataFolder.copyRecursively(cacheSaveDir) + } + + val futureSaveDataFolder = File( + "${DirectoryInitialization.userDirectory}/nand" + + NativeLibrary.getDefaultProfileSaveDataRoot(true) + ) + if (futureSaveDataFolder.exists()) { + futureSaveDataFolder.copyRecursively(cacheSaveDir) + } + + val saveFilesTotal = cacheSaveDir.listFiles()?.size ?: 0 + if (saveFilesTotal == 0) { + cacheSaveDir.deleteRecursively() + return@newInstance getString(R.string.no_save_data_found) + } + + val zipResult = FileUtil.zipFromInternalStorage( + cacheSaveDir, + cacheSaveDir.path, + BufferedOutputStream(requireContext().contentResolver.openOutputStream(result)) + ) + cacheSaveDir.deleteRecursively() + + return@newInstance when (zipResult) { + TaskState.Completed -> getString(R.string.export_success) + TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) + } + }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG) + } } 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, jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, jstring jprogramId) { auto program_id = EmulationSession::GetProgramId(env, jprogramId); + if (program_id == 0) { + return ToJString(env, ""); + } auto& system = EmulationSession::GetInstance().System(); @@ -880,6 +883,19 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j return ToJString(env, user_save_data_path); } +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env, + jobject jobj, + jboolean jfuture) { + Service::Account::ProfileManager manager; + // TODO: Pass in a selected user once we get the relevant UI working + const auto user_id = manager.GetUser(static_cast<std::size_t>(0)); + ASSERT(user_id); + + const auto user_save_data_root = + FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture); + return ToJString(env, user_save_data_root); +} + void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj, jstring jpath) { 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 @@ <string name="add_game_folder">Add game folder</string> <string name="folder_already_added">This folder was already added!</string> <string name="game_folder_properties">Game folder properties</string> + <plurals name="saves_import_failed"> + <item quantity="one">Failed to import %d save</item> + <item quantity="other">Failed to import %d saves</item> + </plurals> + <plurals name="saves_import_success"> + <item quantity="one">Successfully imported %d save</item> + <item quantity="other">Successfully imported %d saves</item> + </plurals> + <string name="no_save_data_found">No save data found</string> <!-- Applet launcher strings --> <string name="applets">Applet launcher</string> @@ -276,6 +285,7 @@ <string name="global">Global</string> <string name="custom">Custom</string> <string name="notice">Notice</string> + <string name="import_complete">Import complete</string> <!-- GPU driver installation --> <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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<game-mode-config + xmlns:android="http://schemas.android.com/apk/res/android" + android:supportsBatteryGameMode="true" + android:supportsPerformanceGameMode="true" + android:allowGameDownscaling="false" + 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, } } +std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future) { + if (future) { + Common::UUID uuid; + std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); + return fmt::format("/user/save/account/{}", uuid.RawString()); + } + return fmt::format("/user/save/{:016X}/{:016X}{:016X}", 0, user_id[1], user_id[0]); +} + SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const { 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: static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id); + static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; 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 {522, nullptr, "SetJoyConRailEnabled"}, {523, nullptr, "IsJoyConRailEnabled"}, {524, nullptr, "IsHandheldHidsEnabled"}, - {525, nullptr, "IsJoyConAttachedOnAllRail"}, + {525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"}, {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, {541, nullptr, "GetPlayReportControllerUsages"}, {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, @@ -131,7 +131,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1001, nullptr, "GetFirmwareVersion"}, {1002, nullptr, "GetAvailableFirmwareVersion"}, {1003, nullptr, "IsFirmwareUpdateAvailable"}, - {1004, nullptr, "CheckFirmwareUpdateRequired"}, + {1004, &IHidSystemServer::CheckFirmwareUpdateRequired, "CheckFirmwareUpdateRequired"}, {1005, nullptr, "StartFirmwareUpdate"}, {1006, nullptr, "AbortFirmwareUpdate"}, {1007, nullptr, "GetFirmwareUpdateState"}, @@ -144,9 +144,9 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, {1100, nullptr, "GetHidbusSystemServiceObject"}, - {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, - {1130, nullptr, "InitializeUsbFirmwareUpdate"}, - {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, + {1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"}, + {1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"}, + {1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"}, {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, {1133, nullptr, "StartUsbFirmwareUpdate"}, {1134, nullptr, "GetUsbFirmwareUpdateState"}, @@ -196,7 +196,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1268, nullptr, "DeleteButtonConfigStorageFull"}, {1269, nullptr, "DeleteButtonConfigStorageLeft"}, {1270, nullptr, "DeleteButtonConfigStorageRight"}, - {1271, nullptr, "IsUsingCustomButtonConfig"}, + {1271, &IHidSystemServer::IsUsingCustomButtonConfig, "IsUsingCustomButtonConfig"}, {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, {1274, nullptr, "SetDefaultButtonConfig"}, @@ -555,6 +555,16 @@ void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) { + const bool is_attached = true; + + LOG_DEBUG(Service_HID, "(STUBBED) called, is_attached={}", is_attached); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); +} + void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) { LOG_INFO(Service_AM, "(STUBBED) called"); @@ -641,6 +651,34 @@ void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IHidSystemServer::CheckFirmwareUpdateRequired(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::InitializeUsbFirmwareUpdate(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called"); @@ -665,6 +703,16 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx rb.PushRaw(touchscreen_config); } +void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) { + const bool is_enabled = false; + + LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_enabled); +} + std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() { resource_manager->Initialize(); 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: void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx); void EnableAppletToGetPadInput(HLERequestContext& ctx); void EnableAppletToGetTouchScreen(HLERequestContext& ctx); + void IsJoyConAttachedOnAllRail(HLERequestContext& ctx); void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); void GetRegisteredDevices(HLERequestContext& ctx); @@ -53,8 +54,13 @@ private: void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); void InitializeFirmwareUpdate(HLERequestContext& ctx); + void CheckFirmwareUpdateRequired(HLERequestContext& ctx); + void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx); + void InitializeUsbFirmwareUpdate(HLERequestContext& ctx); + void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); + void IsUsingCustomButtonConfig(HLERequestContext& ctx); std::shared_ptr<ResourceManager> GetResourceManager(); 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) void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) { std::scoped_lock lock{shared_mutex}; applet_resource->UnregisterAppletResourceUserId(aruid); + npad->UnregisterAppletResourceUserId(aruid); } Result 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 data_index = i; break; } - if (registration_list.flag[i] == RegistrationStatus::None) { + // TODO: Don't Handle pending delete here + if (registration_list.flag[i] == RegistrationStatus::None || + registration_list.flag[i] == RegistrationStatus::PendingDelete) { data_index = i; break; } @@ -104,30 +106,22 @@ Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input } void AppletResource::UnregisterAppletResourceUserId(u64 aruid) { - u64 index = GetIndexFromAruid(aruid); + const u64 index = GetIndexFromAruid(aruid); - if (index < AruidIndexMax) { - if (data[index].flag.is_assigned) { - data[index].shared_memory_format = nullptr; - data[index].flag.is_assigned.Assign(false); - } + if (index >= AruidIndexMax) { + return; } - index = GetIndexFromAruid(aruid); - if (index < AruidIndexMax) { - DestroySevenSixAxisTransferMemory(); - data[index].flag.raw = 0; - data[index].aruid = 0; + FreeAppletResourceId(aruid); + DestroySevenSixAxisTransferMemory(); + data[index].flag.raw = 0; + data[index].aruid = 0; - index = GetIndexFromAruid(aruid); - if (index < AruidIndexMax) { - registration_list.flag[index] = RegistrationStatus::PendingDelete; - } - } + registration_list.flag[index] = RegistrationStatus::PendingDelete; } void AppletResource::FreeAppletResourceId(u64 aruid) { - u64 index = GetIndexFromAruid(aruid); + const u64 index = GetIndexFromAruid(aruid); if (index >= AruidIndexMax) { return; } @@ -144,7 +138,7 @@ u64 AppletResource::GetActiveAruid() { } Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { - u64 index = GetIndexFromAruid(aruid); + const u64 index = GetIndexFromAruid(aruid); if (index >= AruidIndexMax) { return ResultAruidNotRegistered; } @@ -155,7 +149,7 @@ Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid) { - u64 index = GetIndexFromAruid(aruid); + const u64 index = GetIndexFromAruid(aruid); if (index >= AruidIndexMax) { return ResultAruidNotRegistered; } 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( const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid(); const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id); const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); + + if (aruid == 0) { + return; + } + InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index); } 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) { data_index = i; break; } - if (registration_list.flag[i] == RegistrationStatus::None) { + // TODO: Don't Handle pending delete here + if (registration_list.flag[i] == RegistrationStatus::None || + registration_list.flag[i] == RegistrationStatus::PendingDelete) { data_index = i; break; } |